66import { defineAsyncComponent , ref } from 'vue' ;
77import * as Misskey from 'misskey-js' ;
88import { apiUrl , host } from '@@/js/config.js' ;
9- import { generateClientTransactionId } from '@/utility/misskey-api.js' ;
10- import type { MenuItem } from '@/types/menu.js' ;
9+ import { generateClientTransactionId , misskeyApi } from '@/utility/misskey-api.js' ;
10+ import type { MenuButton , MenuItem , MenuUser } from '@/types/menu.js' ;
1111import { showSuspendedDialog } from '@/utility/show-suspended-dialog.js' ;
1212import { i18n } from '@/i18n.js' ;
1313import { miLocalStorage } from '@/local-storage.js' ;
14- import { waiting , popup , popupMenu , success , alert } from '@/os.js' ;
14+ import { popup , popupMenu , success , alert } from '@/os.js' ;
1515import { unisonReload , reloadChannel } from '@/utility/unison-reload.js' ;
1616import { prefer } from '@/preferences.js' ;
1717import { store } from '@/store.js' ;
1818import { $i } from '@/i.js' ;
19- import { signout } from '@/signout.js' ;
2019
2120type AccountWithToken = Misskey . entities . MeDetailed & { token : string } ;
2221
@@ -40,9 +39,11 @@ export async function getAccounts(): Promise<{
4039}
4140
4241async function addAccount ( host : string , user : Misskey . entities . User , token : AccountWithToken [ 'token' ] ) {
42+ const key = host + '/' + user . id ;
43+ store . set ( 'accountTokens' , { ...store . s . accountTokens , [ key ] : token } ) ;
44+ store . set ( 'accountInfos' , { ...store . s . accountInfos , [ key ] : user } ) ;
45+
4346 if ( ! prefer . s . accounts . some ( x => x [ 0 ] === host && x [ 1 ] . id === user . id ) ) {
44- store . set ( 'accountTokens' , { ...store . s . accountTokens , [ host + '/' + user . id ] : token } ) ;
45- store . set ( 'accountInfos' , { ...store . s . accountInfos , [ host + '/' + user . id ] : user } ) ;
4647 prefer . commit ( 'accounts' , [ ...prefer . s . accounts , [ host , { id : user . id , username : user . username } ] ] ) ;
4748 }
4849}
@@ -60,6 +61,17 @@ export async function removeAccount(host: string, id: AccountWithToken['id']) {
6061
6162const isAccountDeleted = Symbol ( 'isAccountDeleted' ) ;
6263
64+ async function removeAccountByToken ( token : string ) {
65+ const entry = Object . entries ( store . s . accountTokens ) . find ( ( [ , value ] ) => value === token ) ;
66+ if ( ! entry ) return ;
67+ const [ key ] = entry ;
68+ const separatorIndex = key . lastIndexOf ( '/' ) ;
69+ if ( separatorIndex === - 1 ) return ;
70+ const accountHost = key . slice ( 0 , separatorIndex ) ;
71+ const accountId = key . slice ( separatorIndex + 1 ) ;
72+ await removeAccount ( accountHost , accountId ) ;
73+ }
74+
6375function fetchAccount ( token : string , id ?: string , forceShowDialog ?: boolean ) : Promise < Misskey . entities . MeDetailed > {
6476 return new Promise ( ( done , fail ) => {
6577 window . fetch ( `${ apiUrl } /i` , {
@@ -152,16 +164,14 @@ export function updateCurrentAccountPartial(accountData: Partial<Misskey.entitie
152164
153165export async function refreshCurrentAccount ( ) {
154166 if ( ! $i ) return ;
155- return fetchAccount ( $i . token , $i . id ) . then ( updateCurrentAccount ) . catch ( reason => {
156- if ( reason === isAccountDeleted ) {
157- removeAccount ( host , $i ! . id ) ;
158- if ( Object . keys ( store . s . accountTokens ) . length > 0 ) {
159- login ( Object . values ( store . s . accountTokens ) [ 0 ] ) ;
160- } else {
161- signout ( ) ;
167+ return fetchAccount ( $i . token , $i . id )
168+ . then ( updateCurrentAccount )
169+ . catch ( async reason => {
170+ if ( reason === isAccountDeleted ) {
171+ const { signout } = await import ( '@/signout.js' ) ;
172+ await signout ( ) ;
162173 }
163- }
164- } ) ;
174+ } ) ;
165175}
166176
167177export async function login ( token : AccountWithToken [ 'token' ] , redirect ?: string ) {
@@ -171,7 +181,10 @@ export async function login(token: AccountWithToken['token'], redirect?: string)
171181 showing : showing ,
172182 } , { } , 'closed' ) ;
173183
174- const me = await fetchAccount ( token , undefined , true ) . catch ( reason => {
184+ const me = await fetchAccount ( token , undefined , true ) . catch ( async reason => {
185+ if ( reason === isAccountDeleted ) {
186+ await removeAccountByToken ( token ) ;
187+ }
175188 showing . value = false ;
176189 throw reason ;
177190 } ) ;
@@ -203,7 +216,6 @@ export async function switchAccount(host: string, id: string) {
203216 } else {
204217 await popup ( defineAsyncComponent ( ( ) => import ( '@/components/MkSigninDialog.vue' ) ) , { } , {
205218 done : async ( res : Misskey . entities . SigninFlowResponse & { finished : true } ) => {
206- store . set ( 'accountTokens' , { ...store . s . accountTokens , [ host + '/' + res . id ] : res . i } ) ;
207219 login ( res . i ) ;
208220 } ,
209221 } , 'closed' ) ;
@@ -218,61 +230,72 @@ export async function openAccountMenu(opts: {
218230} , ev : MouseEvent ) {
219231 if ( ! $i ) return ;
220232
221- function createItem ( host : string , id : Misskey . entities . User [ 'id' ] , username : Misskey . entities . User [ 'username' ] , account : Misskey . entities . User | null | undefined , token : string ) : MenuItem {
222- if ( account ) {
223- return {
224- type : 'user' as const ,
225- user : account ,
226- active : opts . active != null ? opts . active === id : false ,
227- action : async ( ) => {
228- if ( opts . onChoose ) {
229- opts . onChoose ( account ) ;
230- } else {
231- switchAccount ( host , id ) ;
232- }
233- } ,
234- } ;
235- } else {
236- return {
237- type : 'button' as const ,
238- text : username ,
239- active : opts . active != null ? opts . active === id : false ,
240- action : async ( ) => {
241- if ( opts . onChoose ) {
242- fetchAccount ( token , id ) . then ( account => {
243- opts . onChoose ( account ) ;
244- } ) ;
245- } else {
246- switchAccount ( host , id ) ;
247- }
248- } ,
249- } ;
233+ async function switchAccountByUser ( account : Misskey . entities . User ) {
234+ const storedAccounts = await getAccounts ( ) ;
235+ const found = storedAccounts . find ( x => x . id === account . id ) ;
236+ if ( found ?. token ) {
237+ login ( found . token ) ;
238+ return ;
239+ }
240+ if ( found ) {
241+ await switchAccount ( found . host , found . id ) ;
250242 }
251243 }
252244
253- const menuItems : MenuItem [ ] = [ ] ;
245+ function switchAccountWithToken ( token : string ) {
246+ login ( token ) ;
247+ }
248+
249+ const storedAccounts = await getAccounts ( ) . then ( accounts => accounts . filter ( x => x . id !== $i . id ) ) ;
250+ const accountsPromise = storedAccounts . length > 0
251+ ? misskeyApi ( 'users/show' , { userIds : storedAccounts . map ( x => x . id ) } )
252+ : Promise . resolve ( [ ] as Misskey . entities . User [ ] ) ;
253+
254+ function createItem ( account : Misskey . entities . User ) {
255+ return {
256+ type : 'user' as const ,
257+ user : account ,
258+ active : opts . active != null ? opts . active === account . id : false ,
259+ action : ( ) => {
260+ if ( opts . onChoose ) {
261+ opts . onChoose ( account ) ;
262+ } else {
263+ switchAccountByUser ( account ) ;
264+ }
265+ } ,
266+ } ;
267+ }
268+
269+ const accountItemPromises : MenuItem [ ] = storedAccounts . map ( a => new Promise < MenuButton | MenuUser > ( res => {
270+ accountsPromise . then ( accounts => {
271+ const account = accounts . find ( x => x . id === a . id ) ;
272+ if ( account == null ) {
273+ return res ( {
274+ type : 'button' as const ,
275+ text : a . username ?? a . id ,
276+ active : opts . active != null ? opts . active === a . id : false ,
277+ action : ( ) => {
278+ if ( a . token ) {
279+ switchAccountWithToken ( a . token ) ;
280+ } else {
281+ switchAccount ( a . host , a . id ) ;
282+ }
283+ } ,
284+ } ) ;
285+ }
254286
255- // TODO: $iのホストも比較したいけど通常null
256- const accountItems = ( await getAccounts ( ) . then ( accounts => accounts . filter ( x => x . id !== $i . id ) ) ) . map ( a => createItem ( a . host , a . id , a . username , a . user , a . token ) ) ;
287+ res ( createItem ( account ) ) ;
288+ } ) ;
289+ } ) ) ;
257290
258291 if ( opts . withExtraOperation ) {
259- menuItems . push ( {
260- type : 'link' ,
292+ popupMenu ( [ ... [ {
293+ type : 'link' as const ,
261294 text : i18n . ts . profile ,
262295 to : `/@${ $i . username } ` ,
263296 avatar : $i ,
264- } , {
265- type : 'divider' ,
266- } ) ;
267-
268- if ( opts . includeCurrentAccount ) {
269- menuItems . push ( createItem ( host , $i . id , $i . username , $i , $i . token ) ) ;
270- }
271-
272- menuItems . push ( ...accountItems ) ;
273-
274- menuItems . push ( {
275- type : 'parent' ,
297+ } , { type : 'divider' as const } , ...( opts . includeCurrentAccount ? [ createItem ( $i ) ] : [ ] ) , ...accountItemPromises , {
298+ type : 'parent' as const ,
276299 icon : 'ti ti-plus' ,
277300 text : i18n . ts . addAccount ,
278301 children : [ {
@@ -289,28 +312,24 @@ export async function openAccountMenu(opts: {
289312 action : ( ) => {
290313 getAccountWithSignupDialog ( ) . then ( res => {
291314 if ( res != null ) {
292- switchAccount ( host , res . id ) ;
315+ switchAccountWithToken ( res . token ) ;
293316 }
294317 } ) ;
295318 } ,
296319 } ] ,
297320 } , {
298- type : 'link' ,
321+ type : 'link' as const ,
299322 icon : 'ti ti-users' ,
300323 text : i18n . ts . manageAccounts ,
301324 to : '/settings/accounts' ,
325+ } ] ] , ev . currentTarget ?? ev . target , {
326+ align : 'left' ,
302327 } ) ;
303328 } else {
304- if ( opts . includeCurrentAccount ) {
305- menuItems . push ( createItem ( host , $i . id , $i . username , $i , $i . token ) ) ;
306- }
307-
308- menuItems . push ( ...accountItems ) ;
329+ popupMenu ( [ ...( opts . includeCurrentAccount ? [ createItem ( $i ) ] : [ ] ) , ...accountItemPromises ] , ev . currentTarget ?? ev . target , {
330+ align : 'left' ,
331+ } ) ;
309332 }
310-
311- popupMenu ( menuItems , ev . currentTarget ?? ev . target , {
312- align : 'left' ,
313- } ) ;
314333}
315334
316335export function getAccountWithSigninDialog ( ) : Promise < { id : string , token : string } | null > {
0 commit comments