feat(client): make possible to switch account instantly in post form

This commit is contained in:
syuilo 2022-01-21 20:17:31 +09:00
parent b388b78892
commit 331afcb96a
8 changed files with 119 additions and 40 deletions

View file

@ -21,6 +21,7 @@
### Improvements
- カスタム絵文字一括編集機能
- カスタム絵文字一括インポート
- 投稿フォームで一時的に投稿するアカウントを切り替えられるように
### Bugfixes

View file

@ -129,7 +129,12 @@ export async function login(token: Account['token'], redirect?: string) {
unisonReload();
}
export async function openAccountMenu(ev: MouseEvent) {
export async function openAccountMenu(opts: {
includeCurrentAccount?: boolean;
withExtraOperation: boolean;
active?: misskey.entities.UserDetailed['id'];
onChoose?: (account: misskey.entities.UserDetailed) => void;
}, ev: MouseEvent) {
function showSigninDialog() {
popup(import('@/components/signin-dialog.vue'), {}, {
done: res => {
@ -148,7 +153,7 @@ export async function openAccountMenu(ev: MouseEvent) {
}, 'closed');
}
async function switchAccount(account: any) {
async function switchAccount(account: misskey.entities.UserDetailed) {
const storedAccounts = await getAccounts();
const token = storedAccounts.find(x => x.id === account.id).token;
switchAccountWithToken(token);
@ -161,41 +166,58 @@ export async function openAccountMenu(ev: MouseEvent) {
const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id));
const accountsPromise = api('users/show', { userIds: storedAccounts.map(x => x.id) });
function createItem(account: misskey.entities.UserDetailed) {
return {
type: 'user',
user: account,
active: opts.active != null ? opts.active === account.id : false,
action: () => {
if (opts.onChoose) {
opts.onChoose(account);
} else {
switchAccount(account);
}
},
};
}
const accountItemPromises = storedAccounts.map(a => new Promise(res => {
accountsPromise.then(accounts => {
const account = accounts.find(x => x.id === a.id);
if (account == null) return res(null);
res({
type: 'user',
user: account,
action: () => { switchAccount(account); }
});
res(createItem(account));
});
}));
popupMenu([...[{
type: 'link',
text: i18n.locale.profile,
to: `/@${ $i.username }`,
avatar: $i,
}, null, ...accountItemPromises, {
icon: 'fas fa-plus',
text: i18n.locale.addAccount,
action: () => {
popupMenu([{
text: i18n.locale.existingAccount,
action: () => { showSigninDialog(); },
}, {
text: i18n.locale.createAccount,
action: () => { createAccount(); },
}], ev.currentTarget || ev.target);
},
}, {
type: 'link',
icon: 'fas fa-users',
text: i18n.locale.manageAccounts,
to: `/settings/accounts`,
}]], ev.currentTarget || ev.target, {
align: 'left'
});
if (opts.withExtraOperation) {
popupMenu([...[{
type: 'link',
text: i18n.locale.profile,
to: `/@${ $i.username }`,
avatar: $i,
}, null, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, {
icon: 'fas fa-plus',
text: i18n.locale.addAccount,
action: () => {
popupMenu([{
text: i18n.locale.existingAccount,
action: () => { showSigninDialog(); },
}, {
text: i18n.locale.createAccount,
action: () => { createAccount(); },
}], ev.currentTarget || ev.target);
},
}, {
type: 'link',
icon: 'fas fa-users',
text: i18n.locale.manageAccounts,
to: `/settings/accounts`,
}]], ev.currentTarget || ev.target, {
align: 'left'
});
} else {
popupMenu([...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises], ev.currentTarget || ev.target, {
align: 'left'
});
}
}

View file

@ -8,6 +8,9 @@
>
<header>
<button v-if="!fixed" class="cancel _button" @click="cancel"><i class="fas fa-times"></i></button>
<button v-click-anime class="account _button" @click="openAccountMenu">
<MkAvatar :user="postAccount ?? $i" class="avatar"/>
</button>
<div>
<span class="text-count" :class="{ over: textLength > maxTextLength }">{{ maxTextLength - textLength }}</span>
<span v-if="localOnly" class="local-only"><i class="fas fa-biohazard"></i></span>
@ -83,7 +86,7 @@ import { throttle } from 'throttle-debounce';
import MkInfo from '@/components/ui/info.vue';
import { i18n } from '@/i18n';
import { instance } from '@/instance';
import { $i } from '@/account';
import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account';
const modal = inject('modal');
@ -553,8 +556,15 @@ async function post() {
}
}
let token = undefined;
if (postAccount) {
const storedAccounts = await getAccounts();
token = storedAccounts.find(x => x.id === postAccount.id)?.token;
}
posting = true;
os.api('notes/create', data).then(() => {
os.api('notes/create', data, token).then(() => {
clear();
nextTick(() => {
deleteDraft();
@ -585,7 +595,7 @@ function insertMention() {
});
}
async function insertEmoji(ev) {
async function insertEmoji(ev: MouseEvent) {
os.openEmojiPicker(ev.currentTarget || ev.target, {}, textareaEl);
}
@ -602,6 +612,23 @@ function showActions(ev) {
})), ev.currentTarget || ev.target);
}
let postAccount = $ref<misskey.entities.UserDetailed | null>(null);
function openAccountMenu(ev: MouseEvent) {
openAccountMenu_({
withExtraOperation: false,
includeCurrentAccount: true,
active: postAccount != null ? postAccount.id : $i.id,
onChoose: (account) => {
if (account.id === $i.id) {
postAccount = null;
} else {
postAccount = account;
}
},
}, ev);
}
onMounted(() => {
if (props.autofocus) {
focus();
@ -678,6 +705,19 @@ onMounted(() => {
line-height: 66px;
}
> .account {
height: 100%;
aspect-ratio: 1/1;
display: inline-flex;
vertical-align: bottom;
> .avatar {
width: 28px;
height: 28px;
margin: auto;
}
}
> div {
position: absolute;
top: 0;

View file

@ -24,7 +24,7 @@
<span>{{ item.text }}</span>
<span v-if="item.indicate" class="indicator"><i class="fas fa-circle"></i></span>
</a>
<button v-else-if="item.type === 'user'" :tabindex="i" class="_button item" @click="clicked(item.action, $event)">
<button v-else-if="item.type === 'user'" :tabindex="i" class="_button item" :class="{ active: item.active }" :disabled="item.active" @click="clicked(item.action, $event)">
<MkAvatar :user="item.user" class="avatar"/><MkUserName :user="item.user"/>
<span v-if="item.indicate" class="indicator"><i class="fas fa-circle"></i></span>
</button>

View file

@ -61,7 +61,11 @@ export default defineComponent({
otherMenuItemIndicated,
post: os.post,
search,
openAccountMenu,
openAccountMenu:(ev) => {
openAccountMenu({
withExtraOperation: true,
}, ev);
},
more: () => {
os.popup(import('@/components/launch-pad.vue'), {}, {
}, 'closed');

View file

@ -76,7 +76,11 @@ export default defineComponent({
iconOnly,
post: os.post,
search,
openAccountMenu,
openAccountMenu:(ev) => {
openAccountMenu({
withExtraOperation: true,
}, ev);
},
more: () => {
os.popup(import('@/components/launch-pad.vue'), {}, {
}, 'closed');

View file

@ -105,7 +105,11 @@ export default defineComponent({
}, 'closed');
},
openAccountMenu,
openAccountMenu:(ev) => {
openAccountMenu({
withExtraOperation: true,
}, ev);
},
}
});
</script>

View file

@ -125,7 +125,11 @@ export default defineComponent({
}, 'closed');
},
openAccountMenu,
openAccountMenu:(ev) => {
openAccountMenu({
withExtraOperation: true,
}, ev);
},
}
});
</script>