Split Accounts away from Settings

Very MVP, even #settings-container is the same for now
This commit is contained in:
Lim Chee Aun 2023-03-08 00:32:33 +08:00
parent 6e487ad848
commit 73b8294811
6 changed files with 209 additions and 168 deletions

View file

@ -26,6 +26,7 @@ import Shortcuts from './components/shortcuts';
import ShortcutsSettings from './components/shortcuts-settings';
import NotFound from './pages/404';
import AccountStatuses from './pages/account-statuses';
import Accounts from './pages/accounts';
import Bookmarks from './pages/bookmarks';
import Favourites from './pages/favourites';
import FollowedHashtags from './pages/followed-hashtags';
@ -163,6 +164,7 @@ function App() {
const showModal =
snapStates.showCompose ||
snapStates.showSettings ||
snapStates.showAccounts ||
snapStates.showAccount ||
snapStates.showDrafts ||
snapStates.showMediaModal ||
@ -374,6 +376,21 @@ function App() {
/>
</Modal>
)}
{!!snapStates.showAccounts && (
<Modal
onClick={(e) => {
if (e.target === e.currentTarget) {
states.showAccounts = false;
}
}}
>
<Accounts
onClose={() => {
states.showAccounts = false;
}}
/>
</Modal>
)}
{!!snapStates.showAccount && (
<Modal
class="light"

View file

@ -102,6 +102,13 @@ function NavMenu(props) {
{authenticated && (
<>
<MenuDivider />
<MenuItem
onClick={() => {
states.showAccounts = true;
}}
>
<Icon icon="group" size="l" /> <span>Accounts&hellip;</span>
</MenuItem>
<MenuItem
onClick={() => {
states.showShortcutsSettings = true;

152
src/pages/accounts.jsx Normal file
View file

@ -0,0 +1,152 @@
import './settings.css';
import { Menu, MenuItem } from '@szhsin/react-menu';
import { useReducer, useState } from 'preact/hooks';
import Avatar from '../components/avatar';
import Icon from '../components/icon';
import Link from '../components/link';
import NameText from '../components/name-text';
import { api } from '../utils/api';
import states from '../utils/states';
import store from '../utils/store';
function Accounts({ onClose }) {
const { masto } = api();
// Accounts
const accounts = store.local.getJSON('accounts');
const currentAccount = store.session.get('currentAccount');
const moreThanOneAccount = accounts.length > 1;
const [currentDefault, setCurrentDefault] = useState(0);
const [_, reload] = useReducer((x) => x + 1, 0);
return (
<div id="settings-container" class="sheet" tabIndex="-1">
<header class="header-grid">
<h2>Accounts</h2>
<div class="header-side">
<Link to="/login" class="button plain" onClick={onClose}>
<Icon icon="plus" /> <span>Account</span>
</Link>
</div>
</header>
<main>
<section>
<ul class="accounts-list">
{accounts.map((account, i) => {
const isCurrent = account.info.id === currentAccount;
const isDefault = i === (currentDefault || 0);
return (
<li key={i + account.id}>
<div>
{moreThanOneAccount && (
<span class={`current ${isCurrent ? 'is-current' : ''}`}>
<Icon icon="check-circle" alt="Current" />
</span>
)}
<Avatar
url={account.info.avatarStatic}
size="xxl"
onDblClick={async () => {
if (isCurrent) {
try {
const info = await masto.v1.accounts.fetch(
account.info.id,
);
console.log('fetched account info', info);
account.info = info;
store.local.setJSON('accounts', accounts);
reload();
} catch (e) {}
}
}}
/>
<NameText
account={account.info}
showAcct
onClick={() => {
states.showAccount = `${account.info.username}@${account.instanceURL}`;
}}
/>
</div>
<div class="actions">
{isDefault && moreThanOneAccount && (
<>
<span class="tag">Default</span>{' '}
</>
)}
{!isCurrent && (
<button
type="button"
class="light"
onClick={() => {
store.session.set('currentAccount', account.info.id);
location.reload();
}}
>
<Icon icon="transfer" /> Switch
</button>
)}
<Menu
align="end"
menuButton={
<button
type="button"
title="More"
class="plain more-button"
>
<Icon icon="more" size="l" alt="More" />
</button>
}
>
{moreThanOneAccount && (
<MenuItem
disabled={isDefault}
onClick={() => {
// Move account to the top of the list
accounts.splice(i, 1);
accounts.unshift(account);
store.local.setJSON('accounts', accounts);
setCurrentDefault(i);
}}
>
<Icon icon="check-circle" />
<span>Set as default</span>
</MenuItem>
)}
<MenuItem
disabled={!isCurrent}
onClick={() => {
const yes = confirm('Log out?');
if (!yes) return;
accounts.splice(i, 1);
store.local.setJSON('accounts', accounts);
// location.reload();
location.href = '/';
}}
>
<Icon icon="exit" />
<span>Log out</span>
</MenuItem>
</Menu>
</div>
</li>
);
})}
</ul>
{moreThanOneAccount && (
<p>
<small>
Note: <i>Default</i> account will always be used for first load.
Switched accounts will persist during the session.
</small>
</p>
)}
</section>
</main>
</div>
);
}
export default Accounts;

View file

@ -2,19 +2,16 @@
background-color: var(--bg-faded-color);
}
#settings-container h2 {
#settings-container main h3 {
font-size: 85%;
text-transform: uppercase;
color: var(--text-insignificant-color);
font-weight: normal;
}
#settings-container h2 ~ h2 {
margin-top: 2em;
}
#settings-container section {
background-color: var(--bg-color);
margin: 0;
margin: 8px 0 0;
padding: 8px 16px;
border-top: var(--hairline-width) solid var(--outline-color);
border-bottom: var(--hairline-width) solid var(--outline-color);
@ -30,7 +27,7 @@
list-style: none;
}
#settings-container section > ul > li {
padding: 8px 0 16px;
padding: 8px 0;
display: flex;
justify-content: space-between;
align-items: center;
@ -71,6 +68,10 @@
margin-right: 8px;
}
#settings-container section select {
padding: 4px;
}
#settings-container .radio-group {
display: inline-flex;
align-items: center;

View file

@ -1,41 +1,20 @@
import './settings.css';
import { Menu, MenuItem } from '@szhsin/react-menu';
import { useReducer, useRef, useState } from 'preact/hooks';
import { useRef } from 'preact/hooks';
import { useSnapshot } from 'valtio';
import logo from '../assets/logo.svg';
import Avatar from '../components/avatar';
import Icon from '../components/icon';
import Link from '../components/link';
import NameText from '../components/name-text';
import RelativeTime from '../components/relative-time';
import targetLanguages from '../data/lingva-target-languages';
import { api } from '../utils/api';
import getTranslateTargetLanguage from '../utils/get-translate-target-language';
import localeCode2Text from '../utils/localeCode2Text';
import states from '../utils/states';
import store from '../utils/store';
/*
Settings component that shows these settings:
- Accounts list for switching
- Dark/light/auto theme switch (done with adding/removing 'is-light' or 'is-dark' class on the body)
*/
function Settings({ onClose }) {
const { masto } = api();
const snapStates = useSnapshot(states);
// Accounts
const accounts = store.local.getJSON('accounts');
const currentAccount = store.session.get('currentAccount');
const currentTheme = store.local.get('theme') || 'auto';
const themeFormRef = useRef();
const moreThanOneAccount = accounts.length > 1;
const [currentDefault, setCurrentDefault] = useState(0);
const [_, reload] = useReducer((x) => x + 1, 0);
const targetLanguage =
snapStates.settings.contentTranslationTargetLanguage || null;
const systemTargetLanguage = getTranslateTargetLanguage();
@ -43,129 +22,10 @@ function Settings({ onClose }) {
return (
<div id="settings-container" class="sheet" tabIndex="-1">
<main>
{/* <button type="button" class="close-button plain large" onClick={onClose}>
<Icon icon="x" alt="Close" />
</button> */}
<h2>Accounts</h2>
<section>
<ul class="accounts-list">
{accounts.map((account, i) => {
const isCurrent = account.info.id === currentAccount;
const isDefault = i === (currentDefault || 0);
return (
<li key={i + account.id}>
<div>
{moreThanOneAccount && (
<span class={`current ${isCurrent ? 'is-current' : ''}`}>
<Icon icon="check-circle" alt="Current" />
</span>
)}
<Avatar
url={account.info.avatarStatic}
size="xxl"
onDblClick={async () => {
if (isCurrent) {
try {
const info = await masto.v1.accounts.fetch(
account.info.id,
);
console.log('fetched account info', info);
account.info = info;
store.local.setJSON('accounts', accounts);
reload();
} catch (e) {}
}
}}
/>
<NameText
account={account.info}
showAcct
onClick={() => {
states.showAccount = `${account.info.username}@${account.instanceURL}`;
}}
/>
</div>
<div class="actions">
{isDefault && moreThanOneAccount && (
<>
<span class="tag">Default</span>{' '}
</>
)}
{!isCurrent && (
<button
type="button"
class="light"
onClick={() => {
store.session.set('currentAccount', account.info.id);
location.reload();
}}
>
<Icon icon="transfer" /> Switch
</button>
)}
<Menu
align="end"
menuButton={
<button
type="button"
title="More"
class="plain more-button"
>
<Icon icon="more" size="l" alt="More" />
</button>
}
>
{moreThanOneAccount && (
<MenuItem
disabled={isDefault}
onClick={() => {
// Move account to the top of the list
accounts.splice(i, 1);
accounts.unshift(account);
store.local.setJSON('accounts', accounts);
setCurrentDefault(i);
}}
>
<Icon icon="check-circle" />
<span>Set as default</span>
</MenuItem>
)}
<MenuItem
disabled={!isCurrent}
onClick={() => {
const yes = confirm('Log out?');
if (!yes) return;
accounts.splice(i, 1);
store.local.setJSON('accounts', accounts);
// location.reload();
location.href = '/';
}}
>
<Icon icon="exit" />
<span>Log out</span>
</MenuItem>
</Menu>
</div>
</li>
);
})}
</ul>
{moreThanOneAccount && (
<p>
<small>
Note: <i>Default</i> account will always be used for first load.
Switched accounts will persist during the session.
</small>
</p>
)}
<p style={{ textAlign: 'end' }}>
<Link to="/login" class="button" onClick={onClose}>
Add new account
</Link>
</p>
</section>
<header>
<h2>Settings</h2>
</header>
<main>
<section>
<ul>
<li>
@ -236,6 +96,11 @@ function Settings({ onClose }) {
</form>
</div>
</li>
</ul>
</section>
<h3>Experiments</h3>
<section>
<ul>
<li>
<label>
<input
@ -245,7 +110,7 @@ function Settings({ onClose }) {
states.settings.boostsCarousel = e.target.checked;
}}
/>{' '}
Boosts carousel (experimental)
Boosts carousel
</label>
</li>
<li>
@ -257,7 +122,7 @@ function Settings({ onClose }) {
states.settings.contentTranslation = e.target.checked;
}}
/>{' '}
Post translation (experimental)
Post translation
</label>
{snapStates.settings.contentTranslation && (
<div class="sub-section">
@ -295,24 +160,21 @@ function Settings({ onClose }) {
</div>
)}
</li>
<li>
<button
type="button"
class="light"
onClick={() => {
states.showDrafts = true;
states.showSettings = false;
}}
>
Unsent drafts
</button>
</li>
</ul>
</section>
<h2>Hidden features</h2>
<section>
<div>
<button
type="button"
class="light"
onClick={() => {
states.showDrafts = true;
states.showSettings = false;
}}
>
Unsent drafts
</button>
</div>
</section>
<h2>About</h2>
<h3>About</h3>
<section>
<p>
<img

View file

@ -31,6 +31,7 @@ const states = proxy({
showCompose: false,
showSettings: false,
showAccount: false,
showAccounts: false,
showDrafts: false,
showMediaModal: false,
showShortcutsSettings: false,
@ -82,6 +83,7 @@ export function hideAllModals() {
states.showCompose = false;
states.showSettings = false;
states.showAccount = false;
states.showAccounts = false;
states.showDrafts = false;
states.showMediaModal = false;
states.showShortcutsSettings = false;