Merge branch 'develop' into fix-msg-room
This commit is contained in:
		
						commit
						7af5562b5a
					
				
					 94 changed files with 750 additions and 775 deletions
				
			
		|  | @ -15,6 +15,8 @@ | ||||||
| - 投稿のNSFW画像を表示したあとにリアクションが更新されると画像が非表示になる問題を修正 | - 投稿のNSFW画像を表示したあとにリアクションが更新されると画像が非表示になる問題を修正 | ||||||
| - 「クリップ」ページが開かない問題を修正 | - 「クリップ」ページが開かない問題を修正 | ||||||
| - トレンドウィジェットが動作しないのを修正 | - トレンドウィジェットが動作しないのを修正 | ||||||
|  | - リアクション設定で絵文字ピッカーが開かないのを修正 | ||||||
|  | - DMページでメンションが含まれる問題を修正 | ||||||
| 
 | 
 | ||||||
| ## 12.102.1 (2022/01/27) | ## 12.102.1 (2022/01/27) | ||||||
| ### Bugfixes | ### Bugfixes | ||||||
|  |  | ||||||
|  | @ -192,31 +192,31 @@ export async function openAccountMenu(opts: { | ||||||
| 	if (opts.withExtraOperation) { | 	if (opts.withExtraOperation) { | ||||||
| 		popupMenu([...[{ | 		popupMenu([...[{ | ||||||
| 			type: 'link', | 			type: 'link', | ||||||
| 			text: i18n.locale.profile, | 			text: i18n.ts.profile, | ||||||
| 			to: `/@${ $i.username }`, | 			to: `/@${ $i.username }`, | ||||||
| 			avatar: $i, | 			avatar: $i, | ||||||
| 		}, null, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { | 		}, null, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { | ||||||
| 			icon: 'fas fa-plus', | 			icon: 'fas fa-plus', | ||||||
| 			text: i18n.locale.addAccount, | 			text: i18n.ts.addAccount, | ||||||
| 			action: () => { | 			action: () => { | ||||||
| 				popupMenu([{ | 				popupMenu([{ | ||||||
| 					text: i18n.locale.existingAccount, | 					text: i18n.ts.existingAccount, | ||||||
| 					action: () => { showSigninDialog(); }, | 					action: () => { showSigninDialog(); }, | ||||||
| 				}, { | 				}, { | ||||||
| 					text: i18n.locale.createAccount, | 					text: i18n.ts.createAccount, | ||||||
| 					action: () => { createAccount(); }, | 					action: () => { createAccount(); }, | ||||||
| 				}], ev.currentTarget || ev.target); | 				}], ev.currentTarget ?? ev.target); | ||||||
| 			}, | 			}, | ||||||
| 		}, { | 		}, { | ||||||
| 			type: 'link', | 			type: 'link', | ||||||
| 			icon: 'fas fa-users', | 			icon: 'fas fa-users', | ||||||
| 			text: i18n.locale.manageAccounts, | 			text: i18n.ts.manageAccounts, | ||||||
| 			to: `/settings/accounts`, | 			to: `/settings/accounts`, | ||||||
| 		}]], ev.currentTarget || ev.target, { | 		}]], ev.currentTarget ?? ev.target, { | ||||||
| 			align: 'left' | 			align: 'left' | ||||||
| 		}); | 		}); | ||||||
| 	} else { | 	} else { | ||||||
| 		popupMenu([...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises], ev.currentTarget || ev.target, { | 		popupMenu([...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises], ev.currentTarget ?? ev.target, { | ||||||
| 			align: 'left' | 			align: 'left' | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| <XWindow ref="window" :initial-width="400" :initial-height="500" :can-resize="true" @closed="emit('closed')"> | <XWindow ref="window" :initial-width="400" :initial-height="500" :can-resize="true" @closed="emit('closed')"> | ||||||
| 	<template #header> | 	<template #header> | ||||||
| 		<i class="fas fa-exclamation-circle" style="margin-right: 0.5em;"></i> | 		<i class="fas fa-exclamation-circle" style="margin-right: 0.5em;"></i> | ||||||
| 		<I18n :src="i18n.locale.reportAbuseOf" tag="span"> | 		<I18n :src="i18n.ts.reportAbuseOf" tag="span"> | ||||||
| 			<template #name> | 			<template #name> | ||||||
| 				<b><MkAcct :user="user"/></b> | 				<b><MkAcct :user="user"/></b> | ||||||
| 			</template> | 			</template> | ||||||
|  | @ -11,12 +11,12 @@ | ||||||
| 	<div class="dpvffvvy _monolithic_"> | 	<div class="dpvffvvy _monolithic_"> | ||||||
| 		<div class="_section"> | 		<div class="_section"> | ||||||
| 			<MkTextarea v-model="comment"> | 			<MkTextarea v-model="comment"> | ||||||
| 				<template #label>{{ i18n.locale.details }}</template> | 				<template #label>{{ i18n.ts.details }}</template> | ||||||
| 				<template #caption>{{ i18n.locale.fillAbuseReportDescription }}</template> | 				<template #caption>{{ i18n.ts.fillAbuseReportDescription }}</template> | ||||||
| 			</MkTextarea> | 			</MkTextarea> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="_section"> | 		<div class="_section"> | ||||||
| 			<MkButton primary full :disabled="comment.length === 0" @click="send">{{ i18n.locale.send }}</MkButton> | 			<MkButton primary full :disabled="comment.length === 0" @click="send">{{ i18n.ts.send }}</MkButton> | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
| </XWindow> | </XWindow> | ||||||
|  | @ -50,7 +50,7 @@ function send() { | ||||||
| 	}, undefined).then(res => { | 	}, undefined).then(res => { | ||||||
| 		os.alert({ | 		os.alert({ | ||||||
| 			type: 'success', | 			type: 'success', | ||||||
| 			text: i18n.locale.abuseReported | 			text: i18n.ts.abuseReported | ||||||
| 		}); | 		}); | ||||||
| 		window.value?.close(); | 		window.value?.close(); | ||||||
| 		emit('closed'); | 		emit('closed'); | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ | ||||||
| 			</span> | 			</span> | ||||||
| 			<span class="username">@{{ acct(user) }}</span> | 			<span class="username">@{{ acct(user) }}</span> | ||||||
| 		</li> | 		</li> | ||||||
| 		<li tabindex="-1" class="choose" @click="chooseUser()" @keydown="onKeydown">{{ i18n.locale.selectUser }}</li> | 		<li tabindex="-1" class="choose" @click="chooseUser()" @keydown="onKeydown">{{ i18n.ts.selectUser }}</li> | ||||||
| 	</ol> | 	</ol> | ||||||
| 	<ol v-else-if="hashtags.length > 0" ref="suggests" class="hashtags"> | 	<ol v-else-if="hashtags.length > 0" ref="suggests" class="hashtags"> | ||||||
| 		<li v-for="hashtag in hashtags" tabindex="-1" @click="complete(type, hashtag)" @keydown="onKeydown"> | 		<li v-for="hashtag in hashtags" tabindex="-1" @click="complete(type, hashtag)" @keydown="onKeydown"> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <div> | <div> | ||||||
| 	<span v-if="!available">{{ i18n.locale.waiting }}<MkEllipsis/></span> | 	<span v-if="!available">{{ i18n.ts.waiting }}<MkEllipsis/></span> | ||||||
| 	<div ref="captchaEl"></div> | 	<div ref="captchaEl"></div> | ||||||
| </div> | </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  | @ -6,14 +6,14 @@ | ||||||
| > | > | ||||||
| 	<template v-if="!wait"> | 	<template v-if="!wait"> | ||||||
| 		<template v-if="isFollowing"> | 		<template v-if="isFollowing"> | ||||||
| 			<span v-if="full">{{ i18n.locale.unfollow }}</span><i class="fas fa-minus"></i> | 			<span v-if="full">{{ i18n.ts.unfollow }}</span><i class="fas fa-minus"></i> | ||||||
| 		</template> | 		</template> | ||||||
| 		<template v-else> | 		<template v-else> | ||||||
| 			<span v-if="full">{{ i18n.locale.follow }}</span><i class="fas fa-plus"></i> | 			<span v-if="full">{{ i18n.ts.follow }}</span><i class="fas fa-plus"></i> | ||||||
| 		</template> | 		</template> | ||||||
| 	</template> | 	</template> | ||||||
| 	<template v-else> | 	<template v-else> | ||||||
| 		<span v-if="full">{{ i18n.locale.processing }}</span><i class="fas fa-spinner fa-pulse fa-fw"></i> | 		<span v-if="full">{{ i18n.ts.processing }}</span><i class="fas fa-spinner fa-pulse fa-fw"></i> | ||||||
| 	</template> | 	</template> | ||||||
| </button> | </button> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ | ||||||
| 		<div class="status"> | 		<div class="status"> | ||||||
| 			<div> | 			<div> | ||||||
| 				<i class="fas fa-users fa-fw"></i> | 				<i class="fas fa-users fa-fw"></i> | ||||||
| 				<I18n :src="i18n.locale._channel.usersCount" tag="span" style="margin-left: 4px;"> | 				<I18n :src="i18n.ts._channel.usersCount" tag="span" style="margin-left: 4px;"> | ||||||
| 					<template #n> | 					<template #n> | ||||||
| 						<b>{{ channel.usersCount }}</b> | 						<b>{{ channel.usersCount }}</b> | ||||||
| 					</template> | 					</template> | ||||||
|  | @ -14,7 +14,7 @@ | ||||||
| 			</div> | 			</div> | ||||||
| 			<div> | 			<div> | ||||||
| 				<i class="fas fa-pencil-alt fa-fw"></i> | 				<i class="fas fa-pencil-alt fa-fw"></i> | ||||||
| 				<I18n :src="i18n.locale._channel.notesCount" tag="span" style="margin-left: 4px;"> | 				<I18n :src="i18n.ts._channel.notesCount" tag="span" style="margin-left: 4px;"> | ||||||
| 					<template #n> | 					<template #n> | ||||||
| 						<b>{{ channel.notesCount }}</b> | 						<b>{{ channel.notesCount }}</b> | ||||||
| 					</template> | 					</template> | ||||||
|  | @ -27,7 +27,7 @@ | ||||||
| 	</article> | 	</article> | ||||||
| 	<footer> | 	<footer> | ||||||
| 		<span v-if="channel.lastNotedAt"> | 		<span v-if="channel.lastNotedAt"> | ||||||
| 			{{ i18n.locale.updatedAt }}: <MkTime :time="channel.lastNotedAt"/> | 			{{ i18n.ts.updatedAt }}: <MkTime :time="channel.lastNotedAt"/> | ||||||
| 		</span> | 		</span> | ||||||
| 	</footer> | 	</footer> | ||||||
| </MkA> | </MkA> | ||||||
|  |  | ||||||
|  | @ -143,6 +143,7 @@ export default defineComponent({ | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			const gridColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; | 			const gridColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; | ||||||
|  | 			const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; | ||||||
| 
 | 
 | ||||||
| 			// フォントカラー | 			// フォントカラー | ||||||
| 			Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg'); | 			Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg'); | ||||||
|  | @ -255,6 +256,27 @@ export default defineComponent({ | ||||||
| 						}, | 						}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
|  | 				plugins: [{ | ||||||
|  | 					id: 'vLine', | ||||||
|  | 					beforeDraw(chart, args, options) { | ||||||
|  | 						if (chart.tooltip._active && chart.tooltip._active.length) { | ||||||
|  | 							const activePoint = chart.tooltip._active[0]; | ||||||
|  | 							const ctx = chart.ctx; | ||||||
|  | 							const x = activePoint.element.x; | ||||||
|  | 							const topY = chart.scales.y.top; | ||||||
|  | 							const bottomY = chart.scales.y.bottom; | ||||||
|  | 
 | ||||||
|  | 							ctx.save(); | ||||||
|  | 							ctx.beginPath(); | ||||||
|  | 							ctx.moveTo(x, bottomY); | ||||||
|  | 							ctx.lineTo(x, topY); | ||||||
|  | 							ctx.lineWidth = 1; | ||||||
|  | 							ctx.strokeStyle = vLineColor; | ||||||
|  | 							ctx.stroke(); | ||||||
|  | 							ctx.restore(); | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				}] | ||||||
| 			}); | 			}); | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <button class="nrvgflfu _button" @click="toggle"> | <button class="nrvgflfu _button" @click="toggle"> | ||||||
| 	<b>{{ modelValue ? i18n.locale._cw.hide : i18n.locale._cw.show }}</b> | 	<b>{{ modelValue ? i18n.ts._cw.hide : i18n.ts._cw.show }}</b> | ||||||
| 	<span v-if="!modelValue">{{ label }}</span> | 	<span v-if="!modelValue">{{ label }}</span> | ||||||
| </button> | </button> | ||||||
| </template> | </template> | ||||||
|  | @ -25,7 +25,7 @@ const label = computed(() => { | ||||||
| 	return concat([ | 	return concat([ | ||||||
| 		props.note.text ? [i18n.t('_cw.chars', { count: length(props.note.text) })] : [], | 		props.note.text ? [i18n.t('_cw.chars', { count: length(props.note.text) })] : [], | ||||||
| 		props.note.files && props.note.files.length !== 0 ? [i18n.t('_cw.files', { count: props.note.files.length }) ] : [], | 		props.note.files && props.note.files.length !== 0 ? [i18n.t('_cw.files', { count: props.note.files.length }) ] : [], | ||||||
| 		props.note.poll != null ? [i18n.locale.poll] : [] | 		props.note.poll != null ? [i18n.ts.poll] : [] | ||||||
| 	] as string[][]).join(' / '); | 	] as string[][]).join(' / '); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -28,8 +28,8 @@ | ||||||
| 			</template> | 			</template> | ||||||
| 		</MkSelect> | 		</MkSelect> | ||||||
| 		<div v-if="(showOkButton || showCancelButton) && !actions" class="buttons"> | 		<div v-if="(showOkButton || showCancelButton) && !actions" class="buttons"> | ||||||
| 			<MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ (showCancelButton || input || select) ? i18n.locale.ok : i18n.locale.gotIt }}</MkButton> | 			<MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ (showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt }}</MkButton> | ||||||
| 			<MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ i18n.locale.cancel }}</MkButton> | 			<MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ i18n.ts.cancel }}</MkButton> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div v-if="actions" class="buttons"> | 		<div v-if="actions" class="buttons"> | ||||||
| 			<MkButton v-for="action in actions" :key="action.text" inline :primary="action.primary" @click="() => { action.callback(); close(); }">{{ action.text }}</MkButton> | 			<MkButton v-for="action in actions" :key="action.text" inline :primary="action.primary" @click="() => { action.callback(); close(); }">{{ action.text }}</MkButton> | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ | ||||||
| 	@closed="emit('closed')" | 	@closed="emit('closed')" | ||||||
| > | > | ||||||
| 	<template #header> | 	<template #header> | ||||||
| 		{{ multiple ? ((type === 'file') ? i18n.locale.selectFiles : i18n.locale.selectFolders) : ((type === 'file') ? i18n.locale.selectFile : i18n.locale.selectFolder) }} | 		{{ multiple ? ((type === 'file') ? i18n.ts.selectFiles : i18n.ts.selectFolders) : ((type === 'file') ? i18n.ts.selectFile : i18n.ts.selectFolder) }} | ||||||
| 		<span v-if="selected.length > 0" style="margin-left: 8px; opacity: 0.5;">({{ number(selected.length) }})</span> | 		<span v-if="selected.length > 0" style="margin-left: 8px; opacity: 0.5;">({{ number(selected.length) }})</span> | ||||||
| 	</template> | 	</template> | ||||||
| 	<XDrive :multiple="multiple" :select="type" @changeSelection="onChangeSelection" @selected="ok()"/> | 	<XDrive :multiple="multiple" :select="type" @changeSelection="onChangeSelection" @selected="ok()"/> | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ | ||||||
| 	@closed="emit('closed')" | 	@closed="emit('closed')" | ||||||
| > | > | ||||||
| 	<template #header> | 	<template #header> | ||||||
| 		{{ i18n.locale.drive }} | 		{{ i18n.ts.drive }} | ||||||
| 	</template> | 	</template> | ||||||
| 	<XDrive :initial-folder="initialFolder"/> | 	<XDrive :initial-folder="initialFolder"/> | ||||||
| </XWindow> | </XWindow> | ||||||
|  |  | ||||||
|  | @ -10,15 +10,15 @@ | ||||||
| > | > | ||||||
| 	<div v-if="$i?.avatarId == file.id" class="label"> | 	<div v-if="$i?.avatarId == file.id" class="label"> | ||||||
| 		<img src="/client-assets/label.svg"/> | 		<img src="/client-assets/label.svg"/> | ||||||
| 		<p>{{ i18n.locale.avatar }}</p> | 		<p>{{ i18n.ts.avatar }}</p> | ||||||
| 	</div> | 	</div> | ||||||
| 	<div v-if="$i?.bannerId == file.id" class="label"> | 	<div v-if="$i?.bannerId == file.id" class="label"> | ||||||
| 		<img src="/client-assets/label.svg"/> | 		<img src="/client-assets/label.svg"/> | ||||||
| 		<p>{{ i18n.locale.banner }}</p> | 		<p>{{ i18n.ts.banner }}</p> | ||||||
| 	</div> | 	</div> | ||||||
| 	<div v-if="file.isSensitive" class="label red"> | 	<div v-if="file.isSensitive" class="label red"> | ||||||
| 		<img src="/client-assets/label-red.svg"/> | 		<img src="/client-assets/label-red.svg"/> | ||||||
| 		<p>{{ i18n.locale.nsfw }}</p> | 		<p>{{ i18n.ts.nsfw }}</p> | ||||||
| 	</div> | 	</div> | ||||||
| 
 | 
 | ||||||
| 	<MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain"/> | 	<MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain"/> | ||||||
|  | @ -61,30 +61,30 @@ const title = computed(() => `${props.file.name}\n${props.file.type} ${bytes(pro | ||||||
| 
 | 
 | ||||||
| function getMenu() { | function getMenu() { | ||||||
| 	return [{ | 	return [{ | ||||||
| 		text: i18n.locale.rename, | 		text: i18n.ts.rename, | ||||||
| 		icon: 'fas fa-i-cursor', | 		icon: 'fas fa-i-cursor', | ||||||
| 		action: rename | 		action: rename | ||||||
| 	}, { | 	}, { | ||||||
| 		text: props.file.isSensitive ? i18n.locale.unmarkAsSensitive : i18n.locale.markAsSensitive, | 		text: props.file.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive, | ||||||
| 		icon: props.file.isSensitive ? 'fas fa-eye' : 'fas fa-eye-slash', | 		icon: props.file.isSensitive ? 'fas fa-eye' : 'fas fa-eye-slash', | ||||||
| 		action: toggleSensitive | 		action: toggleSensitive | ||||||
| 	}, { | 	}, { | ||||||
| 		text: i18n.locale.describeFile, | 		text: i18n.ts.describeFile, | ||||||
| 		icon: 'fas fa-i-cursor', | 		icon: 'fas fa-i-cursor', | ||||||
| 		action: describe | 		action: describe | ||||||
| 	}, null, { | 	}, null, { | ||||||
| 		text: i18n.locale.copyUrl, | 		text: i18n.ts.copyUrl, | ||||||
| 		icon: 'fas fa-link', | 		icon: 'fas fa-link', | ||||||
| 		action: copyUrl | 		action: copyUrl | ||||||
| 	}, { | 	}, { | ||||||
| 		type: 'a', | 		type: 'a', | ||||||
| 		href: props.file.url, | 		href: props.file.url, | ||||||
| 		target: '_blank', | 		target: '_blank', | ||||||
| 		text: i18n.locale.download, | 		text: i18n.ts.download, | ||||||
| 		icon: 'fas fa-download', | 		icon: 'fas fa-download', | ||||||
| 		download: props.file.name | 		download: props.file.name | ||||||
| 	}, null, { | 	}, null, { | ||||||
| 		text: i18n.locale.delete, | 		text: i18n.ts.delete, | ||||||
| 		icon: 'fas fa-trash-alt', | 		icon: 'fas fa-trash-alt', | ||||||
| 		danger: true, | 		danger: true, | ||||||
| 		action: deleteFile | 		action: deleteFile | ||||||
|  | @ -95,7 +95,7 @@ function onClick(ev: MouseEvent) { | ||||||
| 	if (props.selectMode) { | 	if (props.selectMode) { | ||||||
| 		emit('chosen', props.file); | 		emit('chosen', props.file); | ||||||
| 	} else { | 	} else { | ||||||
| 		os.popupMenu(getMenu(), (ev.currentTarget || ev.target || undefined) as HTMLElement | undefined); | 		os.popupMenu(getMenu(), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -120,8 +120,8 @@ function onDragend() { | ||||||
| 
 | 
 | ||||||
| function rename() { | function rename() { | ||||||
| 	os.inputText({ | 	os.inputText({ | ||||||
| 		title: i18n.locale.renameFile, | 		title: i18n.ts.renameFile, | ||||||
| 		placeholder: i18n.locale.inputNewFileName, | 		placeholder: i18n.ts.inputNewFileName, | ||||||
| 		default: props.file.name, | 		default: props.file.name, | ||||||
| 	}).then(({ canceled, result: name }) => { | 	}).then(({ canceled, result: name }) => { | ||||||
| 		if (canceled) return; | 		if (canceled) return; | ||||||
|  | @ -134,9 +134,9 @@ function rename() { | ||||||
| 
 | 
 | ||||||
| function describe() { | function describe() { | ||||||
| 	os.popup(import('@/components/media-caption.vue'), { | 	os.popup(import('@/components/media-caption.vue'), { | ||||||
| 		title: i18n.locale.describeFile, | 		title: i18n.ts.describeFile, | ||||||
| 		input: { | 		input: { | ||||||
| 			placeholder: i18n.locale.inputNewDescription, | 			placeholder: i18n.ts.inputNewDescription, | ||||||
| 			default: props.file.comment !== null ? props.file.comment : '', | 			default: props.file.comment !== null ? props.file.comment : '', | ||||||
| 		}, | 		}, | ||||||
| 		image: props.file | 		image: props.file | ||||||
|  |  | ||||||
|  | @ -20,7 +20,7 @@ | ||||||
| 		{{ folder.name }} | 		{{ folder.name }} | ||||||
| 	</p> | 	</p> | ||||||
| 	<p v-if="defaultStore.state.uploadFolder == folder.id" class="upload"> | 	<p v-if="defaultStore.state.uploadFolder == folder.id" class="upload"> | ||||||
| 		{{ i18n.locale.uploadFolder }} | 		{{ i18n.ts.uploadFolder }} | ||||||
| 	</p> | 	</p> | ||||||
| 	<button v-if="selectMode" class="checkbox _button" :class="{ checked: isSelected }" @click.prevent.stop="checkboxClicked"></button> | 	<button v-if="selectMode" class="checkbox _button" :class="{ checked: isSelected }" @click.prevent.stop="checkboxClicked"></button> | ||||||
| </div> | </div> | ||||||
|  | @ -146,14 +146,14 @@ function onDrop(ev: DragEvent) { | ||||||
| 			switch (err) { | 			switch (err) { | ||||||
| 				case 'detected-circular-definition': | 				case 'detected-circular-definition': | ||||||
| 					os.alert({ | 					os.alert({ | ||||||
| 						title: i18n.locale.unableToProcess, | 						title: i18n.ts.unableToProcess, | ||||||
| 						text: i18n.locale.circularReferenceFolder | 						text: i18n.ts.circularReferenceFolder | ||||||
| 					}); | 					}); | ||||||
| 					break; | 					break; | ||||||
| 				default: | 				default: | ||||||
| 					os.alert({ | 					os.alert({ | ||||||
| 						type: 'error', | 						type: 'error', | ||||||
| 						text: i18n.locale.somethingHappened | 						text: i18n.ts.somethingHappened | ||||||
| 					}); | 					}); | ||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
|  | @ -184,8 +184,8 @@ function go() { | ||||||
| 
 | 
 | ||||||
| function rename() { | function rename() { | ||||||
| 	os.inputText({ | 	os.inputText({ | ||||||
| 		title: i18n.locale.renameFolder, | 		title: i18n.ts.renameFolder, | ||||||
| 		placeholder: i18n.locale.inputNewFolderName, | 		placeholder: i18n.ts.inputNewFolderName, | ||||||
| 		default: props.folder.name | 		default: props.folder.name | ||||||
| 	}).then(({ canceled, result: name }) => { | 	}).then(({ canceled, result: name }) => { | ||||||
| 		if (canceled) return; | 		if (canceled) return; | ||||||
|  | @ -208,14 +208,14 @@ function deleteFolder() { | ||||||
| 			case 'b0fc8a17-963c-405d-bfbc-859a487295e1': | 			case 'b0fc8a17-963c-405d-bfbc-859a487295e1': | ||||||
| 				os.alert({ | 				os.alert({ | ||||||
| 					type: 'error', | 					type: 'error', | ||||||
| 					title: i18n.locale.unableToDelete, | 					title: i18n.ts.unableToDelete, | ||||||
| 					text: i18n.locale.hasChildFilesOrFolders | 					text: i18n.ts.hasChildFilesOrFolders | ||||||
| 				}); | 				}); | ||||||
| 				break; | 				break; | ||||||
| 			default: | 			default: | ||||||
| 				os.alert({ | 				os.alert({ | ||||||
| 					type: 'error', | 					type: 'error', | ||||||
| 					text: i18n.locale.unableToDelete | 					text: i18n.ts.unableToDelete | ||||||
| 				}); | 				}); | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
|  | @ -227,7 +227,7 @@ function setAsUploadFolder() { | ||||||
| 
 | 
 | ||||||
| function onContextmenu(ev: MouseEvent) { | function onContextmenu(ev: MouseEvent) { | ||||||
| 	os.contextMenu([{ | 	os.contextMenu([{ | ||||||
| 		text: i18n.locale.openInWindow, | 		text: i18n.ts.openInWindow, | ||||||
| 		icon: 'fas fa-window-restore', | 		icon: 'fas fa-window-restore', | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			os.popup(import('./drive-window.vue'), { | 			os.popup(import('./drive-window.vue'), { | ||||||
|  | @ -236,11 +236,11 @@ function onContextmenu(ev: MouseEvent) { | ||||||
| 			}, 'closed'); | 			}, 'closed'); | ||||||
| 		} | 		} | ||||||
| 	}, null, { | 	}, null, { | ||||||
| 		text: i18n.locale.rename, | 		text: i18n.ts.rename, | ||||||
| 		icon: 'fas fa-i-cursor', | 		icon: 'fas fa-i-cursor', | ||||||
| 		action: rename, | 		action: rename, | ||||||
| 	}, null, { | 	}, null, { | ||||||
| 		text: i18n.locale.delete, | 		text: i18n.ts.delete, | ||||||
| 		icon: 'fas fa-trash-alt', | 		icon: 'fas fa-trash-alt', | ||||||
| 		danger: true, | 		danger: true, | ||||||
| 		action: deleteFolder, | 		action: deleteFolder, | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ | ||||||
| 	@drop.stop="onDrop" | 	@drop.stop="onDrop" | ||||||
| > | > | ||||||
| 	<i v-if="folder == null" class="fas fa-cloud"></i> | 	<i v-if="folder == null" class="fas fa-cloud"></i> | ||||||
| 	<span>{{ folder == null ? i18n.locale.drive : folder.name }}</span> | 	<span>{{ folder == null ? i18n.ts.drive : folder.name }}</span> | ||||||
| </div> | </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -54,7 +54,7 @@ | ||||||
| 				/> | 				/> | ||||||
| 				<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> | 				<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> | ||||||
| 				<div v-for="(n, i) in 16" :key="i" class="padding"></div> | 				<div v-for="(n, i) in 16" :key="i" class="padding"></div> | ||||||
| 				<MkButton v-if="moreFolders" ref="moreFolders">{{ i18n.locale.loadMore }}</MkButton> | 				<MkButton v-if="moreFolders" ref="moreFolders">{{ i18n.ts.loadMore }}</MkButton> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div v-show="files.length > 0" ref="filesContainer" class="files"> | 			<div v-show="files.length > 0" ref="filesContainer" class="files"> | ||||||
| 				<XFile | 				<XFile | ||||||
|  | @ -71,12 +71,12 @@ | ||||||
| 				/> | 				/> | ||||||
| 				<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> | 				<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> | ||||||
| 				<div v-for="(n, i) in 16" :key="i" class="padding"></div> | 				<div v-for="(n, i) in 16" :key="i" class="padding"></div> | ||||||
| 				<MkButton v-show="moreFiles" ref="loadMoreFiles" @click="fetchMoreFiles">{{ i18n.locale.loadMore }}</MkButton> | 				<MkButton v-show="moreFiles" ref="loadMoreFiles" @click="fetchMoreFiles">{{ i18n.ts.loadMore }}</MkButton> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div v-if="files.length == 0 && folders.length == 0 && !fetching" class="empty"> | 			<div v-if="files.length == 0 && folders.length == 0 && !fetching" class="empty"> | ||||||
| 				<p v-if="draghover">{{ i18n.t('empty-draghover') }}</p> | 				<p v-if="draghover">{{ i18n.t('empty-draghover') }}</p> | ||||||
| 				<p v-if="!draghover && folder == null"><strong>{{ i18n.locale.emptyDrive }}</strong><br/>{{ i18n.t('empty-drive-description') }}</p> | 				<p v-if="!draghover && folder == null"><strong>{{ i18n.ts.emptyDrive }}</strong><br/>{{ i18n.t('empty-drive-description') }}</p> | ||||||
| 				<p v-if="!draghover && folder != null">{{ i18n.locale.emptyFolder }}</p> | 				<p v-if="!draghover && folder != null">{{ i18n.ts.emptyFolder }}</p> | ||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
| 		<MkLoading v-if="fetching"/> | 		<MkLoading v-if="fetching"/> | ||||||
|  | @ -253,14 +253,14 @@ function onDrop(e: DragEvent): any { | ||||||
| 			switch (err) { | 			switch (err) { | ||||||
| 				case 'detected-circular-definition': | 				case 'detected-circular-definition': | ||||||
| 					os.alert({ | 					os.alert({ | ||||||
| 						title: i18n.locale.unableToProcess, | 						title: i18n.ts.unableToProcess, | ||||||
| 						text: i18n.locale.circularReferenceFolder | 						text: i18n.ts.circularReferenceFolder | ||||||
| 					}); | 					}); | ||||||
| 					break; | 					break; | ||||||
| 				default: | 				default: | ||||||
| 					os.alert({ | 					os.alert({ | ||||||
| 						type: 'error', | 						type: 'error', | ||||||
| 						text: i18n.locale.somethingHappened | 						text: i18n.ts.somethingHappened | ||||||
| 					}); | 					}); | ||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
|  | @ -274,9 +274,9 @@ function selectLocalFile() { | ||||||
| 
 | 
 | ||||||
| function urlUpload() { | function urlUpload() { | ||||||
| 	os.inputText({ | 	os.inputText({ | ||||||
| 		title: i18n.locale.uploadFromUrl, | 		title: i18n.ts.uploadFromUrl, | ||||||
| 		type: 'url', | 		type: 'url', | ||||||
| 		placeholder: i18n.locale.uploadFromUrlDescription | 		placeholder: i18n.ts.uploadFromUrlDescription | ||||||
| 	}).then(({ canceled, result: url }) => { | 	}).then(({ canceled, result: url }) => { | ||||||
| 		if (canceled || !url) return; | 		if (canceled || !url) return; | ||||||
| 		os.api('drive/files/upload-from-url', { | 		os.api('drive/files/upload-from-url', { | ||||||
|  | @ -285,16 +285,16 @@ function urlUpload() { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		os.alert({ | 		os.alert({ | ||||||
| 			title: i18n.locale.uploadFromUrlRequested, | 			title: i18n.ts.uploadFromUrlRequested, | ||||||
| 			text: i18n.locale.uploadFromUrlMayTakeTime | 			text: i18n.ts.uploadFromUrlMayTakeTime | ||||||
| 		}); | 		}); | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function createFolder() { | function createFolder() { | ||||||
| 	os.inputText({ | 	os.inputText({ | ||||||
| 		title: i18n.locale.createFolder, | 		title: i18n.ts.createFolder, | ||||||
| 		placeholder: i18n.locale.folderName | 		placeholder: i18n.ts.folderName | ||||||
| 	}).then(({ canceled, result: name }) => { | 	}).then(({ canceled, result: name }) => { | ||||||
| 		if (canceled) return; | 		if (canceled) return; | ||||||
| 		os.api('drive/folders/create', { | 		os.api('drive/folders/create', { | ||||||
|  | @ -308,8 +308,8 @@ function createFolder() { | ||||||
| 
 | 
 | ||||||
| function renameFolder(folderToRename: Misskey.entities.DriveFolder) { | function renameFolder(folderToRename: Misskey.entities.DriveFolder) { | ||||||
| 	os.inputText({ | 	os.inputText({ | ||||||
| 		title: i18n.locale.renameFolder, | 		title: i18n.ts.renameFolder, | ||||||
| 		placeholder: i18n.locale.inputNewFolderName, | 		placeholder: i18n.ts.inputNewFolderName, | ||||||
| 		default: folderToRename.name | 		default: folderToRename.name | ||||||
| 	}).then(({ canceled, result: name }) => { | 	}).then(({ canceled, result: name }) => { | ||||||
| 		if (canceled) return; | 		if (canceled) return; | ||||||
|  | @ -334,14 +334,14 @@ function deleteFolder(folderToDelete: Misskey.entities.DriveFolder) { | ||||||
| 			case 'b0fc8a17-963c-405d-bfbc-859a487295e1': | 			case 'b0fc8a17-963c-405d-bfbc-859a487295e1': | ||||||
| 				os.alert({ | 				os.alert({ | ||||||
| 					type: 'error', | 					type: 'error', | ||||||
| 					title: i18n.locale.unableToDelete, | 					title: i18n.ts.unableToDelete, | ||||||
| 					text: i18n.locale.hasChildFilesOrFolders | 					text: i18n.ts.hasChildFilesOrFolders | ||||||
| 				}); | 				}); | ||||||
| 				break; | 				break; | ||||||
| 			default: | 			default: | ||||||
| 				os.alert({ | 				os.alert({ | ||||||
| 					type: 'error', | 					type: 'error', | ||||||
| 					text: i18n.locale.unableToDelete | 					text: i18n.ts.unableToDelete | ||||||
| 				}); | 				}); | ||||||
| 			} | 			} | ||||||
| 	}); | 	}); | ||||||
|  | @ -562,36 +562,36 @@ function fetchMoreFiles() { | ||||||
| 
 | 
 | ||||||
| function getMenu() { | function getMenu() { | ||||||
| 	return [{ | 	return [{ | ||||||
| 		text: i18n.locale.addFile, | 		text: i18n.ts.addFile, | ||||||
| 		type: 'label' | 		type: 'label' | ||||||
| 	}, { | 	}, { | ||||||
| 		text: i18n.locale.upload, | 		text: i18n.ts.upload, | ||||||
| 		icon: 'fas fa-upload', | 		icon: 'fas fa-upload', | ||||||
| 		action: () => { selectLocalFile(); } | 		action: () => { selectLocalFile(); } | ||||||
| 	}, { | 	}, { | ||||||
| 		text: i18n.locale.fromUrl, | 		text: i18n.ts.fromUrl, | ||||||
| 		icon: 'fas fa-link', | 		icon: 'fas fa-link', | ||||||
| 		action: () => { urlUpload(); } | 		action: () => { urlUpload(); } | ||||||
| 	}, null, { | 	}, null, { | ||||||
| 		text: folder.value ? folder.value.name : i18n.locale.drive, | 		text: folder.value ? folder.value.name : i18n.ts.drive, | ||||||
| 		type: 'label' | 		type: 'label' | ||||||
| 	}, folder.value ? { | 	}, folder.value ? { | ||||||
| 		text: i18n.locale.renameFolder, | 		text: i18n.ts.renameFolder, | ||||||
| 		icon: 'fas fa-i-cursor', | 		icon: 'fas fa-i-cursor', | ||||||
| 		action: () => { renameFolder(folder.value); } | 		action: () => { renameFolder(folder.value); } | ||||||
| 	} : undefined, folder.value ? { | 	} : undefined, folder.value ? { | ||||||
| 		text: i18n.locale.deleteFolder, | 		text: i18n.ts.deleteFolder, | ||||||
| 		icon: 'fas fa-trash-alt', | 		icon: 'fas fa-trash-alt', | ||||||
| 		action: () => { deleteFolder(folder.value as Misskey.entities.DriveFolder); } | 		action: () => { deleteFolder(folder.value as Misskey.entities.DriveFolder); } | ||||||
| 	} : undefined, { | 	} : undefined, { | ||||||
| 		text: i18n.locale.createFolder, | 		text: i18n.ts.createFolder, | ||||||
| 		icon: 'fas fa-folder-plus', | 		icon: 'fas fa-folder-plus', | ||||||
| 		action: () => { createFolder(); } | 		action: () => { createFolder(); } | ||||||
| 	}]; | 	}]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function showMenu(ev: MouseEvent) { | function showMenu(ev: MouseEvent) { | ||||||
| 	os.popupMenu(getMenu(), (ev.currentTarget || ev.target || undefined) as HTMLElement | undefined); | 	os.popupMenu(getMenu(), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function onContextmenu(ev: MouseEvent) { | function onContextmenu(ev: MouseEvent) { | ||||||
|  |  | ||||||
|  | @ -32,20 +32,20 @@ import MkEmojiPicker from '@/components/emoji-picker.vue'; | ||||||
| import { defaultStore } from '@/store'; | import { defaultStore } from '@/store'; | ||||||
| 
 | 
 | ||||||
| withDefaults(defineProps<{ | withDefaults(defineProps<{ | ||||||
| 	manualShowing?: boolean; | 	manualShowing?: boolean | null; | ||||||
| 	src?: HTMLElement; | 	src?: HTMLElement; | ||||||
| 	showPinned?: boolean; | 	showPinned?: boolean; | ||||||
| 	asReactionPicker?: boolean; | 	asReactionPicker?: boolean; | ||||||
| }>(), { | }>(), { | ||||||
| 	manualShowing: false, | 	manualShowing: null, | ||||||
| 	showPinned: true, | 	showPinned: true, | ||||||
| 	asReactionPicker: false, | 	asReactionPicker: false, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const emit = defineEmits<{ | const emit = defineEmits<{ | ||||||
| 	(e: 'done', v: any): void; | 	(ev: 'done', v: any): void; | ||||||
| 	(e: 'close'): void; | 	(ev: 'close'): void; | ||||||
| 	(e: 'closed'): void; | 	(ev: 'closed'): void; | ||||||
| }>(); | }>(); | ||||||
| 
 | 
 | ||||||
| const modal = ref<InstanceType<typeof MkModal>>(); | const modal = ref<InstanceType<typeof MkModal>>(); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <div class="omfetrab" :class="['w' + width, 'h' + height, { big, asDrawer }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> | <div class="omfetrab" :class="['w' + width, 'h' + height, { big, asDrawer }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> | ||||||
| 	<input ref="search" v-model.trim="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.locale.search" @paste.stop="paste" @keyup.enter="done()"> | 	<input ref="search" v-model.trim="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" @paste.stop="paste" @keyup.enter="done()"> | ||||||
| 	<div ref="emojis" class="emojis"> | 	<div ref="emojis" class="emojis"> | ||||||
| 		<section class="result"> | 		<section class="result"> | ||||||
| 			<div v-if="searchResultCustom.length > 0"> | 			<div v-if="searchResultCustom.length > 0"> | ||||||
|  | @ -43,7 +43,7 @@ | ||||||
| 			</section> | 			</section> | ||||||
| 
 | 
 | ||||||
| 			<section> | 			<section> | ||||||
| 				<header class="_acrylic"><i class="far fa-clock fa-fw"></i> {{ i18n.locale.recentUsed }}</header> | 				<header class="_acrylic"><i class="far fa-clock fa-fw"></i> {{ i18n.ts.recentUsed }}</header> | ||||||
| 				<div> | 				<div> | ||||||
| 					<button v-for="emoji in recentlyUsedEmojis" | 					<button v-for="emoji in recentlyUsedEmojis" | ||||||
| 						:key="emoji" | 						:key="emoji" | ||||||
|  | @ -56,11 +56,11 @@ | ||||||
| 			</section> | 			</section> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div> | 		<div> | ||||||
| 			<header class="_acrylic">{{ i18n.locale.customEmojis }}</header> | 			<header class="_acrylic">{{ i18n.ts.customEmojis }}</header> | ||||||
| 			<XSection v-for="category in customEmojiCategories" :key="'custom:' + category" :initial-shown="false" :emojis="customEmojis.filter(e => e.category === category).map(e => ':' + e.name + ':')" @chosen="chosen">{{ category || i18n.locale.other }}</XSection> | 			<XSection v-for="category in customEmojiCategories" :key="'custom:' + category" :initial-shown="false" :emojis="customEmojis.filter(e => e.category === category).map(e => ':' + e.name + ':')" @chosen="chosen">{{ category || i18n.ts.other }}</XSection> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div> | 		<div> | ||||||
| 			<header class="_acrylic">{{ i18n.locale.emoji }}</header> | 			<header class="_acrylic">{{ i18n.ts.emoji }}</header> | ||||||
| 			<XSection v-for="category in categories" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)" @chosen="chosen">{{ category }}</XSection> | 			<XSection v-for="category in categories" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)" @chosen="chosen">{{ category }}</XSection> | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
|  | @ -280,7 +280,7 @@ function getKey(emoji: string | Misskey.entities.CustomEmoji | UnicodeEmojiDef): | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function chosen(emoji: any, ev?: MouseEvent) { | function chosen(emoji: any, ev?: MouseEvent) { | ||||||
| 	const el = ev && (ev.currentTarget || ev.target) as HTMLElement | null | undefined; | 	const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined; | ||||||
| 	if (el) { | 	if (el) { | ||||||
| 		const rect = el.getBoundingClientRect(); | 		const rect = el.getBoundingClientRect(); | ||||||
| 		const x = rect.left + (el.offsetWidth / 2); | 		const x = rect.left + (el.offsetWidth / 2); | ||||||
|  |  | ||||||
|  | @ -6,23 +6,23 @@ | ||||||
| > | > | ||||||
| 	<template v-if="!wait"> | 	<template v-if="!wait"> | ||||||
| 		<template v-if="hasPendingFollowRequestFromYou && user.isLocked"> | 		<template v-if="hasPendingFollowRequestFromYou && user.isLocked"> | ||||||
| 			<span v-if="full">{{ i18n.locale.followRequestPending }}</span><i class="fas fa-hourglass-half"></i> | 			<span v-if="full">{{ i18n.ts.followRequestPending }}</span><i class="fas fa-hourglass-half"></i> | ||||||
| 		</template> | 		</template> | ||||||
| 		<template v-else-if="hasPendingFollowRequestFromYou && !user.isLocked"> <!-- つまりリモートフォローの場合。 --> | 		<template v-else-if="hasPendingFollowRequestFromYou && !user.isLocked"> <!-- つまりリモートフォローの場合。 --> | ||||||
| 			<span v-if="full">{{ i18n.locale.processing }}</span><i class="fas fa-spinner fa-pulse"></i> | 			<span v-if="full">{{ i18n.ts.processing }}</span><i class="fas fa-spinner fa-pulse"></i> | ||||||
| 		</template> | 		</template> | ||||||
| 		<template v-else-if="isFollowing"> | 		<template v-else-if="isFollowing"> | ||||||
| 			<span v-if="full">{{ i18n.locale.unfollow }}</span><i class="fas fa-minus"></i> | 			<span v-if="full">{{ i18n.ts.unfollow }}</span><i class="fas fa-minus"></i> | ||||||
| 		</template> | 		</template> | ||||||
| 		<template v-else-if="!isFollowing && user.isLocked"> | 		<template v-else-if="!isFollowing && user.isLocked"> | ||||||
| 			<span v-if="full">{{ i18n.locale.followRequest }}</span><i class="fas fa-plus"></i> | 			<span v-if="full">{{ i18n.ts.followRequest }}</span><i class="fas fa-plus"></i> | ||||||
| 		</template> | 		</template> | ||||||
| 		<template v-else-if="!isFollowing && !user.isLocked"> | 		<template v-else-if="!isFollowing && !user.isLocked"> | ||||||
| 			<span v-if="full">{{ i18n.locale.follow }}</span><i class="fas fa-plus"></i> | 			<span v-if="full">{{ i18n.ts.follow }}</span><i class="fas fa-plus"></i> | ||||||
| 		</template> | 		</template> | ||||||
| 	</template> | 	</template> | ||||||
| 	<template v-else> | 	<template v-else> | ||||||
| 		<span v-if="full">{{ i18n.locale.processing }}</span><i class="fas fa-spinner fa-pulse fa-fw"></i> | 		<span v-if="full">{{ i18n.ts.processing }}</span><i class="fas fa-spinner fa-pulse fa-fw"></i> | ||||||
| 	</template> | 	</template> | ||||||
| </button> | </button> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  | @ -5,28 +5,28 @@ | ||||||
| 	@close="dialog.close()" | 	@close="dialog.close()" | ||||||
| 	@closed="emit('closed')" | 	@closed="emit('closed')" | ||||||
| > | > | ||||||
| 	<template #header>{{ i18n.locale.forgotPassword }}</template> | 	<template #header>{{ i18n.ts.forgotPassword }}</template> | ||||||
| 
 | 
 | ||||||
| 	<form v-if="instance.enableEmail" class="bafeceda" @submit.prevent="onSubmit"> | 	<form v-if="instance.enableEmail" class="bafeceda" @submit.prevent="onSubmit"> | ||||||
| 		<div class="main _formRoot"> | 		<div class="main _formRoot"> | ||||||
| 			<MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required> | 			<MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required> | ||||||
| 				<template #label>{{ i18n.locale.username }}</template> | 				<template #label>{{ i18n.ts.username }}</template> | ||||||
| 				<template #prefix>@</template> | 				<template #prefix>@</template> | ||||||
| 			</MkInput> | 			</MkInput> | ||||||
| 
 | 
 | ||||||
| 			<MkInput v-model="email" class="_formBlock" type="email" spellcheck="false" required> | 			<MkInput v-model="email" class="_formBlock" type="email" spellcheck="false" required> | ||||||
| 				<template #label>{{ i18n.locale.emailAddress }}</template> | 				<template #label>{{ i18n.ts.emailAddress }}</template> | ||||||
| 				<template #caption>{{ i18n.locale._forgotPassword.enterEmail }}</template> | 				<template #caption>{{ i18n.ts._forgotPassword.enterEmail }}</template> | ||||||
| 			</MkInput> | 			</MkInput> | ||||||
| 
 | 
 | ||||||
| 			<MkButton class="_formBlock" type="submit" :disabled="processing" primary style="margin: 0 auto;">{{ i18n.locale.send }}</MkButton> | 			<MkButton class="_formBlock" type="submit" :disabled="processing" primary style="margin: 0 auto;">{{ i18n.ts.send }}</MkButton> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="sub"> | 		<div class="sub"> | ||||||
| 			<MkA to="/about" class="_link">{{ i18n.locale._forgotPassword.ifNoEmail }}</MkA> | 			<MkA to="/about" class="_link">{{ i18n.ts._forgotPassword.ifNoEmail }}</MkA> | ||||||
| 		</div> | 		</div> | ||||||
| 	</form> | 	</form> | ||||||
| 	<div v-else class="bafecedb"> | 	<div v-else class="bafecedb"> | ||||||
| 		{{ i18n.locale._forgotPassword.contactAdmin }} | 		{{ i18n.ts._forgotPassword.contactAdmin }} | ||||||
| 	</div> | 	</div> | ||||||
| </XModalWindow> | </XModalWindow> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  | @ -43,31 +43,31 @@ function onContextmenu(ev) { | ||||||
| 		text: props.to, | 		text: props.to, | ||||||
| 	}, { | 	}, { | ||||||
| 		icon: 'fas fa-window-maximize', | 		icon: 'fas fa-window-maximize', | ||||||
| 		text: i18n.locale.openInWindow, | 		text: i18n.ts.openInWindow, | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			os.pageWindow(props.to); | 			os.pageWindow(props.to); | ||||||
| 		} | 		} | ||||||
| 	}, sideViewHook ? { | 	}, sideViewHook ? { | ||||||
| 		icon: 'fas fa-columns', | 		icon: 'fas fa-columns', | ||||||
| 		text: i18n.locale.openInSideView, | 		text: i18n.ts.openInSideView, | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			sideViewHook(props.to); | 			sideViewHook(props.to); | ||||||
| 		} | 		} | ||||||
| 	} : undefined, { | 	} : undefined, { | ||||||
| 		icon: 'fas fa-expand-alt', | 		icon: 'fas fa-expand-alt', | ||||||
| 		text: i18n.locale.showInPage, | 		text: i18n.ts.showInPage, | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			router.push(props.to); | 			router.push(props.to); | ||||||
| 		} | 		} | ||||||
| 	}, null, { | 	}, null, { | ||||||
| 		icon: 'fas fa-external-link-alt', | 		icon: 'fas fa-external-link-alt', | ||||||
| 		text: i18n.locale.openInNewTab, | 		text: i18n.ts.openInNewTab, | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			window.open(props.to, '_blank'); | 			window.open(props.to, '_blank'); | ||||||
| 		} | 		} | ||||||
| 	}, { | 	}, { | ||||||
| 		icon: 'fas fa-link', | 		icon: 'fas fa-link', | ||||||
| 		text: i18n.locale.copyLink, | 		text: i18n.ts.copyLink, | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			copyToClipboard(`${url}${props.to}`); | 			copyToClipboard(`${url}${props.to}`); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -104,7 +104,7 @@ export default defineComponent({ | ||||||
| 			if (props.info.share) { | 			if (props.info.share) { | ||||||
| 				if (menu.length > 0) menu.push(null); | 				if (menu.length > 0) menu.push(null); | ||||||
| 				menu.push({ | 				menu.push({ | ||||||
| 					text: i18n.locale.share, | 					text: i18n.ts.share, | ||||||
| 					icon: 'fas fa-share-alt', | 					icon: 'fas fa-share-alt', | ||||||
| 					action: share | 					action: share | ||||||
| 				}); | 				}); | ||||||
|  | @ -113,7 +113,7 @@ export default defineComponent({ | ||||||
| 				if (menu.length > 0) menu.push(null); | 				if (menu.length > 0) menu.push(null); | ||||||
| 				menu = menu.concat(props.menu); | 				menu = menu.concat(props.menu); | ||||||
| 			} | 			} | ||||||
| 			popupMenu(menu, ev.currentTarget || ev.target); | 			popupMenu(menu, ev.currentTarget ?? ev.target); | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		const showTabsPopup = (ev: MouseEvent) => { | 		const showTabsPopup = (ev: MouseEvent) => { | ||||||
|  | @ -126,7 +126,7 @@ export default defineComponent({ | ||||||
| 				icon: tab.icon, | 				icon: tab.icon, | ||||||
| 				action: tab.onClick, | 				action: tab.onClick, | ||||||
| 			})); | 			})); | ||||||
| 			popupMenu(menu, ev.currentTarget || ev.target); | 			popupMenu(menu, ev.currentTarget ?? ev.target); | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		const preventDrag = (ev: TouchEvent) => { | 		const preventDrag = (ev: TouchEvent) => { | ||||||
|  |  | ||||||
|  | @ -24,16 +24,16 @@ let now = $ref(new Date()); | ||||||
| const relative = $computed(() => { | const relative = $computed(() => { | ||||||
| 	const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/; | 	const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/; | ||||||
| 	return ( | 	return ( | ||||||
| 		ago >= 31536000 ? i18n.t('_ago.yearsAgo',   { n: (~~(ago / 31536000)).toString() }) : | 		ago >= 31536000 ? i18n.t('_ago.yearsAgo',   { n: Math.round(ago / 31536000).toString() }) : | ||||||
| 		ago >= 2592000  ? i18n.t('_ago.monthsAgo',  { n: (~~(ago / 2592000)).toString() }) : | 		ago >= 2592000  ? i18n.t('_ago.monthsAgo',  { n: Math.round(ago / 2592000).toString() }) : | ||||||
| 		ago >= 604800   ? i18n.t('_ago.weeksAgo',   { n: (~~(ago / 604800)).toString() }) : | 		ago >= 604800   ? i18n.t('_ago.weeksAgo',   { n: Math.round(ago / 604800).toString() }) : | ||||||
| 		ago >= 86400    ? i18n.t('_ago.daysAgo',    { n: (~~(ago / 86400)).toString() }) : | 		ago >= 86400    ? i18n.t('_ago.daysAgo',    { n: Math.round(ago / 86400).toString() }) : | ||||||
| 		ago >= 3600     ? i18n.t('_ago.hoursAgo',   { n: (~~(ago / 3600)).toString() }) : | 		ago >= 3600     ? i18n.t('_ago.hoursAgo',   { n: Math.round(ago / 3600).toString() }) : | ||||||
| 		ago >= 60       ? i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) : | 		ago >= 60       ? i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) : | ||||||
| 		ago >= 10       ? i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) : | 		ago >= 10       ? i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) : | ||||||
| 		ago >= -1       ? i18n.locale._ago.justNow : | 		ago >= -1       ? i18n.ts._ago.justNow : | ||||||
| 		ago <  -1       ? i18n.locale._ago.future : | 		ago <  -1       ? i18n.ts._ago.future : | ||||||
| 		i18n.locale._ago.unknown); | 		i18n.ts._ago.unknown); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| function tick() { | function tick() { | ||||||
|  |  | ||||||
|  | @ -250,7 +250,7 @@ function menu(viaKeyboard = false): void { | ||||||
| function showRenoteMenu(viaKeyboard = false): void { | function showRenoteMenu(viaKeyboard = false): void { | ||||||
| 	if (!isMyRenote) return; | 	if (!isMyRenote) return; | ||||||
| 	os.popupMenu([{ | 	os.popupMenu([{ | ||||||
| 		text: i18n.locale.unrenote, | 		text: i18n.ts.unrenote, | ||||||
| 		icon: 'fas fa-trash-alt', | 		icon: 'fas fa-trash-alt', | ||||||
| 		danger: true, | 		danger: true, | ||||||
| 		action: () => { | 		action: () => { | ||||||
|  |  | ||||||
|  | @ -10,13 +10,13 @@ | ||||||
| 	:class="{ renote: isRenote }" | 	:class="{ renote: isRenote }" | ||||||
| > | > | ||||||
| 	<MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" class="reply-to"/> | 	<MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" class="reply-to"/> | ||||||
| 	<div v-if="pinned" class="info"><i class="fas fa-thumbtack"></i> {{ i18n.locale.pinnedNote }}</div> | 	<div v-if="pinned" class="info"><i class="fas fa-thumbtack"></i> {{ i18n.ts.pinnedNote }}</div> | ||||||
| 	<div v-if="appearNote._prId_" class="info"><i class="fas fa-bullhorn"></i> {{ i18n.locale.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.locale.hideThisNote }} <i class="fas fa-times"></i></button></div> | 	<div v-if="appearNote._prId_" class="info"><i class="fas fa-bullhorn"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="fas fa-times"></i></button></div> | ||||||
| 	<div v-if="appearNote._featuredId_" class="info"><i class="fas fa-bolt"></i> {{ i18n.locale.featured }}</div> | 	<div v-if="appearNote._featuredId_" class="info"><i class="fas fa-bolt"></i> {{ i18n.ts.featured }}</div> | ||||||
| 	<div v-if="isRenote" class="renote"> | 	<div v-if="isRenote" class="renote"> | ||||||
| 		<MkAvatar class="avatar" :user="note.user"/> | 		<MkAvatar class="avatar" :user="note.user"/> | ||||||
| 		<i class="fas fa-retweet"></i> | 		<i class="fas fa-retweet"></i> | ||||||
| 		<I18n :src="i18n.locale.renotedBy" tag="span"> | 		<I18n :src="i18n.ts.renotedBy" tag="span"> | ||||||
| 			<template #user> | 			<template #user> | ||||||
| 				<MkA v-user-preview="note.userId" class="name" :to="userPage(note.user)"> | 				<MkA v-user-preview="note.userId" class="name" :to="userPage(note.user)"> | ||||||
| 					<MkUserName :user="note.user"/> | 					<MkUserName :user="note.user"/> | ||||||
|  | @ -48,7 +48,7 @@ | ||||||
| 				</p> | 				</p> | ||||||
| 				<div v-show="appearNote.cw == null || showContent" class="content" :class="{ collapsed }"> | 				<div v-show="appearNote.cw == null || showContent" class="content" :class="{ collapsed }"> | ||||||
| 					<div class="text"> | 					<div class="text"> | ||||||
| 						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.locale.private }})</span> | 						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> | ||||||
| 						<MkA v-if="appearNote.replyId" class="reply" :to="`/notes/${appearNote.replyId}`"><i class="fas fa-reply"></i></MkA> | 						<MkA v-if="appearNote.replyId" class="reply" :to="`/notes/${appearNote.replyId}`"><i class="fas fa-reply"></i></MkA> | ||||||
| 						<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> | 						<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> | ||||||
| 						<a v-if="appearNote.renote != null" class="rp">RN:</a> | 						<a v-if="appearNote.renote != null" class="rp">RN:</a> | ||||||
|  | @ -67,7 +67,7 @@ | ||||||
| 					<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" class="url-preview"/> | 					<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" class="url-preview"/> | ||||||
| 					<div v-if="appearNote.renote" class="renote"><XNoteSimple :note="appearNote.renote"/></div> | 					<div v-if="appearNote.renote" class="renote"><XNoteSimple :note="appearNote.renote"/></div> | ||||||
| 					<button v-if="collapsed" class="fade _button" @click="collapsed = false"> | 					<button v-if="collapsed" class="fade _button" @click="collapsed = false"> | ||||||
| 						<span>{{ i18n.locale.showMore }}</span> | 						<span>{{ i18n.ts.showMore }}</span> | ||||||
| 					</button> | 					</button> | ||||||
| 				</div> | 				</div> | ||||||
| 				<MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><i class="fas fa-satellite-dish"></i> {{ appearNote.channel.name }}</MkA> | 				<MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><i class="fas fa-satellite-dish"></i> {{ appearNote.channel.name }}</MkA> | ||||||
|  | @ -94,7 +94,7 @@ | ||||||
| 	</article> | 	</article> | ||||||
| </div> | </div> | ||||||
| <div v-else class="muted" @click="muted = false"> | <div v-else class="muted" @click="muted = false"> | ||||||
| 	<I18n :src="i18n.locale.userSaysSomething" tag="small"> | 	<I18n :src="i18n.ts.userSaysSomething" tag="small"> | ||||||
| 		<template #name> | 		<template #name> | ||||||
| 			<MkA v-user-preview="appearNote.userId" class="name" :to="userPage(appearNote.user)"> | 			<MkA v-user-preview="appearNote.userId" class="name" :to="userPage(appearNote.user)"> | ||||||
| 				<MkUserName :user="appearNote.user"/> | 				<MkUserName :user="appearNote.user"/> | ||||||
|  | @ -238,7 +238,7 @@ function menu(viaKeyboard = false): void { | ||||||
| function showRenoteMenu(viaKeyboard = false): void { | function showRenoteMenu(viaKeyboard = false): void { | ||||||
| 	if (!isMyRenote) return; | 	if (!isMyRenote) return; | ||||||
| 	os.popupMenu([{ | 	os.popupMenu([{ | ||||||
| 		text: i18n.locale.unrenote, | 		text: i18n.ts.unrenote, | ||||||
| 		icon: 'fas fa-trash-alt', | 		icon: 'fas fa-trash-alt', | ||||||
| 		danger: true, | 		danger: true, | ||||||
| 		action: () => { | 		action: () => { | ||||||
|  |  | ||||||
|  | @ -160,7 +160,7 @@ export default defineComponent({ | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					copyToClipboard(this.url); | 					copyToClipboard(this.url); | ||||||
| 				} | 				} | ||||||
| 			}], ev.currentTarget || ev.target); | 			}], ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		back() { | 		back() { | ||||||
|  |  | ||||||
|  | @ -127,7 +127,7 @@ export default defineComponent({ | ||||||
| 				text: this.$ts.attachCancel, | 				text: this.$ts.attachCancel, | ||||||
| 				icon: 'fas fa-times-circle', | 				icon: 'fas fa-times-circle', | ||||||
| 				action: () => { this.detachMedia(file.id) } | 				action: () => { this.detachMedia(file.id) } | ||||||
| 			}], ev.currentTarget || ev.target).then(() => this.menu = null); | 			}], ev.currentTarget ?? ev.target).then(() => this.menu = null); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -8,28 +8,28 @@ | ||||||
| > | > | ||||||
| 	<header> | 	<header> | ||||||
| 		<button v-if="!fixed" class="cancel _button" @click="cancel"><i class="fas fa-times"></i></button> | 		<button v-if="!fixed" class="cancel _button" @click="cancel"><i class="fas fa-times"></i></button> | ||||||
| 		<button v-click-anime v-tooltip="i18n.locale.switchAccount" class="account _button" @click="openAccountMenu"> | 		<button v-click-anime v-tooltip="i18n.ts.switchAccount" class="account _button" @click="openAccountMenu"> | ||||||
| 			<MkAvatar :user="postAccount ?? $i" class="avatar"/> | 			<MkAvatar :user="postAccount ?? $i" class="avatar"/> | ||||||
| 		</button> | 		</button> | ||||||
| 		<div> | 		<div> | ||||||
| 			<span class="text-count" :class="{ over: textLength > maxTextLength }">{{ maxTextLength - textLength }}</span> | 			<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> | 			<span v-if="localOnly" class="local-only"><i class="fas fa-biohazard"></i></span> | ||||||
| 			<button ref="visibilityButton" v-tooltip="i18n.locale.visibility" class="_button visibility" :disabled="channel != null" @click="setVisibility"> | 			<button ref="visibilityButton" v-tooltip="i18n.ts.visibility" class="_button visibility" :disabled="channel != null" @click="setVisibility"> | ||||||
| 				<span v-if="visibility === 'public'"><i class="fas fa-globe"></i></span> | 				<span v-if="visibility === 'public'"><i class="fas fa-globe"></i></span> | ||||||
| 				<span v-if="visibility === 'home'"><i class="fas fa-home"></i></span> | 				<span v-if="visibility === 'home'"><i class="fas fa-home"></i></span> | ||||||
| 				<span v-if="visibility === 'followers'"><i class="fas fa-unlock"></i></span> | 				<span v-if="visibility === 'followers'"><i class="fas fa-unlock"></i></span> | ||||||
| 				<span v-if="visibility === 'specified'"><i class="fas fa-envelope"></i></span> | 				<span v-if="visibility === 'specified'"><i class="fas fa-envelope"></i></span> | ||||||
| 			</button> | 			</button> | ||||||
| 			<button v-tooltip="i18n.locale.previewNoteText" class="_button preview" :class="{ active: showPreview }" @click="showPreview = !showPreview"><i class="fas fa-file-code"></i></button> | 			<button v-tooltip="i18n.ts.previewNoteText" class="_button preview" :class="{ active: showPreview }" @click="showPreview = !showPreview"><i class="fas fa-file-code"></i></button> | ||||||
| 			<button class="submit _buttonGradate" :disabled="!canPost" data-cy-open-post-form-submit @click="post">{{ submitText }}<i :class="reply ? 'fas fa-reply' : renote ? 'fas fa-quote-right' : 'fas fa-paper-plane'"></i></button> | 			<button class="submit _buttonGradate" :disabled="!canPost" data-cy-open-post-form-submit @click="post">{{ submitText }}<i :class="reply ? 'fas fa-reply' : renote ? 'fas fa-quote-right' : 'fas fa-paper-plane'"></i></button> | ||||||
| 		</div> | 		</div> | ||||||
| 	</header> | 	</header> | ||||||
| 	<div class="form" :class="{ fixed }"> | 	<div class="form" :class="{ fixed }"> | ||||||
| 		<XNoteSimple v-if="reply" class="preview" :note="reply"/> | 		<XNoteSimple v-if="reply" class="preview" :note="reply"/> | ||||||
| 		<XNoteSimple v-if="renote" class="preview" :note="renote"/> | 		<XNoteSimple v-if="renote" class="preview" :note="renote"/> | ||||||
| 		<div v-if="quoteId" class="with-quote"><i class="fas fa-quote-left"></i> {{ i18n.locale.quoteAttached }}<button @click="quoteId = null"><i class="fas fa-times"></i></button></div> | 		<div v-if="quoteId" class="with-quote"><i class="fas fa-quote-left"></i> {{ i18n.ts.quoteAttached }}<button @click="quoteId = null"><i class="fas fa-times"></i></button></div> | ||||||
| 		<div v-if="visibility === 'specified'" class="to-specified"> | 		<div v-if="visibility === 'specified'" class="to-specified"> | ||||||
| 			<span style="margin-right: 8px;">{{ i18n.locale.recipient }}</span> | 			<span style="margin-right: 8px;">{{ i18n.ts.recipient }}</span> | ||||||
| 			<div class="visibleUsers"> | 			<div class="visibleUsers"> | ||||||
| 				<span v-for="u in visibleUsers" :key="u.id"> | 				<span v-for="u in visibleUsers" :key="u.id"> | ||||||
| 					<MkAcct :user="u"/> | 					<MkAcct :user="u"/> | ||||||
|  | @ -38,21 +38,21 @@ | ||||||
| 				<button class="_buttonPrimary" @click="addVisibleUser"><i class="fas fa-plus fa-fw"></i></button> | 				<button class="_buttonPrimary" @click="addVisibleUser"><i class="fas fa-plus fa-fw"></i></button> | ||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
| 		<MkInfo v-if="hasNotSpecifiedMentions" warn class="hasNotSpecifiedMentions">{{ i18n.locale.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.locale.add }}</button></MkInfo> | 		<MkInfo v-if="hasNotSpecifiedMentions" warn class="hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo> | ||||||
| 		<input v-show="useCw" ref="cwInputEl" v-model="cw" class="cw" :placeholder="i18n.locale.annotation" @keydown="onKeydown"> | 		<input v-show="useCw" ref="cwInputEl" v-model="cw" class="cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown"> | ||||||
| 		<textarea ref="textareaEl" v-model="text" class="text" :class="{ withCw: useCw }" :disabled="posting" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/> | 		<textarea ref="textareaEl" v-model="text" class="text" :class="{ withCw: useCw }" :disabled="posting" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/> | ||||||
| 		<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" class="hashtags" :placeholder="i18n.locale.hashtags" list="hashtags"> | 		<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" class="hashtags" :placeholder="i18n.ts.hashtags" list="hashtags"> | ||||||
| 		<XPostFormAttaches class="attaches" :files="files" @updated="updateFiles" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/> | 		<XPostFormAttaches class="attaches" :files="files" @updated="updateFiles" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/> | ||||||
| 		<XPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/> | 		<XPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/> | ||||||
| 		<XNotePreview v-if="showPreview" class="preview" :text="text"/> | 		<XNotePreview v-if="showPreview" class="preview" :text="text"/> | ||||||
| 		<footer> | 		<footer> | ||||||
| 			<button v-tooltip="i18n.locale.attachFile" class="_button" @click="chooseFileFrom"><i class="fas fa-photo-video"></i></button> | 			<button v-tooltip="i18n.ts.attachFile" class="_button" @click="chooseFileFrom"><i class="fas fa-photo-video"></i></button> | ||||||
| 			<button v-tooltip="i18n.locale.poll" class="_button" :class="{ active: poll }" @click="togglePoll"><i class="fas fa-poll-h"></i></button> | 			<button v-tooltip="i18n.ts.poll" class="_button" :class="{ active: poll }" @click="togglePoll"><i class="fas fa-poll-h"></i></button> | ||||||
| 			<button v-tooltip="i18n.locale.useCw" class="_button" :class="{ active: useCw }" @click="useCw = !useCw"><i class="fas fa-eye-slash"></i></button> | 			<button v-tooltip="i18n.ts.useCw" class="_button" :class="{ active: useCw }" @click="useCw = !useCw"><i class="fas fa-eye-slash"></i></button> | ||||||
| 			<button v-tooltip="i18n.locale.mention" class="_button" @click="insertMention"><i class="fas fa-at"></i></button> | 			<button v-tooltip="i18n.ts.mention" class="_button" @click="insertMention"><i class="fas fa-at"></i></button> | ||||||
| 			<button v-tooltip="i18n.locale.hashtags" class="_button" :class="{ active: withHashtags }" @click="withHashtags = !withHashtags"><i class="fas fa-hashtag"></i></button> | 			<button v-tooltip="i18n.ts.hashtags" class="_button" :class="{ active: withHashtags }" @click="withHashtags = !withHashtags"><i class="fas fa-hashtag"></i></button> | ||||||
| 			<button v-tooltip="i18n.locale.emoji" class="_button" @click="insertEmoji"><i class="fas fa-laugh-squint"></i></button> | 			<button v-tooltip="i18n.ts.emoji" class="_button" @click="insertEmoji"><i class="fas fa-laugh-squint"></i></button> | ||||||
| 			<button v-if="postFormActions.length > 0" v-tooltip="i18n.locale.plugin" class="_button" @click="showActions"><i class="fas fa-plug"></i></button> | 			<button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugin" class="_button" @click="showActions"><i class="fas fa-plug"></i></button> | ||||||
| 		</footer> | 		</footer> | ||||||
| 		<datalist id="hashtags"> | 		<datalist id="hashtags"> | ||||||
| 			<option v-for="hashtag in recentHashtags" :key="hashtag" :value="hashtag"/> | 			<option v-for="hashtag in recentHashtags" :key="hashtag" :value="hashtag"/> | ||||||
|  | @ -165,19 +165,19 @@ const draftKey = $computed((): string => { | ||||||
| 
 | 
 | ||||||
| const placeholder = $computed((): string => { | const placeholder = $computed((): string => { | ||||||
| 	if (props.renote) { | 	if (props.renote) { | ||||||
| 		return i18n.locale._postForm.quotePlaceholder; | 		return i18n.ts._postForm.quotePlaceholder; | ||||||
| 	} else if (props.reply) { | 	} else if (props.reply) { | ||||||
| 		return i18n.locale._postForm.replyPlaceholder; | 		return i18n.ts._postForm.replyPlaceholder; | ||||||
| 	} else if (props.channel) { | 	} else if (props.channel) { | ||||||
| 		return i18n.locale._postForm.channelPlaceholder; | 		return i18n.ts._postForm.channelPlaceholder; | ||||||
| 	} else { | 	} else { | ||||||
| 		const xs = [ | 		const xs = [ | ||||||
| 			i18n.locale._postForm._placeholders.a, | 			i18n.ts._postForm._placeholders.a, | ||||||
| 			i18n.locale._postForm._placeholders.b, | 			i18n.ts._postForm._placeholders.b, | ||||||
| 			i18n.locale._postForm._placeholders.c, | 			i18n.ts._postForm._placeholders.c, | ||||||
| 			i18n.locale._postForm._placeholders.d, | 			i18n.ts._postForm._placeholders.d, | ||||||
| 			i18n.locale._postForm._placeholders.e, | 			i18n.ts._postForm._placeholders.e, | ||||||
| 			i18n.locale._postForm._placeholders.f | 			i18n.ts._postForm._placeholders.f | ||||||
| 		]; | 		]; | ||||||
| 		return xs[Math.floor(Math.random() * xs.length)]; | 		return xs[Math.floor(Math.random() * xs.length)]; | ||||||
| 	} | 	} | ||||||
|  | @ -185,10 +185,10 @@ const placeholder = $computed((): string => { | ||||||
| 
 | 
 | ||||||
| const submitText = $computed((): string => { | const submitText = $computed((): string => { | ||||||
| 	return props.renote | 	return props.renote | ||||||
| 		? i18n.locale.quote | 		? i18n.ts.quote | ||||||
| 		: props.reply | 		: props.reply | ||||||
| 			? i18n.locale.reply | 			? i18n.ts.reply | ||||||
| 			: i18n.locale.note; | 			: i18n.ts.note; | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const textLength = $computed((): number => { | const textLength = $computed((): number => { | ||||||
|  | @ -342,7 +342,7 @@ function focus() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function chooseFileFrom(ev) { | function chooseFileFrom(ev) { | ||||||
| 	selectFiles(ev.currentTarget || ev.target, i18n.locale.attachFile).then(files_ => { | 	selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { | ||||||
| 		for (const file of files_) { | 		for (const file of files_) { | ||||||
| 			files.push(file); | 			files.push(file); | ||||||
| 		} | 		} | ||||||
|  | @ -447,7 +447,7 @@ async function onPaste(e: ClipboardEvent) { | ||||||
| 
 | 
 | ||||||
| 		os.confirm({ | 		os.confirm({ | ||||||
| 			type: 'info', | 			type: 'info', | ||||||
| 			text: i18n.locale.quoteQuestion, | 			text: i18n.ts.quoteQuestion, | ||||||
| 		}).then(({ canceled }) => { | 		}).then(({ canceled }) => { | ||||||
| 			if (canceled) { | 			if (canceled) { | ||||||
| 				insertTextAtCursor(textareaEl, paste); | 				insertTextAtCursor(textareaEl, paste); | ||||||
|  | @ -592,7 +592,7 @@ function insertMention() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function insertEmoji(ev: MouseEvent) { | async function insertEmoji(ev: MouseEvent) { | ||||||
| 	os.openEmojiPicker(ev.currentTarget || ev.target, {}, textareaEl); | 	os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function showActions(ev) { | function showActions(ev) { | ||||||
|  | @ -605,7 +605,7 @@ function showActions(ev) { | ||||||
| 				if (key === 'text') { text = value; } | 				if (key === 'text') { text = value; } | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 	})), ev.currentTarget || ev.target); | 	})), ev.currentTarget ?? ev.target); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| let postAccount = $ref<misskey.entities.UserDetailed | null>(null); | let postAccount = $ref<misskey.entities.UserDetailed | null>(null); | ||||||
|  |  | ||||||
|  | @ -59,7 +59,7 @@ export default defineComponent({ | ||||||
| 		const renote = (viaKeyboard = false) => { | 		const renote = (viaKeyboard = false) => { | ||||||
| 			pleaseLogin(); | 			pleaseLogin(); | ||||||
| 			os.popupMenu([{ | 			os.popupMenu([{ | ||||||
| 				text: i18n.locale.renote, | 				text: i18n.ts.renote, | ||||||
| 				icon: 'fas fa-retweet', | 				icon: 'fas fa-retweet', | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					os.api('notes/create', { | 					os.api('notes/create', { | ||||||
|  | @ -67,7 +67,7 @@ export default defineComponent({ | ||||||
| 					}); | 					}); | ||||||
| 				} | 				} | ||||||
| 			}, { | 			}, { | ||||||
| 				text: i18n.locale.quote, | 				text: i18n.ts.quote, | ||||||
| 				icon: 'fas fa-quote-right', | 				icon: 'fas fa-quote-right', | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					os.post({ | 					os.post({ | ||||||
|  |  | ||||||
|  | @ -109,7 +109,7 @@ export default defineComponent({ | ||||||
| 				text: 'Delete some bananas', | 				text: 'Delete some bananas', | ||||||
| 				danger: true, | 				danger: true, | ||||||
| 				action: () => {}, | 				action: () => {}, | ||||||
| 			}], ev.currentTarget || ev.target); | 			}], ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <template> | <template> | ||||||
| <transition :name="$store.state.animation ? (type === 'drawer') ? 'modal-drawer' : (type === 'popup') ? 'modal-popup' : 'modal' : ''" :duration="$store.state.animation ? 200 : 0" appear @after-leave="$emit('closed')" @enter="$emit('opening')" @after-enter="childRendered"> | <transition :name="$store.state.animation ? (type === 'drawer') ? 'modal-drawer' : (type === 'popup') ? 'modal-popup' : 'modal' : ''" :duration="$store.state.animation ? 200 : 0" appear @after-leave="emit('closed')" @enter="emit('opening')" @after-enter="childRendered"> | ||||||
| 	<div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" class="qzhlnise" :class="{ drawer: type === 'drawer', dialog: type === 'dialog' || type === 'dialog:top', popup: type === 'popup' }" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }"> | 	<div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" class="qzhlnise" :class="{ drawer: type === 'drawer', dialog: type === 'dialog' || type === 'dialog:top', popup: type === 'popup' }" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }"> | ||||||
| 		<div class="bg _modalBg" :class="{ transparent: transparentBg && (type === 'popup') }" :style="{ zIndex }" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div> | 		<div class="bg _modalBg" :class="{ transparent: transparentBg && (type === 'popup') }" :style="{ zIndex }" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div> | ||||||
| 		<div ref="content" class="content" :class="{ fixed, top: type === 'dialog:top' }" :style="{ zIndex }" @click.self="onBgClick"> | 		<div ref="content" class="content" :class="{ fixed, top: type === 'dialog:top' }" :style="{ zIndex }" @click.self="onBgClick"> | ||||||
|  | @ -9,8 +9,8 @@ | ||||||
| </transition> | </transition> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts" setup> | ||||||
| import { defineComponent, nextTick, onMounted, computed, PropType, ref, watch } from 'vue'; | import { nextTick, onMounted, computed, ref, watch, provide } from 'vue'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
| import { isTouchUsing } from '@/scripts/touch'; | import { isTouchUsing } from '@/scripts/touch'; | ||||||
| import { defaultStore } from '@/store'; | import { defaultStore } from '@/store'; | ||||||
|  | @ -25,234 +25,206 @@ function getFixedContainer(el: Element | null): Element | null { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | type ModalTypes = 'popup' | 'dialog' | 'dialog:top' | 'drawer'; | ||||||
| 	provide: { |  | ||||||
| 		modal: true |  | ||||||
| 	}, |  | ||||||
| 
 | 
 | ||||||
| 	props: { | const props = withDefaults(defineProps<{ | ||||||
| 		manualShowing: { | 	manualShowing?: boolean | null; | ||||||
| 			type: Boolean, | 	srcCenter?: boolean; | ||||||
| 			required: false, | 	src?: HTMLElement; | ||||||
| 			default: null, | 	preferType?: ModalTypes | 'auto'; | ||||||
| 		}, | 	zPriority?: 'low' | 'middle' | 'high'; | ||||||
| 		srcCenter: { | 	noOverlap?: boolean; | ||||||
| 			type: Boolean, | 	transparentBg?: boolean; | ||||||
| 			required: false | }>(), { | ||||||
| 		}, | 	manualShowing: null, | ||||||
| 		src: { | 	src: null, | ||||||
| 			type: Object as PropType<HTMLElement>, | 	preferType: 'auto', | ||||||
| 			required: false, | 	zPriority: 'low', | ||||||
| 			default: null, | 	noOverlap: true, | ||||||
| 		}, | 	transparentBg: false, | ||||||
| 		preferType: { | }); | ||||||
| 			required: false, |  | ||||||
| 			type: String, |  | ||||||
| 			default: 'auto', |  | ||||||
| 		}, |  | ||||||
| 		zPriority: { |  | ||||||
| 			type: String as PropType<'low' | 'middle' | 'high'>, |  | ||||||
| 			required: false, |  | ||||||
| 			default: 'low', |  | ||||||
| 		}, |  | ||||||
| 		noOverlap: { |  | ||||||
| 			type: Boolean, |  | ||||||
| 			required: false, |  | ||||||
| 			default: true, |  | ||||||
| 		}, |  | ||||||
| 		transparentBg: { |  | ||||||
| 			type: Boolean, |  | ||||||
| 			required: false, |  | ||||||
| 			default: false, |  | ||||||
| 		}, |  | ||||||
| 	}, |  | ||||||
| 
 | 
 | ||||||
| 	emits: ['opening', 'click', 'esc', 'close', 'closed'], | const emit = defineEmits<{ | ||||||
|  | 	(ev: 'opening'): void; | ||||||
|  | 	(ev: 'click'): void; | ||||||
|  | 	(ev: 'esc'): void; | ||||||
|  | 	(ev: 'close'): void; | ||||||
|  | 	(ev: 'closed'): void; | ||||||
|  | }>(); | ||||||
| 
 | 
 | ||||||
| 	setup(props, context) { | provide('modal', true); | ||||||
| 		const maxHeight = ref<number>(); |  | ||||||
| 		const fixed = ref(false); |  | ||||||
| 		const transformOrigin = ref('center'); |  | ||||||
| 		const showing = ref(true); |  | ||||||
| 		const content = ref<HTMLElement>(); |  | ||||||
| 		const zIndex = os.claimZIndex(props.zPriority); |  | ||||||
| 		const type = computed(() => { |  | ||||||
| 			if (props.preferType === 'auto') { |  | ||||||
| 				if (!defaultStore.state.disableDrawer && isTouchUsing && window.innerWidth < 500 && window.innerHeight < 1000) { |  | ||||||
| 					return 'drawer'; |  | ||||||
| 				} else { |  | ||||||
| 					return props.src != null ? 'popup' : 'dialog'; |  | ||||||
| 				} |  | ||||||
| 			} else { |  | ||||||
| 				return props.preferType; |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 		 |  | ||||||
| 		let contentClicking = false; |  | ||||||
| 
 | 
 | ||||||
| 		const close = () => { | const maxHeight = ref<number>(); | ||||||
| 			// eslint-disable-next-line vue/no-mutating-props | const fixed = ref(false); | ||||||
| 			if (props.src) props.src.style.pointerEvents = 'auto'; | const transformOrigin = ref('center'); | ||||||
| 			showing.value = false; | const showing = ref(true); | ||||||
| 			context.emit('close'); | const content = ref<HTMLElement>(); | ||||||
| 		}; | const zIndex = os.claimZIndex(props.zPriority); | ||||||
|  | const type = computed(() => { | ||||||
|  | 	if (props.preferType === 'auto') { | ||||||
|  | 		if (!defaultStore.state.disableDrawer && isTouchUsing && window.innerWidth < 500 && window.innerHeight < 1000) { | ||||||
|  | 			return 'drawer'; | ||||||
|  | 		} else { | ||||||
|  | 			return props.src != null ? 'popup' : 'dialog'; | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		return props.preferType!; | ||||||
|  | 	} | ||||||
|  | }); | ||||||
| 
 | 
 | ||||||
| 		const onBgClick = () => { | let contentClicking = false; | ||||||
| 			if (contentClicking) return; |  | ||||||
| 			context.emit('click'); |  | ||||||
| 		}; |  | ||||||
| 
 | 
 | ||||||
| 		if (type.value === 'drawer') { | const close = () => { | ||||||
| 			maxHeight.value = window.innerHeight / 2; | 	// eslint-disable-next-line vue/no-mutating-props | ||||||
|  | 	if (props.src) props.src.style.pointerEvents = 'auto'; | ||||||
|  | 	showing.value = false; | ||||||
|  | 	emit('close'); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const onBgClick = () => { | ||||||
|  | 	if (contentClicking) return; | ||||||
|  | 	emit('click'); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | if (type.value === 'drawer') { | ||||||
|  | 	maxHeight.value = window.innerHeight / 2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const keymap = { | ||||||
|  | 	'esc': () => emit('esc'), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const MARGIN = 16; | ||||||
|  | 
 | ||||||
|  | const align = () => { | ||||||
|  | 	if (props.src == null) return; | ||||||
|  | 	if (type.value === 'drawer') return; | ||||||
|  | 
 | ||||||
|  | 	const popover = content.value!; | ||||||
|  | 
 | ||||||
|  | 	if (popover == null) return; | ||||||
|  | 
 | ||||||
|  | 	const rect = props.src.getBoundingClientRect(); | ||||||
|  | 	 | ||||||
|  | 	const width = popover.offsetWidth; | ||||||
|  | 	const height = popover.offsetHeight; | ||||||
|  | 
 | ||||||
|  | 	let left; | ||||||
|  | 	let top; | ||||||
|  | 
 | ||||||
|  | 	if (props.srcCenter) { | ||||||
|  | 		const x = rect.left + (fixed.value ? 0 : window.pageXOffset) + (props.src.offsetWidth / 2); | ||||||
|  | 		const y = rect.top + (fixed.value ? 0 : window.pageYOffset) + (props.src.offsetHeight / 2); | ||||||
|  | 		left = (x - (width / 2)); | ||||||
|  | 		top = (y - (height / 2)); | ||||||
|  | 	} else { | ||||||
|  | 		const x = rect.left + (fixed.value ? 0 : window.pageXOffset) + (props.src.offsetWidth / 2); | ||||||
|  | 		const y = rect.top + (fixed.value ? 0 : window.pageYOffset) + props.src.offsetHeight; | ||||||
|  | 		left = (x - (width / 2)); | ||||||
|  | 		top = y; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (fixed.value) { | ||||||
|  | 		// 画面から横にはみ出る場合 | ||||||
|  | 		if (left + width > window.innerWidth) { | ||||||
|  | 			left = window.innerWidth - width; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		const keymap = { | 		// 画面から縦にはみ出る場合 | ||||||
| 			'esc': () => context.emit('esc'), | 		if (top + height > (window.innerHeight - MARGIN)) { | ||||||
| 		}; | 			if (props.noOverlap) { | ||||||
| 
 | 				const underSpace = (window.innerHeight - MARGIN) - top; | ||||||
| 		const MARGIN = 16; | 				const upperSpace = (rect.top - MARGIN); | ||||||
| 
 | 				if (underSpace >= (upperSpace / 3)) { | ||||||
| 		const align = () => { | 					maxHeight.value =  underSpace; | ||||||
| 			if (props.src == null) return; | 				} else { | ||||||
| 			if (type.value === 'drawer') return; | 					maxHeight.value =  upperSpace; | ||||||
| 
 | 					top = (upperSpace + MARGIN) - height; | ||||||
| 			const popover = content.value!; |  | ||||||
| 
 |  | ||||||
| 			if (popover == null) return; |  | ||||||
| 
 |  | ||||||
| 			const rect = props.src.getBoundingClientRect(); |  | ||||||
| 			 |  | ||||||
| 			const width = popover.offsetWidth; |  | ||||||
| 			const height = popover.offsetHeight; |  | ||||||
| 
 |  | ||||||
| 			let left; |  | ||||||
| 			let top; |  | ||||||
| 
 |  | ||||||
| 			if (props.srcCenter) { |  | ||||||
| 				const x = rect.left + (fixed.value ? 0 : window.pageXOffset) + (props.src.offsetWidth / 2); |  | ||||||
| 				const y = rect.top + (fixed.value ? 0 : window.pageYOffset) + (props.src.offsetHeight / 2); |  | ||||||
| 				left = (x - (width / 2)); |  | ||||||
| 				top = (y - (height / 2)); |  | ||||||
| 			} else { |  | ||||||
| 				const x = rect.left + (fixed.value ? 0 : window.pageXOffset) + (props.src.offsetWidth / 2); |  | ||||||
| 				const y = rect.top + (fixed.value ? 0 : window.pageYOffset) + props.src.offsetHeight; |  | ||||||
| 				left = (x - (width / 2)); |  | ||||||
| 				top = y; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (fixed.value) { |  | ||||||
| 				// 画面から横にはみ出る場合 |  | ||||||
| 				if (left + width > window.innerWidth) { |  | ||||||
| 					left = window.innerWidth - width; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				// 画面から縦にはみ出る場合 |  | ||||||
| 				if (top + height > (window.innerHeight - MARGIN)) { |  | ||||||
| 					if (props.noOverlap) { |  | ||||||
| 						const underSpace = (window.innerHeight - MARGIN) - top; |  | ||||||
| 						const upperSpace = (rect.top - MARGIN); |  | ||||||
| 						if (underSpace >= (upperSpace / 3)) { |  | ||||||
| 							maxHeight.value =  underSpace; |  | ||||||
| 						} else { |  | ||||||
| 							maxHeight.value =  upperSpace; |  | ||||||
| 							top = (upperSpace + MARGIN) - height; |  | ||||||
| 						} |  | ||||||
| 					} else { |  | ||||||
| 						top = (window.innerHeight - MARGIN) - height; |  | ||||||
| 					} |  | ||||||
| 				} | 				} | ||||||
| 			} else { | 			} else { | ||||||
| 				// 画面から横にはみ出る場合 | 				top = (window.innerHeight - MARGIN) - height; | ||||||
| 				if (left + width - window.pageXOffset > window.innerWidth) { | 			} | ||||||
| 					left = window.innerWidth - width + window.pageXOffset - 1; | 		} | ||||||
|  | 	} else { | ||||||
|  | 		// 画面から横にはみ出る場合 | ||||||
|  | 		if (left + width - window.pageXOffset > window.innerWidth) { | ||||||
|  | 			left = window.innerWidth - width + window.pageXOffset - 1; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// 画面から縦にはみ出る場合 | ||||||
|  | 		if (top + height - window.pageYOffset > (window.innerHeight - MARGIN)) { | ||||||
|  | 			if (props.noOverlap) { | ||||||
|  | 				const underSpace = (window.innerHeight - MARGIN) - (top - window.pageYOffset); | ||||||
|  | 				const upperSpace = (rect.top - MARGIN); | ||||||
|  | 				if (underSpace >= (upperSpace / 3)) { | ||||||
|  | 					maxHeight.value =  underSpace; | ||||||
|  | 				} else { | ||||||
|  | 					maxHeight.value =  upperSpace; | ||||||
|  | 					top = window.pageYOffset + ((upperSpace + MARGIN) - height); | ||||||
| 				} | 				} | ||||||
| 
 |  | ||||||
| 				// 画面から縦にはみ出る場合 |  | ||||||
| 				if (top + height - window.pageYOffset > (window.innerHeight - MARGIN)) { |  | ||||||
| 					if (props.noOverlap) { |  | ||||||
| 						const underSpace = (window.innerHeight - MARGIN) - (top - window.pageYOffset); |  | ||||||
| 						const upperSpace = (rect.top - MARGIN); |  | ||||||
| 						if (underSpace >= (upperSpace / 3)) { |  | ||||||
| 							maxHeight.value =  underSpace; |  | ||||||
| 						} else { |  | ||||||
| 							maxHeight.value =  upperSpace; |  | ||||||
| 							top = window.pageYOffset + ((upperSpace + MARGIN) - height); |  | ||||||
| 						} |  | ||||||
| 					} else { |  | ||||||
| 						top = (window.innerHeight - MARGIN) - height + window.pageYOffset - 1; |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (top < 0) { |  | ||||||
| 				top = MARGIN; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (left < 0) { |  | ||||||
| 				left = 0; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (top > rect.top + (fixed.value ? 0 : window.pageYOffset)) { |  | ||||||
| 				transformOrigin.value = 'center top'; |  | ||||||
| 			} else if ((top + height) <= rect.top + (fixed.value ? 0 : window.pageYOffset)) { |  | ||||||
| 				transformOrigin.value = 'center bottom'; |  | ||||||
| 			} else { | 			} else { | ||||||
| 				transformOrigin.value = 'center'; | 				top = (window.innerHeight - MARGIN) - height + window.pageYOffset - 1; | ||||||
| 			} | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 			popover.style.left = left + 'px'; | 	if (top < 0) { | ||||||
| 			popover.style.top = top + 'px'; | 		top = MARGIN; | ||||||
| 		}; | 	} | ||||||
| 
 | 
 | ||||||
| 		const childRendered = () => { | 	if (left < 0) { | ||||||
| 			// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する | 		left = 0; | ||||||
| 			const el = content.value!.children[0]; | 	} | ||||||
| 			el.addEventListener('mousedown', e => { |  | ||||||
| 				contentClicking = true; |  | ||||||
| 				window.addEventListener('mouseup', e => { |  | ||||||
| 					// click イベントより先に mouseup イベントが発生するかもしれないのでちょっと待つ |  | ||||||
| 					window.setTimeout(() => { |  | ||||||
| 						contentClicking = false; |  | ||||||
| 					}, 100); |  | ||||||
| 				}, { passive: true, once: true }); |  | ||||||
| 			}, { passive: true }); |  | ||||||
| 		}; |  | ||||||
| 
 | 
 | ||||||
| 		onMounted(() => { | 	if (top > rect.top + (fixed.value ? 0 : window.pageYOffset)) { | ||||||
| 			watch(() => props.src, async () => { | 		transformOrigin.value = 'center top'; | ||||||
| 				if (props.src) { | 	} else if ((top + height) <= rect.top + (fixed.value ? 0 : window.pageYOffset)) { | ||||||
| 					// eslint-disable-next-line vue/no-mutating-props | 		transformOrigin.value = 'center bottom'; | ||||||
| 					props.src.style.pointerEvents = 'none'; | 	} else { | ||||||
| 				} | 		transformOrigin.value = 'center'; | ||||||
| 				fixed.value = (type.value === 'drawer') || (getFixedContainer(props.src) != null); | 	} | ||||||
| 
 | 
 | ||||||
| 				await nextTick() | 	popover.style.left = left + 'px'; | ||||||
| 				 | 	popover.style.top = top + 'px'; | ||||||
| 				align(); | }; | ||||||
| 			}, { immediate: true, }); |  | ||||||
| 
 | 
 | ||||||
| 			nextTick(() => { | const childRendered = () => { | ||||||
| 				const popover = content.value; | 	// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する | ||||||
| 				new ResizeObserver((entries, observer) => { | 	const el = content.value!.children[0]; | ||||||
| 					align(); | 	el.addEventListener('mousedown', ev => { | ||||||
| 				}).observe(popover!); | 		contentClicking = true; | ||||||
| 			}); | 		window.addEventListener('mouseup', ev => { | ||||||
| 		}); | 			// click イベントより先に mouseup イベントが発生するかもしれないのでちょっと待つ | ||||||
|  | 			window.setTimeout(() => { | ||||||
|  | 				contentClicking = false; | ||||||
|  | 			}, 100); | ||||||
|  | 		}, { passive: true, once: true }); | ||||||
|  | 	}, { passive: true }); | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| 		return { | onMounted(() => { | ||||||
| 			showing, | 	watch(() => props.src, async () => { | ||||||
| 			type, | 		if (props.src) { | ||||||
| 			fixed, | 			// eslint-disable-next-line vue/no-mutating-props | ||||||
| 			content, | 			props.src.style.pointerEvents = 'none'; | ||||||
| 			transformOrigin, | 		} | ||||||
| 			maxHeight, | 		fixed.value = (type.value === 'drawer') || (getFixedContainer(props.src) != null); | ||||||
| 			close, | 
 | ||||||
| 			zIndex, | 		await nextTick() | ||||||
| 			keymap, | 		 | ||||||
| 			onBgClick, | 		align(); | ||||||
| 			childRendered, | 	}, { immediate: true, }); | ||||||
| 		}; | 
 | ||||||
| 	}, | 	nextTick(() => { | ||||||
|  | 		const popover = content.value; | ||||||
|  | 		new ResizeObserver((entries, observer) => { | ||||||
|  | 			align(); | ||||||
|  | 		}).observe(popover!); | ||||||
|  | 	}); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | defineExpose({ | ||||||
|  | 	close, | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,10 +13,10 @@ const props = defineProps<{ | ||||||
| 
 | 
 | ||||||
| const text = $computed(() => { | const text = $computed(() => { | ||||||
| 	switch (props.user.onlineStatus) { | 	switch (props.user.onlineStatus) { | ||||||
| 		case 'online': return i18n.locale.online; | 		case 'online': return i18n.ts.online; | ||||||
| 		case 'active': return i18n.locale.active; | 		case 'active': return i18n.ts.active; | ||||||
| 		case 'offline': return i18n.locale.offline; | 		case 'offline': return i18n.ts.offline; | ||||||
| 		case 'unknown': return i18n.locale.unknown; | 		case 'unknown': return i18n.ts.unknown; | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -185,7 +185,7 @@ app.config.globalProperties = { | ||||||
| 	$store: defaultStore, | 	$store: defaultStore, | ||||||
| 	$instance: instance, | 	$instance: instance, | ||||||
| 	$t: i18n.t, | 	$t: i18n.t, | ||||||
| 	$ts: i18n.locale, | 	$ts: i18n.ts, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| app.use(router); | app.use(router); | ||||||
|  | @ -299,8 +299,8 @@ stream.on('_disconnected_', async () => { | ||||||
| 		reloadDialogShowing = true; | 		reloadDialogShowing = true; | ||||||
| 		const { canceled } = await confirm({ | 		const { canceled } = await confirm({ | ||||||
| 			type: 'warning', | 			type: 'warning', | ||||||
| 			title: i18n.locale.disconnectedFromServer, | 			title: i18n.ts.disconnectedFromServer, | ||||||
| 			text: i18n.locale.reloadConfirm, | 			text: i18n.ts.reloadConfirm, | ||||||
| 		}); | 		}); | ||||||
| 		reloadDialogShowing = false; | 		reloadDialogShowing = false; | ||||||
| 		if (!canceled) { | 		if (!canceled) { | ||||||
|  | @ -324,7 +324,7 @@ if ($i) { | ||||||
| 	if ($i.isDeleted) { | 	if ($i.isDeleted) { | ||||||
| 		alert({ | 		alert({ | ||||||
| 			type: 'warning', | 			type: 'warning', | ||||||
| 			text: i18n.locale.accountDeletionInProgress, | 			text: i18n.ts.accountDeletionInProgress, | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -73,12 +73,12 @@ export const menuDef = reactive({ | ||||||
| 				})), null, { | 				})), null, { | ||||||
| 					type: 'link', | 					type: 'link', | ||||||
| 					to: '/my/lists', | 					to: '/my/lists', | ||||||
| 					text: i18n.locale.manageLists, | 					text: i18n.ts.manageLists, | ||||||
| 					icon: 'fas fa-cog', | 					icon: 'fas fa-cog', | ||||||
| 				}]; | 				}]; | ||||||
| 				items.value = _items; | 				items.value = _items; | ||||||
| 			}); | 			}); | ||||||
| 			os.popupMenu(items, ev.currentTarget || ev.target); | 			os.popupMenu(items, ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 	groups: { | 	groups: { | ||||||
|  | @ -104,12 +104,12 @@ export const menuDef = reactive({ | ||||||
| 				})), null, { | 				})), null, { | ||||||
| 					type: 'link', | 					type: 'link', | ||||||
| 					to: '/my/antennas', | 					to: '/my/antennas', | ||||||
| 					text: i18n.locale.manageAntennas, | 					text: i18n.ts.manageAntennas, | ||||||
| 					icon: 'fas fa-cog', | 					icon: 'fas fa-cog', | ||||||
| 				}]; | 				}]; | ||||||
| 				items.value = _items; | 				items.value = _items; | ||||||
| 			}); | 			}); | ||||||
| 			os.popupMenu(items, ev.currentTarget || ev.target); | 			os.popupMenu(items, ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 	mentions: { | 	mentions: { | ||||||
|  | @ -173,34 +173,34 @@ export const menuDef = reactive({ | ||||||
| 		icon: 'fas fa-columns', | 		icon: 'fas fa-columns', | ||||||
| 		action: (ev) => { | 		action: (ev) => { | ||||||
| 			os.popupMenu([{ | 			os.popupMenu([{ | ||||||
| 				text: i18n.locale.default, | 				text: i18n.ts.default, | ||||||
| 				active: ui === 'default' || ui === null, | 				active: ui === 'default' || ui === null, | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					localStorage.setItem('ui', 'default'); | 					localStorage.setItem('ui', 'default'); | ||||||
| 					unisonReload(); | 					unisonReload(); | ||||||
| 				} | 				} | ||||||
| 			}, { | 			}, { | ||||||
| 				text: i18n.locale.deck, | 				text: i18n.ts.deck, | ||||||
| 				active: ui === 'deck', | 				active: ui === 'deck', | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					localStorage.setItem('ui', 'deck'); | 					localStorage.setItem('ui', 'deck'); | ||||||
| 					unisonReload(); | 					unisonReload(); | ||||||
| 				} | 				} | ||||||
| 			}, { | 			}, { | ||||||
| 				text: i18n.locale.classic, | 				text: i18n.ts.classic, | ||||||
| 				active: ui === 'classic', | 				active: ui === 'classic', | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					localStorage.setItem('ui', 'classic'); | 					localStorage.setItem('ui', 'classic'); | ||||||
| 					unisonReload(); | 					unisonReload(); | ||||||
| 				} | 				} | ||||||
| 			}, /*{ | 			}, /*{ | ||||||
| 				text: i18n.locale.desktop + ' (β)', | 				text: i18n.ts.desktop + ' (β)', | ||||||
| 				active: ui === 'desktop', | 				active: ui === 'desktop', | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					localStorage.setItem('ui', 'desktop'); | 					localStorage.setItem('ui', 'desktop'); | ||||||
| 					unisonReload(); | 					unisonReload(); | ||||||
| 				} | 				} | ||||||
| 			}*/], ev.currentTarget || ev.target); | 			}*/], ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -403,7 +403,7 @@ export async function selectDriveFolder(multiple: boolean) { | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function pickEmoji(src?: HTMLElement, opts) { | export async function pickEmoji(src: HTMLElement | null, opts) { | ||||||
| 	return new Promise((resolve, reject) => { | 	return new Promise((resolve, reject) => { | ||||||
| 		popup(import('@/components/emoji-picker-dialog.vue'), { | 		popup(import('@/components/emoji-picker-dialog.vue'), { | ||||||
| 			src, | 			src, | ||||||
|  |  | ||||||
|  | @ -3,15 +3,15 @@ | ||||||
| <transition :name="$store.state.animation ? 'zoom' : ''" appear> | <transition :name="$store.state.animation ? 'zoom' : ''" appear> | ||||||
| 	<div v-show="loaded" class="mjndxjch"> | 	<div v-show="loaded" class="mjndxjch"> | ||||||
| 		<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/> | 		<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/> | ||||||
| 		<p><b><i class="fas fa-exclamation-triangle"></i> {{ i18n.locale.pageLoadError }}</b></p> | 		<p><b><i class="fas fa-exclamation-triangle"></i> {{ i18n.ts.pageLoadError }}</b></p> | ||||||
| 		<p v-if="meta && (version === meta.version)">{{ i18n.locale.pageLoadErrorDescription }}</p> | 		<p v-if="meta && (version === meta.version)">{{ i18n.ts.pageLoadErrorDescription }}</p> | ||||||
| 		<p v-else-if="serverIsDead">{{ i18n.locale.serverIsDead }}</p> | 		<p v-else-if="serverIsDead">{{ i18n.ts.serverIsDead }}</p> | ||||||
| 		<template v-else> | 		<template v-else> | ||||||
| 			<p>{{ i18n.locale.newVersionOfClientAvailable }}</p> | 			<p>{{ i18n.ts.newVersionOfClientAvailable }}</p> | ||||||
| 			<p>{{ i18n.locale.youShouldUpgradeClient }}</p> | 			<p>{{ i18n.ts.youShouldUpgradeClient }}</p> | ||||||
| 			<MkButton class="button primary" @click="reload">{{ i18n.locale.reload }}</MkButton> | 			<MkButton class="button primary" @click="reload">{{ i18n.ts.reload }}</MkButton> | ||||||
| 		</template> | 		</template> | ||||||
| 		<p><MkA to="/docs/general/troubleshooting" class="_link">{{ i18n.locale.troubleshooting }}</MkA></p> | 		<p><MkA to="/docs/general/troubleshooting" class="_link">{{ i18n.ts.troubleshooting }}</MkA></p> | ||||||
| 		<p v-if="error" class="error">ERROR: {{ error }}</p> | 		<p v-if="error" class="error">ERROR: {{ error }}</p> | ||||||
| 	</div> | 	</div> | ||||||
| </transition> | </transition> | ||||||
|  | @ -54,7 +54,7 @@ function reload() { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.error, | 		title: i18n.ts.error, | ||||||
| 		icon: 'fas fa-exclamation-triangle', | 		icon: 'fas fa-exclamation-triangle', | ||||||
| 	}, | 	}, | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ | ||||||
| 				<span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :custom-emojis="$instance.emojis" :is-reaction="false" :normal="true" :no-style="true"/></span> | 				<span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :custom-emojis="$instance.emojis" :is-reaction="false" :normal="true" :no-style="true"/></span> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="_formBlock" style="text-align: center;"> | 			<div class="_formBlock" style="text-align: center;"> | ||||||
| 				{{ i18n.locale._aboutMisskey.about }}<br><a href="https://misskey-hub.net/docs/misskey.html" target="_blank" class="_link">{{ i18n.locale.learnMore }}</a> | 				{{ i18n.ts._aboutMisskey.about }}<br><a href="https://misskey-hub.net/docs/misskey.html" target="_blank" class="_link">{{ i18n.ts.learnMore }}</a> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="_formBlock" style="text-align: center;"> | 			<div class="_formBlock" style="text-align: center;"> | ||||||
| 				<MkButton primary rounded inline @click="iLoveMisskey">I <Mfm text="$[jelly ❤]"/> #Misskey</MkButton> | 				<MkButton primary rounded inline @click="iLoveMisskey">I <Mfm text="$[jelly ❤]"/> #Misskey</MkButton> | ||||||
|  | @ -19,23 +19,23 @@ | ||||||
| 				<div class="_formLinks"> | 				<div class="_formLinks"> | ||||||
| 					<FormLink to="https://github.com/misskey-dev/misskey" external> | 					<FormLink to="https://github.com/misskey-dev/misskey" external> | ||||||
| 						<template #icon><i class="fas fa-code"></i></template> | 						<template #icon><i class="fas fa-code"></i></template> | ||||||
| 						{{ i18n.locale._aboutMisskey.source }} | 						{{ i18n.ts._aboutMisskey.source }} | ||||||
| 						<template #suffix>GitHub</template> | 						<template #suffix>GitHub</template> | ||||||
| 					</FormLink> | 					</FormLink> | ||||||
| 					<FormLink to="https://crowdin.com/project/misskey" external> | 					<FormLink to="https://crowdin.com/project/misskey" external> | ||||||
| 						<template #icon><i class="fas fa-language"></i></template> | 						<template #icon><i class="fas fa-language"></i></template> | ||||||
| 						{{ i18n.locale._aboutMisskey.translation }} | 						{{ i18n.ts._aboutMisskey.translation }} | ||||||
| 						<template #suffix>Crowdin</template> | 						<template #suffix>Crowdin</template> | ||||||
| 					</FormLink> | 					</FormLink> | ||||||
| 					<FormLink to="https://www.patreon.com/syuilo" external> | 					<FormLink to="https://www.patreon.com/syuilo" external> | ||||||
| 						<template #icon><i class="fas fa-hand-holding-medical"></i></template> | 						<template #icon><i class="fas fa-hand-holding-medical"></i></template> | ||||||
| 						{{ i18n.locale._aboutMisskey.donate }} | 						{{ i18n.ts._aboutMisskey.donate }} | ||||||
| 						<template #suffix>Patreon</template> | 						<template #suffix>Patreon</template> | ||||||
| 					</FormLink> | 					</FormLink> | ||||||
| 				</div> | 				</div> | ||||||
| 			</FormSection> | 			</FormSection> | ||||||
| 			<FormSection> | 			<FormSection> | ||||||
| 				<template #label>{{ i18n.locale._aboutMisskey.contributors }}</template> | 				<template #label>{{ i18n.ts._aboutMisskey.contributors }}</template> | ||||||
| 				<div class="_formLinks"> | 				<div class="_formLinks"> | ||||||
| 					<FormLink to="https://github.com/syuilo" external>@syuilo</FormLink> | 					<FormLink to="https://github.com/syuilo" external>@syuilo</FormLink> | ||||||
| 					<FormLink to="https://github.com/AyaMorisawa" external>@AyaMorisawa</FormLink> | 					<FormLink to="https://github.com/AyaMorisawa" external>@AyaMorisawa</FormLink> | ||||||
|  | @ -47,12 +47,12 @@ | ||||||
| 					<FormLink to="https://github.com/u1-liquid" external>@u1-liquid</FormLink> | 					<FormLink to="https://github.com/u1-liquid" external>@u1-liquid</FormLink> | ||||||
| 					<FormLink to="https://github.com/marihachi" external>@marihachi</FormLink> | 					<FormLink to="https://github.com/marihachi" external>@marihachi</FormLink> | ||||||
| 				</div> | 				</div> | ||||||
| 				<template #caption><MkLink url="https://github.com/misskey-dev/misskey/graphs/contributors">{{ i18n.locale._aboutMisskey.allContributors }}</MkLink></template> | 				<template #caption><MkLink url="https://github.com/misskey-dev/misskey/graphs/contributors">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink></template> | ||||||
| 			</FormSection> | 			</FormSection> | ||||||
| 			<FormSection> | 			<FormSection> | ||||||
| 				<template #label><Mfm text="$[jelly ❤]"/> {{ i18n.locale._aboutMisskey.patrons }}</template> | 				<template #label><Mfm text="$[jelly ❤]"/> {{ i18n.ts._aboutMisskey.patrons }}</template> | ||||||
| 				<div v-for="patron in patrons" :key="patron">{{ patron }}</div> | 				<div v-for="patron in patrons" :key="patron">{{ patron }}</div> | ||||||
| 				<template #caption>{{ i18n.locale._aboutMisskey.morePatrons }}</template> | 				<template #caption>{{ i18n.ts._aboutMisskey.morePatrons }}</template> | ||||||
| 			</FormSection> | 			</FormSection> | ||||||
| 		</div> | 		</div> | ||||||
| 	</MkSpacer> | 	</MkSpacer> | ||||||
|  | @ -194,7 +194,7 @@ onBeforeUnmount(() => { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.aboutMisskey, | 		title: i18n.ts.aboutMisskey, | ||||||
| 		icon: null, | 		icon: null, | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -90,7 +90,7 @@ const initStats = () => os.api('stats', { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.instanceInfo, | 		title: i18n.ts.instanceInfo, | ||||||
| 		icon: 'fas fa-info-circle', | 		icon: 'fas fa-info-circle', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -118,7 +118,7 @@ const toggleSelect = (emoji) => { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const add = async (ev: MouseEvent) => { | const add = async (ev: MouseEvent) => { | ||||||
| 	const files = await selectFiles(ev.currentTarget || ev.target, null); | 	const files = await selectFiles(ev.currentTarget ?? ev.target, null); | ||||||
| 
 | 
 | ||||||
| 	const promise = Promise.all(files.map(file => os.api('admin/emoji/add', { | 	const promise = Promise.all(files.map(file => os.api('admin/emoji/add', { | ||||||
| 		fileId: file.id, | 		fileId: file.id, | ||||||
|  | @ -157,23 +157,23 @@ const remoteMenu = (emoji, ev: MouseEvent) => { | ||||||
| 		type: 'label', | 		type: 'label', | ||||||
| 		text: ':' + emoji.name + ':', | 		text: ':' + emoji.name + ':', | ||||||
| 	}, { | 	}, { | ||||||
| 		text: i18n.locale.import, | 		text: i18n.ts.import, | ||||||
| 		icon: 'fas fa-plus', | 		icon: 'fas fa-plus', | ||||||
| 		action: () => { im(emoji) } | 		action: () => { im(emoji) } | ||||||
| 	}], ev.currentTarget || ev.target); | 	}], ev.currentTarget ?? ev.target); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const menu = (ev: MouseEvent) => { | const menu = (ev: MouseEvent) => { | ||||||
| 	os.popupMenu([{ | 	os.popupMenu([{ | ||||||
| 		icon: 'fas fa-download', | 		icon: 'fas fa-download', | ||||||
| 		text: i18n.locale.export, | 		text: i18n.ts.export, | ||||||
| 		action: async () => { | 		action: async () => { | ||||||
| 			os.api('export-custom-emojis', { | 			os.api('export-custom-emojis', { | ||||||
| 			}) | 			}) | ||||||
| 			.then(() => { | 			.then(() => { | ||||||
| 				os.alert({ | 				os.alert({ | ||||||
| 					type: 'info', | 					type: 'info', | ||||||
| 					text: i18n.locale.exportRequested, | 					text: i18n.ts.exportRequested, | ||||||
| 				}); | 				}); | ||||||
| 			}).catch((e) => { | 			}).catch((e) => { | ||||||
| 				os.alert({ | 				os.alert({ | ||||||
|  | @ -184,16 +184,16 @@ const menu = (ev: MouseEvent) => { | ||||||
| 		} | 		} | ||||||
| 	}, { | 	}, { | ||||||
| 		icon: 'fas fa-upload', | 		icon: 'fas fa-upload', | ||||||
| 		text: i18n.locale.import, | 		text: i18n.ts.import, | ||||||
| 		action: async () => { | 		action: async () => { | ||||||
| 			const file = await selectFile(ev.currentTarget || ev.target); | 			const file = await selectFile(ev.currentTarget ?? ev.target); | ||||||
| 			os.api('admin/emoji/import-zip', { | 			os.api('admin/emoji/import-zip', { | ||||||
| 				fileId: file.id, | 				fileId: file.id, | ||||||
| 			}) | 			}) | ||||||
| 			.then(() => { | 			.then(() => { | ||||||
| 				os.alert({ | 				os.alert({ | ||||||
| 					type: 'info', | 					type: 'info', | ||||||
| 					text: i18n.locale.importRequested, | 					text: i18n.ts.importRequested, | ||||||
| 				}); | 				}); | ||||||
| 			}).catch((e) => { | 			}).catch((e) => { | ||||||
| 				os.alert({ | 				os.alert({ | ||||||
|  | @ -202,7 +202,7 @@ const menu = (ev: MouseEvent) => { | ||||||
| 				}); | 				}); | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 	}], ev.currentTarget || ev.target); | 	}], ev.currentTarget ?? ev.target); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const setCategoryBulk = async () => { | const setCategoryBulk = async () => { | ||||||
|  | @ -256,7 +256,7 @@ const setTagBulk = async () => { | ||||||
| const delBulk = async () => { | const delBulk = async () => { | ||||||
| 	const { canceled } = await os.confirm({ | 	const { canceled } = await os.confirm({ | ||||||
| 		type: 'warning', | 		type: 'warning', | ||||||
| 		text: i18n.locale.deleteConfirm, | 		text: i18n.ts.deleteConfirm, | ||||||
| 	}); | 	}); | ||||||
| 	if (canceled) return; | 	if (canceled) return; | ||||||
| 	await os.apiWithDialog('admin/emoji/delete-bulk', { | 	await os.apiWithDialog('admin/emoji/delete-bulk', { | ||||||
|  | @ -267,13 +267,13 @@ const delBulk = async () => { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: computed(() => ({ | 	[symbols.PAGE_INFO]: computed(() => ({ | ||||||
| 		title: i18n.locale.customEmojis, | 		title: i18n.ts.customEmojis, | ||||||
| 		icon: 'fas fa-laugh', | 		icon: 'fas fa-laugh', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 		actions: [{ | 		actions: [{ | ||||||
| 			asFullButton: true, | 			asFullButton: true, | ||||||
| 			icon: 'fas fa-plus', | 			icon: 'fas fa-plus', | ||||||
| 			text: i18n.locale.addEmoji, | 			text: i18n.ts.addEmoji, | ||||||
| 			handler: add, | 			handler: add, | ||||||
| 		}, { | 		}, { | ||||||
| 			icon: 'fas fa-ellipsis-h', | 			icon: 'fas fa-ellipsis-h', | ||||||
|  | @ -281,11 +281,11 @@ defineExpose({ | ||||||
| 		}], | 		}], | ||||||
| 		tabs: [{ | 		tabs: [{ | ||||||
| 			active: tab.value === 'local', | 			active: tab.value === 'local', | ||||||
| 			title: i18n.locale.local, | 			title: i18n.ts.local, | ||||||
| 			onClick: () => { tab.value = 'local'; }, | 			onClick: () => { tab.value = 'local'; }, | ||||||
| 		}, { | 		}, { | ||||||
| 			active: tab.value === 'remote', | 			active: tab.value === 'remote', | ||||||
| 			title: i18n.locale.remote, | 			title: i18n.ts.remote, | ||||||
| 			onClick: () => { tab.value = 'remote'; }, | 			onClick: () => { tab.value = 'remote'; }, | ||||||
| 		},] | 		},] | ||||||
| 	})), | 	})), | ||||||
|  |  | ||||||
|  | @ -55,7 +55,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	setup(props, context) { | 	setup(props, context) { | ||||||
| 		const indexInfo = { | 		const indexInfo = { | ||||||
| 			title: i18n.locale.controlPanel, | 			title: i18n.ts.controlPanel, | ||||||
| 			icon: 'fas fa-cog', | 			icon: 'fas fa-cog', | ||||||
| 			bg: 'var(--bg)', | 			bg: 'var(--bg)', | ||||||
| 			hideHeader: true, | 			hideHeader: true, | ||||||
|  | @ -91,119 +91,119 @@ export default defineComponent({ | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		const menuDef = computed(() => [{ | 		const menuDef = computed(() => [{ | ||||||
| 			title: i18n.locale.quickAction, | 			title: i18n.ts.quickAction, | ||||||
| 			items: [{ | 			items: [{ | ||||||
| 				type: 'button', | 				type: 'button', | ||||||
| 				icon: 'fas fa-search', | 				icon: 'fas fa-search', | ||||||
| 				text: i18n.locale.lookup, | 				text: i18n.ts.lookup, | ||||||
| 				action: lookup, | 				action: lookup, | ||||||
| 			}, ...(instance.disableRegistration ? [{ | 			}, ...(instance.disableRegistration ? [{ | ||||||
| 				type: 'button', | 				type: 'button', | ||||||
| 				icon: 'fas fa-user', | 				icon: 'fas fa-user', | ||||||
| 				text: i18n.locale.invite, | 				text: i18n.ts.invite, | ||||||
| 				action: invite, | 				action: invite, | ||||||
| 			}] : [])], | 			}] : [])], | ||||||
| 		}, { | 		}, { | ||||||
| 			title: i18n.locale.administration, | 			title: i18n.ts.administration, | ||||||
| 			items: [{ | 			items: [{ | ||||||
| 				icon: 'fas fa-tachometer-alt', | 				icon: 'fas fa-tachometer-alt', | ||||||
| 				text: i18n.locale.dashboard, | 				text: i18n.ts.dashboard, | ||||||
| 				to: '/admin/overview', | 				to: '/admin/overview', | ||||||
| 				active: page.value === 'overview', | 				active: page.value === 'overview', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-users', | 				icon: 'fas fa-users', | ||||||
| 				text: i18n.locale.users, | 				text: i18n.ts.users, | ||||||
| 				to: '/admin/users', | 				to: '/admin/users', | ||||||
| 				active: page.value === 'users', | 				active: page.value === 'users', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-laugh', | 				icon: 'fas fa-laugh', | ||||||
| 				text: i18n.locale.customEmojis, | 				text: i18n.ts.customEmojis, | ||||||
| 				to: '/admin/emojis', | 				to: '/admin/emojis', | ||||||
| 				active: page.value === 'emojis', | 				active: page.value === 'emojis', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-globe', | 				icon: 'fas fa-globe', | ||||||
| 				text: i18n.locale.federation, | 				text: i18n.ts.federation, | ||||||
| 				to: '/admin/federation', | 				to: '/admin/federation', | ||||||
| 				active: page.value === 'federation', | 				active: page.value === 'federation', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-clipboard-list', | 				icon: 'fas fa-clipboard-list', | ||||||
| 				text: i18n.locale.jobQueue, | 				text: i18n.ts.jobQueue, | ||||||
| 				to: '/admin/queue', | 				to: '/admin/queue', | ||||||
| 				active: page.value === 'queue', | 				active: page.value === 'queue', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-cloud', | 				icon: 'fas fa-cloud', | ||||||
| 				text: i18n.locale.files, | 				text: i18n.ts.files, | ||||||
| 				to: '/admin/files', | 				to: '/admin/files', | ||||||
| 				active: page.value === 'files', | 				active: page.value === 'files', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-broadcast-tower', | 				icon: 'fas fa-broadcast-tower', | ||||||
| 				text: i18n.locale.announcements, | 				text: i18n.ts.announcements, | ||||||
| 				to: '/admin/announcements', | 				to: '/admin/announcements', | ||||||
| 				active: page.value === 'announcements', | 				active: page.value === 'announcements', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-audio-description', | 				icon: 'fas fa-audio-description', | ||||||
| 				text: i18n.locale.ads, | 				text: i18n.ts.ads, | ||||||
| 				to: '/admin/ads', | 				to: '/admin/ads', | ||||||
| 				active: page.value === 'ads', | 				active: page.value === 'ads', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-exclamation-circle', | 				icon: 'fas fa-exclamation-circle', | ||||||
| 				text: i18n.locale.abuseReports, | 				text: i18n.ts.abuseReports, | ||||||
| 				to: '/admin/abuses', | 				to: '/admin/abuses', | ||||||
| 				active: page.value === 'abuses', | 				active: page.value === 'abuses', | ||||||
| 			}], | 			}], | ||||||
| 		}, { | 		}, { | ||||||
| 			title: i18n.locale.settings, | 			title: i18n.ts.settings, | ||||||
| 			items: [{ | 			items: [{ | ||||||
| 				icon: 'fas fa-cog', | 				icon: 'fas fa-cog', | ||||||
| 				text: i18n.locale.general, | 				text: i18n.ts.general, | ||||||
| 				to: '/admin/settings', | 				to: '/admin/settings', | ||||||
| 				active: page.value === 'settings', | 				active: page.value === 'settings', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-envelope', | 				icon: 'fas fa-envelope', | ||||||
| 				text: i18n.locale.emailServer, | 				text: i18n.ts.emailServer, | ||||||
| 				to: '/admin/email-settings', | 				to: '/admin/email-settings', | ||||||
| 				active: page.value === 'email-settings', | 				active: page.value === 'email-settings', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-cloud', | 				icon: 'fas fa-cloud', | ||||||
| 				text: i18n.locale.objectStorage, | 				text: i18n.ts.objectStorage, | ||||||
| 				to: '/admin/object-storage', | 				to: '/admin/object-storage', | ||||||
| 				active: page.value === 'object-storage', | 				active: page.value === 'object-storage', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-lock', | 				icon: 'fas fa-lock', | ||||||
| 				text: i18n.locale.security, | 				text: i18n.ts.security, | ||||||
| 				to: '/admin/security', | 				to: '/admin/security', | ||||||
| 				active: page.value === 'security', | 				active: page.value === 'security', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-globe', | 				icon: 'fas fa-globe', | ||||||
| 				text: i18n.locale.relays, | 				text: i18n.ts.relays, | ||||||
| 				to: '/admin/relays', | 				to: '/admin/relays', | ||||||
| 				active: page.value === 'relays', | 				active: page.value === 'relays', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-share-alt', | 				icon: 'fas fa-share-alt', | ||||||
| 				text: i18n.locale.integration, | 				text: i18n.ts.integration, | ||||||
| 				to: '/admin/integrations', | 				to: '/admin/integrations', | ||||||
| 				active: page.value === 'integrations', | 				active: page.value === 'integrations', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-ban', | 				icon: 'fas fa-ban', | ||||||
| 				text: i18n.locale.instanceBlocking, | 				text: i18n.ts.instanceBlocking, | ||||||
| 				to: '/admin/instance-block', | 				to: '/admin/instance-block', | ||||||
| 				active: page.value === 'instance-block', | 				active: page.value === 'instance-block', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-ghost', | 				icon: 'fas fa-ghost', | ||||||
| 				text: i18n.locale.proxyAccount, | 				text: i18n.ts.proxyAccount, | ||||||
| 				to: '/admin/proxy-account', | 				to: '/admin/proxy-account', | ||||||
| 				active: page.value === 'proxy-account', | 				active: page.value === 'proxy-account', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-cogs', | 				icon: 'fas fa-cogs', | ||||||
| 				text: i18n.locale.other, | 				text: i18n.ts.other, | ||||||
| 				to: '/admin/other-settings', | 				to: '/admin/other-settings', | ||||||
| 				active: page.value === 'other-settings', | 				active: page.value === 'other-settings', | ||||||
| 			}], | 			}], | ||||||
| 		}, { | 		}, { | ||||||
| 			title: i18n.locale.info, | 			title: i18n.ts.info, | ||||||
| 			items: [{ | 			items: [{ | ||||||
| 				icon: 'fas fa-database', | 				icon: 'fas fa-database', | ||||||
| 				text: i18n.locale.database, | 				text: i18n.ts.database, | ||||||
| 				to: '/admin/database', | 				to: '/admin/database', | ||||||
| 				active: page.value === 'database', | 				active: page.value === 'database', | ||||||
| 			}], | 			}], | ||||||
|  | @ -275,37 +275,37 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 		const lookup = (ev) => { | 		const lookup = (ev) => { | ||||||
| 			os.popupMenu([{ | 			os.popupMenu([{ | ||||||
| 				text: i18n.locale.user, | 				text: i18n.ts.user, | ||||||
| 				icon: 'fas fa-user', | 				icon: 'fas fa-user', | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					lookupUser(); | 					lookupUser(); | ||||||
| 				} | 				} | ||||||
| 			}, { | 			}, { | ||||||
| 				text: i18n.locale.note, | 				text: i18n.ts.note, | ||||||
| 				icon: 'fas fa-pencil-alt', | 				icon: 'fas fa-pencil-alt', | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					alert('TODO'); | 					alert('TODO'); | ||||||
| 				} | 				} | ||||||
| 			}, { | 			}, { | ||||||
| 				text: i18n.locale.file, | 				text: i18n.ts.file, | ||||||
| 				icon: 'fas fa-cloud', | 				icon: 'fas fa-cloud', | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					alert('TODO'); | 					alert('TODO'); | ||||||
| 				} | 				} | ||||||
| 			}, { | 			}, { | ||||||
| 				text: i18n.locale.instance, | 				text: i18n.ts.instance, | ||||||
| 				icon: 'fas fa-globe', | 				icon: 'fas fa-globe', | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					alert('TODO'); | 					alert('TODO'); | ||||||
| 				} | 				} | ||||||
| 			}], ev.currentTarget || ev.target); | 			}], ev.currentTarget ?? ev.target); | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		return { | 		return { | ||||||
| 			[symbols.PAGE_INFO]: INFO, | 			[symbols.PAGE_INFO]: INFO, | ||||||
| 			menuDef, | 			menuDef, | ||||||
| 			header: { | 			header: { | ||||||
| 				title: i18n.locale.controlPanel, | 				title: i18n.ts.controlPanel, | ||||||
| 			}, | 			}, | ||||||
| 			noMaintainerInformation, | 			noMaintainerInformation, | ||||||
| 			noBotProtection, | 			noBotProtection, | ||||||
|  |  | ||||||
|  | @ -112,7 +112,7 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		setBannerImage(e) { | 		setBannerImage(e) { | ||||||
| 			selectFile(e.currentTarget || e.target, null).then(file => { | 			selectFile(e.currentTarget ?? e.target, null).then(file => { | ||||||
| 				this.bannerId = file.id; | 				this.bannerId = file.id; | ||||||
| 			}); | 			}); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|  | @ -127,7 +127,7 @@ export default defineComponent({ | ||||||
| 						clipId: this.clip.id, | 						clipId: this.clip.id, | ||||||
| 					}); | 					}); | ||||||
| 				} | 				} | ||||||
| 			} : undefined], ev.currentTarget || ev.target); | 			} : undefined], ev.currentTarget ?? ev.target); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ let folder = $ref(null); | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: computed(() => ({ | 	[symbols.PAGE_INFO]: computed(() => ({ | ||||||
| 		title: folder ? folder.name : i18n.locale.drive, | 		title: folder ? folder.name : i18n.ts.drive, | ||||||
| 		icon: 'fas fa-cloud', | 		icon: 'fas fa-cloud', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 		hideHeader: true, | 		hideHeader: true, | ||||||
|  |  | ||||||
|  | @ -23,13 +23,13 @@ function menu(ev) { | ||||||
| 		type: 'label', | 		type: 'label', | ||||||
| 		text: ':' + props.emoji.name + ':', | 		text: ':' + props.emoji.name + ':', | ||||||
| 	}, { | 	}, { | ||||||
| 		text: i18n.locale.copy, | 		text: i18n.ts.copy, | ||||||
| 		icon: 'fas fa-copy', | 		icon: 'fas fa-copy', | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			copyToClipboard(`:${props.emoji.name}:`); | 			copyToClipboard(`:${props.emoji.name}:`); | ||||||
| 			os.success(); | 			os.success(); | ||||||
| 		} | 		} | ||||||
| 	}], ev.currentTarget || ev.target); | 	}], ev.currentTarget ?? ev.target); | ||||||
| } | } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -16,14 +16,14 @@ const tab = ref('category'); | ||||||
| function menu(ev) { | function menu(ev) { | ||||||
| 	os.popupMenu([{ | 	os.popupMenu([{ | ||||||
| 		icon: 'fas fa-download', | 		icon: 'fas fa-download', | ||||||
| 		text: i18n.locale.export, | 		text: i18n.ts.export, | ||||||
| 		action: async () => { | 		action: async () => { | ||||||
| 			os.api('export-custom-emojis', { | 			os.api('export-custom-emojis', { | ||||||
| 			}) | 			}) | ||||||
| 			.then(() => { | 			.then(() => { | ||||||
| 				os.alert({ | 				os.alert({ | ||||||
| 					type: 'info', | 					type: 'info', | ||||||
| 					text: i18n.locale.exportRequested, | 					text: i18n.ts.exportRequested, | ||||||
| 				}); | 				}); | ||||||
| 			}).catch((e) => { | 			}).catch((e) => { | ||||||
| 				os.alert({ | 				os.alert({ | ||||||
|  | @ -32,12 +32,12 @@ function menu(ev) { | ||||||
| 				}); | 				}); | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 	}], ev.currentTarget || ev.target); | 	}], ev.currentTarget ?? ev.target); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.customEmojis, | 		title: i18n.ts.customEmojis, | ||||||
| 		icon: 'fas fa-laugh', | 		icon: 'fas fa-laugh', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 		actions: [{ | 		actions: [{ | ||||||
|  |  | ||||||
|  | @ -34,7 +34,7 @@ const pagingComponent = ref<InstanceType<typeof MkPagination>>(); | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.favorites, | 		title: i18n.ts.favorites, | ||||||
| 		icon: 'fas fa-star', | 		icon: 'fas fa-star', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ const pagination = { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.featured, | 		title: i18n.ts.featured, | ||||||
| 		icon: 'fas fa-fire-alt', | 		icon: 'fas fa-fire-alt', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -135,7 +135,7 @@ function getStatus(instance) { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.federation, | 		title: i18n.ts.federation, | ||||||
| 		icon: 'fas fa-globe', | 		icon: 'fas fa-globe', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -60,7 +60,7 @@ function reject(user) { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: computed(() => ({ | 	[symbols.PAGE_INFO]: computed(() => ({ | ||||||
| 		title: i18n.locale.followRequests, | 		title: i18n.ts.followRequests, | ||||||
| 		icon: 'fas fa-user-clock', | 		icon: 'fas fa-user-clock', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	})), | 	})), | ||||||
|  |  | ||||||
|  | @ -92,7 +92,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	methods: { | 	methods: { | ||||||
| 		selectFile(e) { | 		selectFile(e) { | ||||||
| 			selectFiles(e.currentTarget || e.target, null).then(files => { | 			selectFiles(e.currentTarget ?? e.target, null).then(files => { | ||||||
| 				this.files = this.files.concat(files); | 				this.files = this.files.concat(files); | ||||||
| 			}); | 			}); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ const pagination = { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.mentions, | 		title: i18n.ts.mentions, | ||||||
| 		icon: 'fas fa-at', | 		icon: 'fas fa-at', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -12,14 +12,14 @@ import { i18n } from '@/i18n'; | ||||||
| const pagination = { | const pagination = { | ||||||
| 	endpoint: 'notes/mentions' as const, | 	endpoint: 'notes/mentions' as const, | ||||||
| 	limit: 10, | 	limit: 10, | ||||||
| 	params: () => ({ | 	params: { | ||||||
| 		visibility: 'specified' | 		visibility: 'specified' | ||||||
| 	}), | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.directNotes, | 		title: i18n.ts.directNotes, | ||||||
| 		icon: 'fas fa-envelope', | 		icon: 'fas fa-envelope', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -128,7 +128,7 @@ export default defineComponent({ | ||||||
| 				text: this.$ts.messagingWithGroup, | 				text: this.$ts.messagingWithGroup, | ||||||
| 				icon: 'fas fa-users', | 				icon: 'fas fa-users', | ||||||
| 				action: () => { this.startGroup() } | 				action: () => { this.startGroup() } | ||||||
| 			}], ev.currentTarget || ev.target); | 			}], ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		async startUser() { | 		async startUser() { | ||||||
|  |  | ||||||
|  | @ -133,7 +133,7 @@ function onCompositionUpdate() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function chooseFile(e: MouseEvent) { | function chooseFile(e: MouseEvent) { | ||||||
| 	selectFile(e.currentTarget || e.target, i18n.locale.selectFile).then(selectedFile => { | 	selectFile(e.currentTarget ?? e.target, i18n.locale.selectFile).then(selectedFile => { | ||||||
| 		file = selectedFile; | 		file = selectedFile; | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|  | @ -193,7 +193,7 @@ function deleteDraft() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function insertEmoji(ev: MouseEvent) { | async function insertEmoji(ev: MouseEvent) { | ||||||
| 	os.openEmojiPicker(ev.currentTarget || ev.target, {}, textEl); | 	os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textEl); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| onMounted(() => { | onMounted(() => { | ||||||
|  |  | ||||||
|  | @ -31,7 +31,7 @@ function onAntennaCreated() { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.manageAntennas, | 		title: i18n.ts.manageAntennas, | ||||||
| 		icon: 'fas fa-satellite', | 		icon: 'fas fa-satellite', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -29,20 +29,20 @@ const pagination = { | ||||||
| const pagingComponent = $ref<InstanceType<typeof MkPagination>>(); | const pagingComponent = $ref<InstanceType<typeof MkPagination>>(); | ||||||
| 
 | 
 | ||||||
| async function create() { | async function create() { | ||||||
| 	const { canceled, result } = await os.form(i18n.locale.createNewClip, { | 	const { canceled, result } = await os.form(i18n.ts.createNewClip, { | ||||||
| 		name: { | 		name: { | ||||||
| 			type: 'string', | 			type: 'string', | ||||||
| 			label: i18n.locale.name, | 			label: i18n.ts.name, | ||||||
| 		}, | 		}, | ||||||
| 		description: { | 		description: { | ||||||
| 			type: 'string', | 			type: 'string', | ||||||
| 			required: false, | 			required: false, | ||||||
| 			multiline: true, | 			multiline: true, | ||||||
| 			label: i18n.locale.description, | 			label: i18n.ts.description, | ||||||
| 		}, | 		}, | ||||||
| 		isPublic: { | 		isPublic: { | ||||||
| 			type: 'boolean', | 			type: 'boolean', | ||||||
| 			label: i18n.locale.public, | 			label: i18n.ts.public, | ||||||
| 			default: false, | 			default: false, | ||||||
| 		}, | 		}, | ||||||
| 	}); | 	}); | ||||||
|  | @ -63,7 +63,7 @@ function onClipDeleted() { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.clip, | 		title: i18n.ts.clip, | ||||||
| 		icon: 'fas fa-paperclip', | 		icon: 'fas fa-paperclip', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 		action: { | 		action: { | ||||||
|  |  | ||||||
|  | @ -31,7 +31,7 @@ const pagination = { | ||||||
| 
 | 
 | ||||||
| async function create() { | async function create() { | ||||||
| 	const { canceled, result: name } = await os.inputText({ | 	const { canceled, result: name } = await os.inputText({ | ||||||
| 		title: i18n.locale.enterListName, | 		title: i18n.ts.enterListName, | ||||||
| 	}); | 	}); | ||||||
| 	if (canceled) return; | 	if (canceled) return; | ||||||
| 	await os.apiWithDialog('users/lists/create', { name: name }); | 	await os.apiWithDialog('users/lists/create', { name: name }); | ||||||
|  | @ -40,7 +40,7 @@ async function create() { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.manageLists, | 		title: i18n.ts.manageLists, | ||||||
| 		icon: 'fas fa-list-ul', | 		icon: 'fas fa-list-ul', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 		action: { | 		action: { | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ import { i18n } from '@/i18n'; | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.notFound, | 		title: i18n.ts.notFound, | ||||||
| 		icon: 'fas fa-exclamation-triangle', | 		icon: 'fas fa-exclamation-triangle', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -27,26 +27,26 @@ function setFilter(ev) { | ||||||
| 	})); | 	})); | ||||||
| 	const items = includeTypes != null ? [{ | 	const items = includeTypes != null ? [{ | ||||||
| 		icon: 'fas fa-times', | 		icon: 'fas fa-times', | ||||||
| 		text: i18n.locale.clear, | 		text: i18n.ts.clear, | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			includeTypes = null; | 			includeTypes = null; | ||||||
| 		} | 		} | ||||||
| 	}, null, ...typeItems] : typeItems; | 	}, null, ...typeItems] : typeItems; | ||||||
| 	os.popupMenu(items, ev.currentTarget || ev.target); | 	os.popupMenu(items, ev.currentTarget ?? ev.target); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: computed(() => ({ | 	[symbols.PAGE_INFO]: computed(() => ({ | ||||||
| 		title: i18n.locale.notifications, | 		title: i18n.ts.notifications, | ||||||
| 		icon: 'fas fa-bell', | 		icon: 'fas fa-bell', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 		actions: [{ | 		actions: [{ | ||||||
| 			text: i18n.locale.filter, | 			text: i18n.ts.filter, | ||||||
| 			icon: 'fas fa-filter', | 			icon: 'fas fa-filter', | ||||||
| 			highlighted: includeTypes != null, | 			highlighted: includeTypes != null, | ||||||
| 			handler: setFilter, | 			handler: setFilter, | ||||||
| 		}, { | 		}, { | ||||||
| 			text: i18n.locale.markAllAsRead, | 			text: i18n.ts.markAllAsRead, | ||||||
| 			icon: 'fas fa-check', | 			icon: 'fas fa-check', | ||||||
| 			handler: () => { | 			handler: () => { | ||||||
| 				os.apiWithDialog('notifications/mark-all-as-read'); | 				os.apiWithDialog('notifications/mark-all-as-read'); | ||||||
|  | @ -54,11 +54,11 @@ defineExpose({ | ||||||
| 		}], | 		}], | ||||||
| 		tabs: [{ | 		tabs: [{ | ||||||
| 			active: tab === 'all', | 			active: tab === 'all', | ||||||
| 			title: i18n.locale.all, | 			title: i18n.ts.all, | ||||||
| 			onClick: () => { tab = 'all'; }, | 			onClick: () => { tab = 'all'; }, | ||||||
| 		}, { | 		}, { | ||||||
| 			active: tab === 'unread', | 			active: tab === 'unread', | ||||||
| 			title: i18n.locale.unread, | 			title: i18n.ts.unread, | ||||||
| 			onClick: () => { tab = 'unread'; }, | 			onClick: () => { tab = 'unread'; }, | ||||||
| 		},] | 		},] | ||||||
| 	})), | 	})), | ||||||
|  |  | ||||||
|  | @ -448,7 +448,7 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		setEyeCatchingImage(e) { | 		setEyeCatchingImage(e) { | ||||||
| 			selectFile(e.currentTarget || e.target, null).then(file => { | 			selectFile(e.currentTarget ?? e.target, null).then(file => { | ||||||
| 				this.eyeCatchingImageId = file.id; | 				this.eyeCatchingImageId = file.id; | ||||||
| 			}); | 			}); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ import { i18n } from '@/i18n'; | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: computed(() => ({ | 	[symbols.PAGE_INFO]: computed(() => ({ | ||||||
| 		title: i18n.locale.preview, | 		title: i18n.ts.preview, | ||||||
| 		icon: 'fas fa-eye', | 		icon: 'fas fa-eye', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	})), | 	})), | ||||||
|  |  | ||||||
|  | @ -3,10 +3,10 @@ | ||||||
| 	<div class="_formRoot"> | 	<div class="_formRoot"> | ||||||
| 		<FormInput v-model="password" type="password" class="_formBlock"> | 		<FormInput v-model="password" type="password" class="_formBlock"> | ||||||
| 			<template #prefix><i class="fas fa-lock"></i></template> | 			<template #prefix><i class="fas fa-lock"></i></template> | ||||||
| 			<template #label>{{ i18n.locale.newPassword }}</template> | 			<template #label>{{ i18n.ts.newPassword }}</template> | ||||||
| 		</FormInput> | 		</FormInput> | ||||||
| 		 | 		 | ||||||
| 		<FormButton primary class="_formBlock" @click="save">{{ i18n.locale.save }}</FormButton> | 		<FormButton primary class="_formBlock" @click="save">{{ i18n.ts.save }}</FormButton> | ||||||
| 	</div> | 	</div> | ||||||
| </MkSpacer> | </MkSpacer> | ||||||
| </template> | </template> | ||||||
|  | @ -43,7 +43,7 @@ onMounted(() => { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.resetPassword, | 		title: i18n.ts.resetPassword, | ||||||
| 		icon: 'fas fa-lock', | 		icon: 'fas fa-lock', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -64,7 +64,7 @@ export default defineComponent({ | ||||||
| 				icon: 'fas fa-trash-alt', | 				icon: 'fas fa-trash-alt', | ||||||
| 				danger: true, | 				danger: true, | ||||||
| 				action: () => this.removeAccount(account), | 				action: () => this.removeAccount(account), | ||||||
| 			}], ev.currentTarget || ev.target); | 			}], ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		addAccount(ev) { | 		addAccount(ev) { | ||||||
|  | @ -74,7 +74,7 @@ export default defineComponent({ | ||||||
| 			}, { | 			}, { | ||||||
| 				text: this.$ts.createAccount, | 				text: this.$ts.createAccount, | ||||||
| 				action: () => { this.createAccount(); }, | 				action: () => { this.createAccount(); }, | ||||||
| 			}], ev.currentTarget || ev.target); | 			}], ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		addExistingAccount() { | 		addExistingAccount() { | ||||||
|  |  | ||||||
|  | @ -62,7 +62,7 @@ export default defineComponent({ | ||||||
| 		const emailAddress = ref($i.email); | 		const emailAddress = ref($i.email); | ||||||
| 
 | 
 | ||||||
| 		const INFO = { | 		const INFO = { | ||||||
| 			title: i18n.locale.email, | 			title: i18n.ts.email, | ||||||
| 			icon: 'fas fa-envelope', | 			icon: 'fas fa-envelope', | ||||||
| 			bg: 'var(--bg)', | 			bg: 'var(--bg)', | ||||||
| 		}; | 		}; | ||||||
|  | @ -75,7 +75,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 		const saveEmailAddress = () => { | 		const saveEmailAddress = () => { | ||||||
| 			os.inputText({ | 			os.inputText({ | ||||||
| 				title: i18n.locale.password, | 				title: i18n.ts.password, | ||||||
| 				type: 'password' | 				type: 'password' | ||||||
| 			}).then(({ canceled, result: password }) => { | 			}).then(({ canceled, result: password }) => { | ||||||
| 				if (canceled) return; | 				if (canceled) return; | ||||||
|  |  | ||||||
|  | @ -60,7 +60,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	setup(props, context) { | 	setup(props, context) { | ||||||
| 		const INFO = { | 		const INFO = { | ||||||
| 			title: i18n.locale.importAndExport, | 			title: i18n.ts.importAndExport, | ||||||
| 			icon: 'fas fa-boxes', | 			icon: 'fas fa-boxes', | ||||||
| 			bg: 'var(--bg)', | 			bg: 'var(--bg)', | ||||||
| 		}; | 		}; | ||||||
|  | @ -71,14 +71,14 @@ export default defineComponent({ | ||||||
| 		const onExportSuccess = () => { | 		const onExportSuccess = () => { | ||||||
| 			os.alert({ | 			os.alert({ | ||||||
| 				type: 'info', | 				type: 'info', | ||||||
| 				text: i18n.locale.exportRequested, | 				text: i18n.ts.exportRequested, | ||||||
| 			}); | 			}); | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		const onImportSuccess = () => { | 		const onImportSuccess = () => { | ||||||
| 			os.alert({ | 			os.alert({ | ||||||
| 				type: 'info', | 				type: 'info', | ||||||
| 				text: i18n.locale.importRequested, | 				text: i18n.ts.importRequested, | ||||||
| 			}); | 			}); | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
|  | @ -114,22 +114,22 @@ export default defineComponent({ | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		const importFollowing = async (ev) => { | 		const importFollowing = async (ev) => { | ||||||
| 			const file = await selectFile(ev.currentTarget || ev.target); | 			const file = await selectFile(ev.currentTarget ?? ev.target); | ||||||
| 			os.api('i/import-following', { fileId: file.id }).then(onImportSuccess).catch(onError); | 			os.api('i/import-following', { fileId: file.id }).then(onImportSuccess).catch(onError); | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		const importUserLists = async (ev) => { | 		const importUserLists = async (ev) => { | ||||||
| 			const file = await selectFile(ev.currentTarget || ev.target); | 			const file = await selectFile(ev.currentTarget ?? ev.target); | ||||||
| 			os.api('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError); | 			os.api('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError); | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		const importMuting = async (ev) => { | 		const importMuting = async (ev) => { | ||||||
| 			const file = await selectFile(ev.currentTarget || ev.target); | 			const file = await selectFile(ev.currentTarget ?? ev.target); | ||||||
| 			os.api('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError); | 			os.api('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError); | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		const importBlocking = async (ev) => { | 		const importBlocking = async (ev) => { | ||||||
| 			const file = await selectFile(ev.currentTarget || ev.target); | 			const file = await selectFile(ev.currentTarget ?? ev.target); | ||||||
| 			os.api('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError); | 			os.api('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError); | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -49,7 +49,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	setup(props, context) { | 	setup(props, context) { | ||||||
| 		const indexInfo = { | 		const indexInfo = { | ||||||
| 			title: i18n.locale.settings, | 			title: i18n.ts.settings, | ||||||
| 			icon: 'fas fa-cog', | 			icon: 'fas fa-cog', | ||||||
| 			bg: 'var(--bg)', | 			bg: 'var(--bg)', | ||||||
| 			hideHeader: true, | 			hideHeader: true, | ||||||
|  | @ -61,96 +61,96 @@ export default defineComponent({ | ||||||
| 		const el = ref(null); | 		const el = ref(null); | ||||||
| 		const childInfo = ref(null); | 		const childInfo = ref(null); | ||||||
| 		const menuDef = computed(() => [{ | 		const menuDef = computed(() => [{ | ||||||
| 			title: i18n.locale.basicSettings, | 			title: i18n.ts.basicSettings, | ||||||
| 			items: [{ | 			items: [{ | ||||||
| 				icon: 'fas fa-user', | 				icon: 'fas fa-user', | ||||||
| 				text: i18n.locale.profile, | 				text: i18n.ts.profile, | ||||||
| 				to: '/settings/profile', | 				to: '/settings/profile', | ||||||
| 				active: page.value === 'profile', | 				active: page.value === 'profile', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-lock-open', | 				icon: 'fas fa-lock-open', | ||||||
| 				text: i18n.locale.privacy, | 				text: i18n.ts.privacy, | ||||||
| 				to: '/settings/privacy', | 				to: '/settings/privacy', | ||||||
| 				active: page.value === 'privacy', | 				active: page.value === 'privacy', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-laugh', | 				icon: 'fas fa-laugh', | ||||||
| 				text: i18n.locale.reaction, | 				text: i18n.ts.reaction, | ||||||
| 				to: '/settings/reaction', | 				to: '/settings/reaction', | ||||||
| 				active: page.value === 'reaction', | 				active: page.value === 'reaction', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-cloud', | 				icon: 'fas fa-cloud', | ||||||
| 				text: i18n.locale.drive, | 				text: i18n.ts.drive, | ||||||
| 				to: '/settings/drive', | 				to: '/settings/drive', | ||||||
| 				active: page.value === 'drive', | 				active: page.value === 'drive', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-bell', | 				icon: 'fas fa-bell', | ||||||
| 				text: i18n.locale.notifications, | 				text: i18n.ts.notifications, | ||||||
| 				to: '/settings/notifications', | 				to: '/settings/notifications', | ||||||
| 				active: page.value === 'notifications', | 				active: page.value === 'notifications', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-envelope', | 				icon: 'fas fa-envelope', | ||||||
| 				text: i18n.locale.email, | 				text: i18n.ts.email, | ||||||
| 				to: '/settings/email', | 				to: '/settings/email', | ||||||
| 				active: page.value === 'email', | 				active: page.value === 'email', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-share-alt', | 				icon: 'fas fa-share-alt', | ||||||
| 				text: i18n.locale.integration, | 				text: i18n.ts.integration, | ||||||
| 				to: '/settings/integration', | 				to: '/settings/integration', | ||||||
| 				active: page.value === 'integration', | 				active: page.value === 'integration', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-lock', | 				icon: 'fas fa-lock', | ||||||
| 				text: i18n.locale.security, | 				text: i18n.ts.security, | ||||||
| 				to: '/settings/security', | 				to: '/settings/security', | ||||||
| 				active: page.value === 'security', | 				active: page.value === 'security', | ||||||
| 			}], | 			}], | ||||||
| 		}, { | 		}, { | ||||||
| 			title: i18n.locale.clientSettings, | 			title: i18n.ts.clientSettings, | ||||||
| 			items: [{ | 			items: [{ | ||||||
| 				icon: 'fas fa-cogs', | 				icon: 'fas fa-cogs', | ||||||
| 				text: i18n.locale.general, | 				text: i18n.ts.general, | ||||||
| 				to: '/settings/general', | 				to: '/settings/general', | ||||||
| 				active: page.value === 'general', | 				active: page.value === 'general', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-palette', | 				icon: 'fas fa-palette', | ||||||
| 				text: i18n.locale.theme, | 				text: i18n.ts.theme, | ||||||
| 				to: '/settings/theme', | 				to: '/settings/theme', | ||||||
| 				active: page.value === 'theme', | 				active: page.value === 'theme', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-list-ul', | 				icon: 'fas fa-list-ul', | ||||||
| 				text: i18n.locale.menu, | 				text: i18n.ts.menu, | ||||||
| 				to: '/settings/menu', | 				to: '/settings/menu', | ||||||
| 				active: page.value === 'menu', | 				active: page.value === 'menu', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-music', | 				icon: 'fas fa-music', | ||||||
| 				text: i18n.locale.sounds, | 				text: i18n.ts.sounds, | ||||||
| 				to: '/settings/sounds', | 				to: '/settings/sounds', | ||||||
| 				active: page.value === 'sounds', | 				active: page.value === 'sounds', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-plug', | 				icon: 'fas fa-plug', | ||||||
| 				text: i18n.locale.plugins, | 				text: i18n.ts.plugins, | ||||||
| 				to: '/settings/plugin', | 				to: '/settings/plugin', | ||||||
| 				active: page.value === 'plugin', | 				active: page.value === 'plugin', | ||||||
| 			}], | 			}], | ||||||
| 		}, { | 		}, { | ||||||
| 			title: i18n.locale.otherSettings, | 			title: i18n.ts.otherSettings, | ||||||
| 			items: [{ | 			items: [{ | ||||||
| 				icon: 'fas fa-boxes', | 				icon: 'fas fa-boxes', | ||||||
| 				text: i18n.locale.importAndExport, | 				text: i18n.ts.importAndExport, | ||||||
| 				to: '/settings/import-export', | 				to: '/settings/import-export', | ||||||
| 				active: page.value === 'import-export', | 				active: page.value === 'import-export', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-volume-mute', | 				icon: 'fas fa-volume-mute', | ||||||
| 				text: i18n.locale.instanceMute, | 				text: i18n.ts.instanceMute, | ||||||
| 				to: '/settings/instance-mute', | 				to: '/settings/instance-mute', | ||||||
| 				active: page.value === 'instance-mute', | 				active: page.value === 'instance-mute', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-ban', | 				icon: 'fas fa-ban', | ||||||
| 				text: i18n.locale.muteAndBlock, | 				text: i18n.ts.muteAndBlock, | ||||||
| 				to: '/settings/mute-block', | 				to: '/settings/mute-block', | ||||||
| 				active: page.value === 'mute-block', | 				active: page.value === 'mute-block', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-comment-slash', | 				icon: 'fas fa-comment-slash', | ||||||
| 				text: i18n.locale.wordMute, | 				text: i18n.ts.wordMute, | ||||||
| 				to: '/settings/word-mute', | 				to: '/settings/word-mute', | ||||||
| 				active: page.value === 'word-mute', | 				active: page.value === 'word-mute', | ||||||
| 			}, { | 			}, { | ||||||
|  | @ -160,7 +160,7 @@ export default defineComponent({ | ||||||
| 				active: page.value === 'api', | 				active: page.value === 'api', | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-ellipsis-h', | 				icon: 'fas fa-ellipsis-h', | ||||||
| 				text: i18n.locale.other, | 				text: i18n.ts.other, | ||||||
| 				to: '/settings/other', | 				to: '/settings/other', | ||||||
| 				active: page.value === 'other', | 				active: page.value === 'other', | ||||||
| 			}], | 			}], | ||||||
|  | @ -168,7 +168,7 @@ export default defineComponent({ | ||||||
| 			items: [{ | 			items: [{ | ||||||
| 				type: 'button', | 				type: 'button', | ||||||
| 				icon: 'fas fa-trash', | 				icon: 'fas fa-trash', | ||||||
| 				text: i18n.locale.clearCache, | 				text: i18n.ts.clearCache, | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					localStorage.removeItem('locale'); | 					localStorage.removeItem('locale'); | ||||||
| 					localStorage.removeItem('theme'); | 					localStorage.removeItem('theme'); | ||||||
|  | @ -177,7 +177,7 @@ export default defineComponent({ | ||||||
| 			}, { | 			}, { | ||||||
| 				type: 'button', | 				type: 'button', | ||||||
| 				icon: 'fas fa-sign-in-alt fa-flip-horizontal', | 				icon: 'fas fa-sign-in-alt fa-flip-horizontal', | ||||||
| 				text: i18n.locale.logout, | 				text: i18n.ts.logout, | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					signout(); | 					signout(); | ||||||
| 				}, | 				}, | ||||||
|  |  | ||||||
|  | @ -52,7 +52,7 @@ const blockingPagination = { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.muteAndBlock, | 		title: i18n.ts.muteAndBlock, | ||||||
| 		icon: 'fas fa-ban', | 		icon: 'fas fa-ban', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -86,7 +86,7 @@ function save() { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.privacy, | 		title: i18n.ts.privacy, | ||||||
| 		icon: 'fas fa-lock-open', | 		icon: 'fas fa-lock-open', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -3,45 +3,45 @@ | ||||||
| 	<div class="llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> | 	<div class="llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> | ||||||
| 		<div class="avatar _acrylic"> | 		<div class="avatar _acrylic"> | ||||||
| 			<MkAvatar class="avatar" :user="$i" :disable-link="true" @click="changeAvatar"/> | 			<MkAvatar class="avatar" :user="$i" :disable-link="true" @click="changeAvatar"/> | ||||||
| 			<MkButton primary class="avatarEdit" @click="changeAvatar">{{ i18n.locale._profile.changeAvatar }}</MkButton> | 			<MkButton primary class="avatarEdit" @click="changeAvatar">{{ i18n.ts._profile.changeAvatar }}</MkButton> | ||||||
| 		</div> | 		</div> | ||||||
| 		<MkButton primary class="bannerEdit" @click="changeBanner">{{ i18n.locale._profile.changeBanner }}</MkButton> | 		<MkButton primary class="bannerEdit" @click="changeBanner">{{ i18n.ts._profile.changeBanner }}</MkButton> | ||||||
| 	</div> | 	</div> | ||||||
| 
 | 
 | ||||||
| 	<FormInput v-model="profile.name" :max="30" manual-save class="_formBlock"> | 	<FormInput v-model="profile.name" :max="30" manual-save class="_formBlock"> | ||||||
| 		<template #label>{{ i18n.locale._profile.name }}</template> | 		<template #label>{{ i18n.ts._profile.name }}</template> | ||||||
| 	</FormInput> | 	</FormInput> | ||||||
| 
 | 
 | ||||||
| 	<FormTextarea v-model="profile.description" :max="500" tall manual-save class="_formBlock"> | 	<FormTextarea v-model="profile.description" :max="500" tall manual-save class="_formBlock"> | ||||||
| 		<template #label>{{ i18n.locale._profile.description }}</template> | 		<template #label>{{ i18n.ts._profile.description }}</template> | ||||||
| 		<template #caption>{{ i18n.locale._profile.youCanIncludeHashtags }}</template> | 		<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> | ||||||
| 	</FormTextarea> | 	</FormTextarea> | ||||||
| 
 | 
 | ||||||
| 	<FormInput v-model="profile.location" manual-save class="_formBlock"> | 	<FormInput v-model="profile.location" manual-save class="_formBlock"> | ||||||
| 		<template #label>{{ i18n.locale.location }}</template> | 		<template #label>{{ i18n.ts.location }}</template> | ||||||
| 		<template #prefix><i class="fas fa-map-marker-alt"></i></template> | 		<template #prefix><i class="fas fa-map-marker-alt"></i></template> | ||||||
| 	</FormInput> | 	</FormInput> | ||||||
| 
 | 
 | ||||||
| 	<FormInput v-model="profile.birthday" type="date" manual-save class="_formBlock"> | 	<FormInput v-model="profile.birthday" type="date" manual-save class="_formBlock"> | ||||||
| 		<template #label>{{ i18n.locale.birthday }}</template> | 		<template #label>{{ i18n.ts.birthday }}</template> | ||||||
| 		<template #prefix><i class="fas fa-birthday-cake"></i></template> | 		<template #prefix><i class="fas fa-birthday-cake"></i></template> | ||||||
| 	</FormInput> | 	</FormInput> | ||||||
| 
 | 
 | ||||||
| 	<FormSelect v-model="profile.lang" class="_formBlock"> | 	<FormSelect v-model="profile.lang" class="_formBlock"> | ||||||
| 		<template #label>{{ i18n.locale.language }}</template> | 		<template #label>{{ i18n.ts.language }}</template> | ||||||
| 		<option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option> | 		<option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option> | ||||||
| 	</FormSelect> | 	</FormSelect> | ||||||
| 
 | 
 | ||||||
| 	<FormSlot> | 	<FormSlot> | ||||||
| 		<MkButton @click="editMetadata">{{ i18n.locale._profile.metadataEdit }}</MkButton> | 		<MkButton @click="editMetadata">{{ i18n.ts._profile.metadataEdit }}</MkButton> | ||||||
| 		<template #caption>{{ i18n.locale._profile.metadataDescription }}</template> | 		<template #caption>{{ i18n.ts._profile.metadataDescription }}</template> | ||||||
| 	</FormSlot> | 	</FormSlot> | ||||||
| 
 | 
 | ||||||
| 	<FormSwitch v-model="profile.isCat" class="_formBlock">{{ i18n.locale.flagAsCat }}<template #caption>{{ i18n.locale.flagAsCatDescription }}</template></FormSwitch> | 	<FormSwitch v-model="profile.isCat" class="_formBlock">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></FormSwitch> | ||||||
| 
 | 
 | ||||||
| 	<FormSwitch v-model="profile.isBot" class="_formBlock">{{ i18n.locale.flagAsBot }}<template #caption>{{ i18n.locale.flagAsBotDescription }}</template></FormSwitch> | 	<FormSwitch v-model="profile.isBot" class="_formBlock">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></FormSwitch> | ||||||
| 
 | 
 | ||||||
| 	<FormSwitch v-model="profile.alwaysMarkNsfw" class="_formBlock">{{ i18n.locale.alwaysMarkSensitive }}</FormSwitch> | 	<FormSwitch v-model="profile.alwaysMarkNsfw" class="_formBlock">{{ i18n.ts.alwaysMarkSensitive }}</FormSwitch> | ||||||
| </div> | </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
|  | @ -102,7 +102,7 @@ function save() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function changeAvatar(ev) { | function changeAvatar(ev) { | ||||||
| 	selectFile(ev.currentTarget || ev.target, i18n.locale.avatar).then(async (file) => { | 	selectFile(ev.currentTarget ?? ev.target, i18n.ts.avatar).then(async (file) => { | ||||||
| 		const i = await os.apiWithDialog('i/update', { | 		const i = await os.apiWithDialog('i/update', { | ||||||
| 			avatarId: file.id, | 			avatarId: file.id, | ||||||
| 		}); | 		}); | ||||||
|  | @ -112,7 +112,7 @@ function changeAvatar(ev) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function changeBanner(ev) { | function changeBanner(ev) { | ||||||
| 	selectFile(ev.currentTarget || ev.target, i18n.locale.banner).then(async (file) => { | 	selectFile(ev.currentTarget ?? ev.target, i18n.ts.banner).then(async (file) => { | ||||||
| 		const i = await os.apiWithDialog('i/update', { | 		const i = await os.apiWithDialog('i/update', { | ||||||
| 			bannerId: file.id, | 			bannerId: file.id, | ||||||
| 		}); | 		}); | ||||||
|  | @ -122,45 +122,45 @@ function changeBanner(ev) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function editMetadata() { | async function editMetadata() { | ||||||
| 	const { canceled, result } = await os.form(i18n.locale._profile.metadata, { | 	const { canceled, result } = await os.form(i18n.ts._profile.metadata, { | ||||||
| 		fieldName0: { | 		fieldName0: { | ||||||
| 			type: 'string', | 			type: 'string', | ||||||
| 			label: i18n.locale._profile.metadataLabel + ' 1', | 			label: i18n.ts._profile.metadataLabel + ' 1', | ||||||
| 			default: additionalFields.fieldName0, | 			default: additionalFields.fieldName0, | ||||||
| 		}, | 		}, | ||||||
| 		fieldValue0: { | 		fieldValue0: { | ||||||
| 			type: 'string', | 			type: 'string', | ||||||
| 			label: i18n.locale._profile.metadataContent + ' 1', | 			label: i18n.ts._profile.metadataContent + ' 1', | ||||||
| 			default: additionalFields.fieldValue0, | 			default: additionalFields.fieldValue0, | ||||||
| 		}, | 		}, | ||||||
| 		fieldName1: { | 		fieldName1: { | ||||||
| 			type: 'string', | 			type: 'string', | ||||||
| 			label: i18n.locale._profile.metadataLabel + ' 2', | 			label: i18n.ts._profile.metadataLabel + ' 2', | ||||||
| 			default: additionalFields.fieldName1, | 			default: additionalFields.fieldName1, | ||||||
| 		}, | 		}, | ||||||
| 		fieldValue1: { | 		fieldValue1: { | ||||||
| 			type: 'string', | 			type: 'string', | ||||||
| 			label: i18n.locale._profile.metadataContent + ' 2', | 			label: i18n.ts._profile.metadataContent + ' 2', | ||||||
| 			default: additionalFields.fieldValue1, | 			default: additionalFields.fieldValue1, | ||||||
| 		}, | 		}, | ||||||
| 		fieldName2: { | 		fieldName2: { | ||||||
| 			type: 'string', | 			type: 'string', | ||||||
| 			label: i18n.locale._profile.metadataLabel + ' 3', | 			label: i18n.ts._profile.metadataLabel + ' 3', | ||||||
| 			default: additionalFields.fieldName2, | 			default: additionalFields.fieldName2, | ||||||
| 		}, | 		}, | ||||||
| 		fieldValue2: { | 		fieldValue2: { | ||||||
| 			type: 'string', | 			type: 'string', | ||||||
| 			label: i18n.locale._profile.metadataContent + ' 3', | 			label: i18n.ts._profile.metadataContent + ' 3', | ||||||
| 			default: additionalFields.fieldValue2, | 			default: additionalFields.fieldValue2, | ||||||
| 		}, | 		}, | ||||||
| 		fieldName3: { | 		fieldName3: { | ||||||
| 			type: 'string', | 			type: 'string', | ||||||
| 			label: i18n.locale._profile.metadataLabel + ' 4', | 			label: i18n.ts._profile.metadataLabel + ' 4', | ||||||
| 			default: additionalFields.fieldName3, | 			default: additionalFields.fieldName3, | ||||||
| 		}, | 		}, | ||||||
| 		fieldValue3: { | 		fieldValue3: { | ||||||
| 			type: 'string', | 			type: 'string', | ||||||
| 			label: i18n.locale._profile.metadataContent + ' 4', | 			label: i18n.ts._profile.metadataContent + ' 4', | ||||||
| 			default: additionalFields.fieldValue3, | 			default: additionalFields.fieldValue3, | ||||||
| 		}, | 		}, | ||||||
| 	}); | 	}); | ||||||
|  | @ -196,7 +196,7 @@ async function editMetadata() { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.profile, | 		title: i18n.ts.profile, | ||||||
| 		icon: 'fas fa-user', | 		icon: 'fas fa-user', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -44,8 +44,8 @@ | ||||||
| </div> | </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts" setup> | ||||||
| import { defineComponent } from 'vue'; | import { watch } from 'vue'; | ||||||
| import XDraggable from 'vuedraggable'; | import XDraggable from 'vuedraggable'; | ||||||
| import FormInput from '@/components/form/input.vue'; | import FormInput from '@/components/form/input.vue'; | ||||||
| import FormRadios from '@/components/form/radios.vue'; | import FormRadios from '@/components/form/radios.vue'; | ||||||
|  | @ -56,91 +56,70 @@ import FormSwitch from '@/components/form/switch.vue'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
| import { defaultStore } from '@/store'; | import { defaultStore } from '@/store'; | ||||||
| import * as symbols from '@/symbols'; | import * as symbols from '@/symbols'; | ||||||
|  | import { i18n } from '@/i18n'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | let reactions = $ref(JSON.parse(JSON.stringify(defaultStore.state.reactions))); | ||||||
| 	components: { |  | ||||||
| 		FormInput, |  | ||||||
| 		FormButton, |  | ||||||
| 		FromSlot, |  | ||||||
| 		FormRadios, |  | ||||||
| 		FormSection, |  | ||||||
| 		FormSwitch, |  | ||||||
| 		XDraggable, |  | ||||||
| 	}, |  | ||||||
| 
 | 
 | ||||||
| 	emits: ['info'], | const reactionPickerWidth = $computed(defaultStore.makeGetterSetter('reactionPickerWidth')); | ||||||
| 	 | const reactionPickerHeight = $computed(defaultStore.makeGetterSetter('reactionPickerHeight')); | ||||||
| 	data() { | const reactionPickerUseDrawerForMobile = $computed(defaultStore.makeGetterSetter('reactionPickerUseDrawerForMobile')); | ||||||
| 		return { | 
 | ||||||
| 			[symbols.PAGE_INFO]: { | function save() { | ||||||
| 				title: this.$ts.reaction, | 	defaultStore.set('reactions', reactions); | ||||||
| 				icon: 'fas fa-laugh', | } | ||||||
| 				action: { | 
 | ||||||
| 					icon: 'fas fa-eye', | function remove(reaction, ev: MouseEvent) { | ||||||
| 					handler: this.preview | 	os.popupMenu([{ | ||||||
| 				}, | 		text: i18n.ts.remove, | ||||||
| 				bg: 'var(--bg)', | 		action: () => { | ||||||
| 			}, | 			reactions = reactions.filter(x => x !== reaction); | ||||||
| 			reactions: JSON.parse(JSON.stringify(this.$store.state.reactions)), |  | ||||||
| 		} | 		} | ||||||
| 	}, | 	}], ev.currentTarget ?? ev.target); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 	computed: { | function preview(ev: MouseEvent) { | ||||||
| 		reactionPickerWidth: defaultStore.makeGetterSetter('reactionPickerWidth'), | 	os.popup(import('@/components/emoji-picker-dialog.vue'), { | ||||||
| 		reactionPickerHeight: defaultStore.makeGetterSetter('reactionPickerHeight'), | 		asReactionPicker: true, | ||||||
| 		reactionPickerUseDrawerForMobile: defaultStore.makeGetterSetter('reactionPickerUseDrawerForMobile'), | 		src: ev.currentTarget ?? ev.target, | ||||||
| 	}, | 	}, {}, 'closed'); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 	watch: { | async function setDefault() { | ||||||
| 		reactions: { | 	const { canceled } = await os.confirm({ | ||||||
| 			handler() { | 		type: 'warning', | ||||||
| 				this.save(); | 		text: i18n.ts.resetAreYouSure, | ||||||
| 			}, | 	}); | ||||||
| 			deep: true | 	if (canceled) return; | ||||||
|  | 
 | ||||||
|  | 	reactions = JSON.parse(JSON.stringify(defaultStore.def.reactions.default)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function chooseEmoji(ev: MouseEvent) { | ||||||
|  | 	os.pickEmoji(ev.currentTarget ?? ev.target, { | ||||||
|  | 		showPinned: false | ||||||
|  | 	}).then(emoji => { | ||||||
|  | 		if (!reactions.includes(emoji)) { | ||||||
|  | 			reactions.push(emoji); | ||||||
| 		} | 		} | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | watch($$(reactions), () => { | ||||||
|  | 	save(); | ||||||
|  | }, { | ||||||
|  | 	deep: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | defineExpose({ | ||||||
|  | 	[symbols.PAGE_INFO]: { | ||||||
|  | 		title: i18n.ts.reaction, | ||||||
|  | 		icon: 'fas fa-laugh', | ||||||
|  | 		action: { | ||||||
|  | 			icon: 'fas fa-eye', | ||||||
|  | 			handler: preview, | ||||||
|  | 		}, | ||||||
|  | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
| 
 |  | ||||||
| 	methods: { |  | ||||||
| 		save() { |  | ||||||
| 			this.$store.set('reactions', this.reactions); |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		remove(reaction, ev) { |  | ||||||
| 			os.popupMenu([{ |  | ||||||
| 				text: this.$ts.remove, |  | ||||||
| 				action: () => { |  | ||||||
| 					this.reactions = this.reactions.filter(x => x !== reaction) |  | ||||||
| 				} |  | ||||||
| 			}], ev.currentTarget || ev.target); |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		preview(ev) { |  | ||||||
| 			os.popup(import('@/components/emoji-picker-dialog.vue'), { |  | ||||||
| 				asReactionPicker: true, |  | ||||||
| 				src: ev.currentTarget || ev.target, |  | ||||||
| 			}, {}, 'closed'); |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		async setDefault() { |  | ||||||
| 			const { canceled } = await os.confirm({ |  | ||||||
| 				type: 'warning', |  | ||||||
| 				text: this.$ts.resetAreYouSure, |  | ||||||
| 			}); |  | ||||||
| 			if (canceled) return; |  | ||||||
| 
 |  | ||||||
| 			this.reactions = JSON.parse(JSON.stringify(this.$store.def.reactions.default)); |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		chooseEmoji(ev) { |  | ||||||
| 			os.pickEmoji(ev.currentTarget || ev.target, { |  | ||||||
| 				showPinned: false |  | ||||||
| 			}).then(emoji => { |  | ||||||
| 				if (!this.reactions.includes(emoji)) { |  | ||||||
| 					this.reactions.push(emoji); |  | ||||||
| 				} |  | ||||||
| 			}); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,12 +1,12 @@ | ||||||
| <template> | <template> | ||||||
| <div class="_formRoot"> | <div class="_formRoot"> | ||||||
| 	<FormTextarea v-model="installThemeCode" class="_formBlock"> | 	<FormTextarea v-model="installThemeCode" class="_formBlock"> | ||||||
| 		<template #label>{{ i18n.locale._theme.code }}</template> | 		<template #label>{{ i18n.ts._theme.code }}</template> | ||||||
| 	</FormTextarea> | 	</FormTextarea> | ||||||
| 
 | 
 | ||||||
| 	<div class="_formBlock" style="display: flex; gap: var(--margin); flex-wrap: wrap;"> | 	<div class="_formBlock" style="display: flex; gap: var(--margin); flex-wrap: wrap;"> | ||||||
| 		<FormButton :disabled="installThemeCode == null" inline @click="() => preview(installThemeCode)"><i class="fas fa-eye"></i> {{ i18n.locale.preview }}</FormButton> | 		<FormButton :disabled="installThemeCode == null" inline @click="() => preview(installThemeCode)"><i class="fas fa-eye"></i> {{ i18n.ts.preview }}</FormButton> | ||||||
| 		<FormButton :disabled="installThemeCode == null" primary inline @click="() => install(installThemeCode)"><i class="fas fa-check"></i> {{ i18n.locale.install }}</FormButton> | 		<FormButton :disabled="installThemeCode == null" primary inline @click="() => install(installThemeCode)"><i class="fas fa-check"></i> {{ i18n.ts.install }}</FormButton> | ||||||
| 	</div> | 	</div> | ||||||
| </div> | </div> | ||||||
| </template> | </template> | ||||||
|  | @ -32,21 +32,21 @@ function parseThemeCode(code: string) { | ||||||
| 	} catch (e) { | 	} catch (e) { | ||||||
| 		os.alert({ | 		os.alert({ | ||||||
| 			type: 'error', | 			type: 'error', | ||||||
| 			text: i18n.locale._theme.invalid | 			text: i18n.ts._theme.invalid | ||||||
| 		}); | 		}); | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 	if (!validateTheme(theme)) { | 	if (!validateTheme(theme)) { | ||||||
| 		os.alert({ | 		os.alert({ | ||||||
| 			type: 'error', | 			type: 'error', | ||||||
| 			text: i18n.locale._theme.invalid | 			text: i18n.ts._theme.invalid | ||||||
| 		}); | 		}); | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 	if (getThemes().some(t => t.id === theme.id)) { | 	if (getThemes().some(t => t.id === theme.id)) { | ||||||
| 		os.alert({ | 		os.alert({ | ||||||
| 			type: 'info', | 			type: 'info', | ||||||
| 			text: i18n.locale._theme.alreadyInstalled | 			text: i18n.ts._theme.alreadyInstalled | ||||||
| 		}); | 		}); | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
|  | @ -71,7 +71,7 @@ async function install(code: string): Promise<void> { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale._theme.install, | 		title: i18n.ts._theme.install, | ||||||
| 		icon: 'fas fa-download', | 		icon: 'fas fa-download', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -116,7 +116,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	setup(props, { emit }) { | 	setup(props, { emit }) { | ||||||
| 		const INFO = { | 		const INFO = { | ||||||
| 			title: i18n.locale.theme, | 			title: i18n.ts.theme, | ||||||
| 			icon: 'fas fa-palette', | 			icon: 'fas fa-palette', | ||||||
| 				bg: 'var(--bg)', | 				bg: 'var(--bg)', | ||||||
| 		}; | 		}; | ||||||
|  | @ -184,7 +184,7 @@ export default defineComponent({ | ||||||
| 			themesCount, | 			themesCount, | ||||||
| 			wallpaper, | 			wallpaper, | ||||||
| 			setWallpaper(e) { | 			setWallpaper(e) { | ||||||
| 				selectFile(e.currentTarget || e.target, null).then(file => { | 				selectFile(e.currentTarget ?? e.target, null).then(file => { | ||||||
| 					wallpaper.value = file.url; | 					wallpaper.value = file.url; | ||||||
| 				}); | 				}); | ||||||
| 			}, | 			}, | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <div> | <div> | ||||||
| 	{{ i18n.locale.processing }} | 	{{ i18n.ts.processing }} | ||||||
| </div> | </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
|  | @ -18,7 +18,7 @@ const props = defineProps<{ | ||||||
| onMounted(async () => { | onMounted(async () => { | ||||||
| 	await os.alert({ | 	await os.alert({ | ||||||
| 		type: 'info', | 		type: 'info', | ||||||
| 		text: i18n.t('clickToFinishEmailVerification', { ok: i18n.locale.gotIt }), | 		text: i18n.t('clickToFinishEmailVerification', { ok: i18n.ts.gotIt }), | ||||||
| 	}); | 	}); | ||||||
| 	const res = await os.apiWithDialog('signup-pending', { | 	const res = await os.apiWithDialog('signup-pending', { | ||||||
| 		code: props.code, | 		code: props.code, | ||||||
|  | @ -28,7 +28,7 @@ onMounted(async () => { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.signup, | 		title: i18n.ts.signup, | ||||||
| 		icon: 'fas fa-user', | 		icon: 'fas fa-user', | ||||||
| 	}, | 	}, | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| <MkSpacer :content-max="800" :margin-min="16" :margin-max="32"> | <MkSpacer :content-max="800" :margin-min="16" :margin-max="32"> | ||||||
| 	<div class="cwepdizn _formRoot"> | 	<div class="cwepdizn _formRoot"> | ||||||
| 		<FormFolder :default-open="true" class="_formBlock"> | 		<FormFolder :default-open="true" class="_formBlock"> | ||||||
| 			<template #label>{{ i18n.locale.backgroundColor }}</template> | 			<template #label>{{ i18n.ts.backgroundColor }}</template> | ||||||
| 			<div class="cwepdizn-colors"> | 			<div class="cwepdizn-colors"> | ||||||
| 				<div class="row"> | 				<div class="row"> | ||||||
| 					<button v-for="color in bgColors.filter(x => x.kind === 'light')" :key="color.color" class="color _button" :class="{ active: theme.props.bg === color.color }" @click="setBgColor(color)"> | 					<button v-for="color in bgColors.filter(x => x.kind === 'light')" :key="color.color" class="color _button" :class="{ active: theme.props.bg === color.color }" @click="setBgColor(color)"> | ||||||
|  | @ -18,7 +18,7 @@ | ||||||
| 		</FormFolder> | 		</FormFolder> | ||||||
| 
 | 
 | ||||||
| 		<FormFolder :default-open="true" class="_formBlock"> | 		<FormFolder :default-open="true" class="_formBlock"> | ||||||
| 			<template #label>{{ i18n.locale.accentColor }}</template> | 			<template #label>{{ i18n.ts.accentColor }}</template> | ||||||
| 			<div class="cwepdizn-colors"> | 			<div class="cwepdizn-colors"> | ||||||
| 				<div class="row"> | 				<div class="row"> | ||||||
| 					<button v-for="color in accentColors" :key="color" class="color rounded _button" :class="{ active: theme.props.accent === color }" @click="setAccentColor(color)"> | 					<button v-for="color in accentColors" :key="color" class="color rounded _button" :class="{ active: theme.props.accent === color }" @click="setAccentColor(color)"> | ||||||
|  | @ -29,7 +29,7 @@ | ||||||
| 		</FormFolder> | 		</FormFolder> | ||||||
| 
 | 
 | ||||||
| 		<FormFolder :default-open="true" class="_formBlock"> | 		<FormFolder :default-open="true" class="_formBlock"> | ||||||
| 			<template #label>{{ i18n.locale.textColor }}</template> | 			<template #label>{{ i18n.ts.textColor }}</template> | ||||||
| 			<div class="cwepdizn-colors"> | 			<div class="cwepdizn-colors"> | ||||||
| 				<div class="row"> | 				<div class="row"> | ||||||
| 					<button v-for="color in fgColors" :key="color" class="color char _button" :class="{ active: (theme.props.fg === color.forLight) || (theme.props.fg === color.forDark) }" @click="setFgColor(color)"> | 					<button v-for="color in fgColors" :key="color" class="color char _button" :class="{ active: (theme.props.fg === color.forLight) || (theme.props.fg === color.forDark) }" @click="setFgColor(color)"> | ||||||
|  | @ -41,22 +41,22 @@ | ||||||
| 
 | 
 | ||||||
| 		<FormFolder :default-open="false" class="_formBlock"> | 		<FormFolder :default-open="false" class="_formBlock"> | ||||||
| 			<template #icon><i class="fas fa-code"></i></template> | 			<template #icon><i class="fas fa-code"></i></template> | ||||||
| 			<template #label>{{ i18n.locale.editCode }}</template> | 			<template #label>{{ i18n.ts.editCode }}</template> | ||||||
| 
 | 
 | ||||||
| 			<div class="_formRoot"> | 			<div class="_formRoot"> | ||||||
| 				<FormTextarea v-model="themeCode" tall class="_formBlock"> | 				<FormTextarea v-model="themeCode" tall class="_formBlock"> | ||||||
| 					<template #label>{{ i18n.locale._theme.code }}</template> | 					<template #label>{{ i18n.ts._theme.code }}</template> | ||||||
| 				</FormTextarea> | 				</FormTextarea> | ||||||
| 				<FormButton primary class="_formBlock" @click="applyThemeCode">{{ i18n.locale.apply }}</FormButton> | 				<FormButton primary class="_formBlock" @click="applyThemeCode">{{ i18n.ts.apply }}</FormButton> | ||||||
| 			</div> | 			</div> | ||||||
| 		</FormFolder> | 		</FormFolder> | ||||||
| 
 | 
 | ||||||
| 		<FormFolder :default-open="false" class="_formBlock"> | 		<FormFolder :default-open="false" class="_formBlock"> | ||||||
| 			<template #label>{{ i18n.locale.addDescription }}</template> | 			<template #label>{{ i18n.ts.addDescription }}</template> | ||||||
| 
 | 
 | ||||||
| 			<div class="_formRoot"> | 			<div class="_formRoot"> | ||||||
| 				<FormTextarea v-model="description"> | 				<FormTextarea v-model="description"> | ||||||
| 					<template #label>{{ i18n.locale._theme.description }}</template> | 					<template #label>{{ i18n.ts._theme.description }}</template> | ||||||
| 				</FormTextarea> | 				</FormTextarea> | ||||||
| 			</div> | 			</div> | ||||||
| 		</FormFolder> | 		</FormFolder> | ||||||
|  | @ -167,7 +167,7 @@ function applyThemeCode() { | ||||||
| 	} catch (err) { | 	} catch (err) { | ||||||
| 		os.alert({ | 		os.alert({ | ||||||
| 			type: 'error', | 			type: 'error', | ||||||
| 			text: i18n.locale._theme.invalid, | 			text: i18n.ts._theme.invalid, | ||||||
| 		}); | 		}); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
|  | @ -177,7 +177,7 @@ function applyThemeCode() { | ||||||
| 
 | 
 | ||||||
| async function saveAs() { | async function saveAs() { | ||||||
| 	const { canceled, result: name } = await os.inputText({ | 	const { canceled, result: name } = await os.inputText({ | ||||||
| 		title: i18n.locale.name, | 		title: i18n.ts.name, | ||||||
| 		allowEmpty: false, | 		allowEmpty: false, | ||||||
| 	}); | 	}); | ||||||
| 	if (canceled) return; | 	if (canceled) return; | ||||||
|  | @ -204,18 +204,18 @@ watch($$(theme), apply, { deep: true }); | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: { | 	[symbols.PAGE_INFO]: { | ||||||
| 		title: i18n.locale.themeEditor, | 		title: i18n.ts.themeEditor, | ||||||
| 		icon: 'fas fa-palette', | 		icon: 'fas fa-palette', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 		actions: [{ | 		actions: [{ | ||||||
| 			asFullButton: true, | 			asFullButton: true, | ||||||
| 			icon: 'fas fa-eye', | 			icon: 'fas fa-eye', | ||||||
| 			text: i18n.locale.preview, | 			text: i18n.ts.preview, | ||||||
| 			handler: showPreview, | 			handler: showPreview, | ||||||
| 		}, { | 		}, { | ||||||
| 			asFullButton: true, | 			asFullButton: true, | ||||||
| 			icon: 'fas fa-check', | 			icon: 'fas fa-check', | ||||||
| 			text: i18n.locale.saveAs, | 			text: i18n.ts.saveAs, | ||||||
| 			handler: saveAs, | 			handler: saveAs, | ||||||
| 		}], | 		}], | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -64,7 +64,7 @@ async function chooseList(ev: MouseEvent): Promise<void> { | ||||||
| 		text: list.name, | 		text: list.name, | ||||||
| 		to: `/timeline/list/${list.id}`, | 		to: `/timeline/list/${list.id}`, | ||||||
| 	})); | 	})); | ||||||
| 	os.popupMenu(items, ev.currentTarget || ev.target); | 	os.popupMenu(items, ev.currentTarget ?? ev.target); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function chooseAntenna(ev: MouseEvent): Promise<void> { | async function chooseAntenna(ev: MouseEvent): Promise<void> { | ||||||
|  | @ -75,7 +75,7 @@ async function chooseAntenna(ev: MouseEvent): Promise<void> { | ||||||
| 		indicate: antenna.hasUnreadNote, | 		indicate: antenna.hasUnreadNote, | ||||||
| 		to: `/timeline/antenna/${antenna.id}`, | 		to: `/timeline/antenna/${antenna.id}`, | ||||||
| 	})); | 	})); | ||||||
| 	os.popupMenu(items, ev.currentTarget || ev.target); | 	os.popupMenu(items, ev.currentTarget ?? ev.target); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function chooseChannel(ev: MouseEvent): Promise<void> { | async function chooseChannel(ev: MouseEvent): Promise<void> { | ||||||
|  | @ -86,7 +86,7 @@ async function chooseChannel(ev: MouseEvent): Promise<void> { | ||||||
| 		indicate: channel.hasUnreadNote, | 		indicate: channel.hasUnreadNote, | ||||||
| 		to: `/channels/${channel.id}`, | 		to: `/channels/${channel.id}`, | ||||||
| 	})); | 	})); | ||||||
| 	os.popupMenu(items, ev.currentTarget || ev.target); | 	os.popupMenu(items, ev.currentTarget ?? ev.target); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function saveSrc(): void { | function saveSrc(): void { | ||||||
|  | @ -97,7 +97,7 @@ function saveSrc(): void { | ||||||
| 
 | 
 | ||||||
| async function timetravel(): Promise<void> { | async function timetravel(): Promise<void> { | ||||||
| 	const { canceled, result: date } = await os.inputDate({ | 	const { canceled, result: date } = await os.inputDate({ | ||||||
| 		title: i18n.locale.date, | 		title: i18n.ts.date, | ||||||
| 	}); | 	}); | ||||||
| 	if (canceled) return; | 	if (canceled) return; | ||||||
| 
 | 
 | ||||||
|  | @ -110,47 +110,47 @@ function focus(): void { | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ | ||||||
| 	[symbols.PAGE_INFO]: computed(() => ({ | 	[symbols.PAGE_INFO]: computed(() => ({ | ||||||
| 		title: i18n.locale.timeline, | 		title: i18n.ts.timeline, | ||||||
| 		icon: src === 'local' ? 'fas fa-comments' : src === 'social' ? 'fas fa-share-alt' : src === 'global' ? 'fas fa-globe' : 'fas fa-home', | 		icon: src === 'local' ? 'fas fa-comments' : src === 'social' ? 'fas fa-share-alt' : src === 'global' ? 'fas fa-globe' : 'fas fa-home', | ||||||
| 		bg: 'var(--bg)', | 		bg: 'var(--bg)', | ||||||
| 		actions: [{ | 		actions: [{ | ||||||
| 			icon: 'fas fa-list-ul', | 			icon: 'fas fa-list-ul', | ||||||
| 			text: i18n.locale.lists, | 			text: i18n.ts.lists, | ||||||
| 			handler: chooseList, | 			handler: chooseList, | ||||||
| 		}, { | 		}, { | ||||||
| 			icon: 'fas fa-satellite', | 			icon: 'fas fa-satellite', | ||||||
| 			text: i18n.locale.antennas, | 			text: i18n.ts.antennas, | ||||||
| 			handler: chooseAntenna, | 			handler: chooseAntenna, | ||||||
| 		}, { | 		}, { | ||||||
| 			icon: 'fas fa-satellite-dish', | 			icon: 'fas fa-satellite-dish', | ||||||
| 			text: i18n.locale.channel, | 			text: i18n.ts.channel, | ||||||
| 			handler: chooseChannel, | 			handler: chooseChannel, | ||||||
| 		}, { | 		}, { | ||||||
| 			icon: 'fas fa-calendar-alt', | 			icon: 'fas fa-calendar-alt', | ||||||
| 			text: i18n.locale.jumpToSpecifiedDate, | 			text: i18n.ts.jumpToSpecifiedDate, | ||||||
| 			handler: timetravel, | 			handler: timetravel, | ||||||
| 		}], | 		}], | ||||||
| 		tabs: [{ | 		tabs: [{ | ||||||
| 			active: src === 'home', | 			active: src === 'home', | ||||||
| 			title: i18n.locale._timelines.home, | 			title: i18n.ts._timelines.home, | ||||||
| 			icon: 'fas fa-home', | 			icon: 'fas fa-home', | ||||||
| 			iconOnly: true, | 			iconOnly: true, | ||||||
| 			onClick: () => { src = 'home'; saveSrc(); }, | 			onClick: () => { src = 'home'; saveSrc(); }, | ||||||
| 		}, ...(isLocalTimelineAvailable ? [{ | 		}, ...(isLocalTimelineAvailable ? [{ | ||||||
| 			active: src === 'local', | 			active: src === 'local', | ||||||
| 			title: i18n.locale._timelines.local, | 			title: i18n.ts._timelines.local, | ||||||
| 			icon: 'fas fa-comments', | 			icon: 'fas fa-comments', | ||||||
| 			iconOnly: true, | 			iconOnly: true, | ||||||
| 			onClick: () => { src = 'local'; saveSrc(); }, | 			onClick: () => { src = 'local'; saveSrc(); }, | ||||||
| 		}, { | 		}, { | ||||||
| 			active: src === 'social', | 			active: src === 'social', | ||||||
| 			title: i18n.locale._timelines.social, | 			title: i18n.ts._timelines.social, | ||||||
| 			icon: 'fas fa-share-alt', | 			icon: 'fas fa-share-alt', | ||||||
| 			iconOnly: true, | 			iconOnly: true, | ||||||
| 			onClick: () => { src = 'social'; saveSrc(); }, | 			onClick: () => { src = 'social'; saveSrc(); }, | ||||||
| 		}] : []), ...(isGlobalTimelineAvailable ? [{ | 		}] : []), ...(isGlobalTimelineAvailable ? [{ | ||||||
| 			active: src === 'global', | 			active: src === 'global', | ||||||
| 			title: i18n.locale._timelines.global, | 			title: i18n.ts._timelines.global, | ||||||
| 			icon: 'fas fa-globe', | 			icon: 'fas fa-globe', | ||||||
| 			iconOnly: true, | 			iconOnly: true, | ||||||
| 			onClick: () => { src = 'global'; saveSrc(); }, | 			onClick: () => { src = 'global'; saveSrc(); }, | ||||||
|  |  | ||||||
|  | @ -264,7 +264,7 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		menu(ev) { | 		menu(ev) { | ||||||
| 			os.popupMenu(getUserMenu(this.user), ev.currentTarget || ev.target); | 			os.popupMenu(getUserMenu(this.user), ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		parallaxLoop() { | 		parallaxLoop() { | ||||||
|  |  | ||||||
|  | @ -135,7 +135,7 @@ export default defineComponent({ | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					window.open(`https://misskey-hub.net/help.md`, '_blank'); | 					window.open(`https://misskey-hub.net/help.md`, '_blank'); | ||||||
| 				} | 				} | ||||||
| 			}], ev.currentTarget || ev.target); | 			}], ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		number | 		number | ||||||
|  |  | ||||||
|  | @ -119,7 +119,7 @@ export default defineComponent({ | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					window.open(`https://misskey-hub.net/help.md`, '_blank'); | 					window.open(`https://misskey-hub.net/help.md`, '_blank'); | ||||||
| 				} | 				} | ||||||
| 			}], ev.currentTarget || ev.target); | 			}], ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		number | 		number | ||||||
|  |  | ||||||
|  | @ -139,7 +139,7 @@ export default defineComponent({ | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					window.open(`https://misskey-hub.net/help.md`, '_blank'); | 					window.open(`https://misskey-hub.net/help.md`, '_blank'); | ||||||
| 				} | 				} | ||||||
| 			}], ev.currentTarget || ev.target); | 			}], ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		number | 		number | ||||||
|  |  | ||||||
|  | @ -27,7 +27,7 @@ export function getNoteMenu(props: { | ||||||
| 	function del(): void { | 	function del(): void { | ||||||
| 		os.confirm({ | 		os.confirm({ | ||||||
| 			type: 'warning', | 			type: 'warning', | ||||||
| 			text: i18n.locale.noteDeleteConfirm, | 			text: i18n.ts.noteDeleteConfirm, | ||||||
| 		}).then(({ canceled }) => { | 		}).then(({ canceled }) => { | ||||||
| 			if (canceled) return; | 			if (canceled) return; | ||||||
| 
 | 
 | ||||||
|  | @ -40,7 +40,7 @@ export function getNoteMenu(props: { | ||||||
| 	function delEdit(): void { | 	function delEdit(): void { | ||||||
| 		os.confirm({ | 		os.confirm({ | ||||||
| 			type: 'warning', | 			type: 'warning', | ||||||
| 			text: i18n.locale.deleteAndEditConfirm, | 			text: i18n.ts.deleteAndEditConfirm, | ||||||
| 		}).then(({ canceled }) => { | 		}).then(({ canceled }) => { | ||||||
| 			if (canceled) return; | 			if (canceled) return; | ||||||
| 
 | 
 | ||||||
|  | @ -87,7 +87,7 @@ export function getNoteMenu(props: { | ||||||
| 			if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') { | 			if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') { | ||||||
| 				os.alert({ | 				os.alert({ | ||||||
| 					type: 'error', | 					type: 'error', | ||||||
| 					text: i18n.locale.pinLimitExceeded | 					text: i18n.ts.pinLimitExceeded | ||||||
| 				}); | 				}); | ||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
|  | @ -97,22 +97,22 @@ export function getNoteMenu(props: { | ||||||
| 		const clips = await os.api('clips/list'); | 		const clips = await os.api('clips/list'); | ||||||
| 		os.popupMenu([{ | 		os.popupMenu([{ | ||||||
| 			icon: 'fas fa-plus', | 			icon: 'fas fa-plus', | ||||||
| 			text: i18n.locale.createNew, | 			text: i18n.ts.createNew, | ||||||
| 			action: async () => { | 			action: async () => { | ||||||
| 				const { canceled, result } = await os.form(i18n.locale.createNewClip, { | 				const { canceled, result } = await os.form(i18n.ts.createNewClip, { | ||||||
| 					name: { | 					name: { | ||||||
| 						type: 'string', | 						type: 'string', | ||||||
| 						label: i18n.locale.name | 						label: i18n.ts.name | ||||||
| 					}, | 					}, | ||||||
| 					description: { | 					description: { | ||||||
| 						type: 'string', | 						type: 'string', | ||||||
| 						required: false, | 						required: false, | ||||||
| 						multiline: true, | 						multiline: true, | ||||||
| 						label: i18n.locale.description | 						label: i18n.ts.description | ||||||
| 					}, | 					}, | ||||||
| 					isPublic: { | 					isPublic: { | ||||||
| 						type: 'boolean', | 						type: 'boolean', | ||||||
| 						label: i18n.locale.public, | 						label: i18n.ts.public, | ||||||
| 						default: false | 						default: false | ||||||
| 					} | 					} | ||||||
| 				}); | 				}); | ||||||
|  | @ -133,7 +133,7 @@ export function getNoteMenu(props: { | ||||||
| 
 | 
 | ||||||
| 	async function promote(): Promise<void> { | 	async function promote(): Promise<void> { | ||||||
| 		const { canceled, result: days } = await os.inputNumber({ | 		const { canceled, result: days } = await os.inputNumber({ | ||||||
| 			title: i18n.locale.numberOfDays, | 			title: i18n.ts.numberOfDays, | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		if (canceled) return; | 		if (canceled) return; | ||||||
|  | @ -171,69 +171,69 @@ export function getNoteMenu(props: { | ||||||
| 
 | 
 | ||||||
| 		menu = [{ | 		menu = [{ | ||||||
| 			icon: 'fas fa-copy', | 			icon: 'fas fa-copy', | ||||||
| 			text: i18n.locale.copyContent, | 			text: i18n.ts.copyContent, | ||||||
| 			action: copyContent | 			action: copyContent | ||||||
| 		}, { | 		}, { | ||||||
| 			icon: 'fas fa-link', | 			icon: 'fas fa-link', | ||||||
| 			text: i18n.locale.copyLink, | 			text: i18n.ts.copyLink, | ||||||
| 			action: copyLink | 			action: copyLink | ||||||
| 		}, (appearNote.url || appearNote.uri) ? { | 		}, (appearNote.url || appearNote.uri) ? { | ||||||
| 			icon: 'fas fa-external-link-square-alt', | 			icon: 'fas fa-external-link-square-alt', | ||||||
| 			text: i18n.locale.showOnRemote, | 			text: i18n.ts.showOnRemote, | ||||||
| 			action: () => { | 			action: () => { | ||||||
| 				window.open(appearNote.url || appearNote.uri, '_blank'); | 				window.open(appearNote.url || appearNote.uri, '_blank'); | ||||||
| 			} | 			} | ||||||
| 		} : undefined, | 		} : undefined, | ||||||
| 		{ | 		{ | ||||||
| 			icon: 'fas fa-share-alt', | 			icon: 'fas fa-share-alt', | ||||||
| 			text: i18n.locale.share, | 			text: i18n.ts.share, | ||||||
| 			action: share | 			action: share | ||||||
| 		}, | 		}, | ||||||
| 		instance.translatorAvailable ? { | 		instance.translatorAvailable ? { | ||||||
| 			icon: 'fas fa-language', | 			icon: 'fas fa-language', | ||||||
| 			text: i18n.locale.translate, | 			text: i18n.ts.translate, | ||||||
| 			action: translate | 			action: translate | ||||||
| 		} : undefined, | 		} : undefined, | ||||||
| 		null, | 		null, | ||||||
| 		statePromise.then(state => state.isFavorited ? { | 		statePromise.then(state => state.isFavorited ? { | ||||||
| 			icon: 'fas fa-star', | 			icon: 'fas fa-star', | ||||||
| 			text: i18n.locale.unfavorite, | 			text: i18n.ts.unfavorite, | ||||||
| 			action: () => toggleFavorite(false) | 			action: () => toggleFavorite(false) | ||||||
| 		} : { | 		} : { | ||||||
| 			icon: 'fas fa-star', | 			icon: 'fas fa-star', | ||||||
| 			text: i18n.locale.favorite, | 			text: i18n.ts.favorite, | ||||||
| 			action: () => toggleFavorite(true) | 			action: () => toggleFavorite(true) | ||||||
| 		}), | 		}), | ||||||
| 		{ | 		{ | ||||||
| 			icon: 'fas fa-paperclip', | 			icon: 'fas fa-paperclip', | ||||||
| 			text: i18n.locale.clip, | 			text: i18n.ts.clip, | ||||||
| 			action: () => clip() | 			action: () => clip() | ||||||
| 		}, | 		}, | ||||||
| 		(appearNote.userId != $i.id) ? statePromise.then(state => state.isWatching ? { | 		(appearNote.userId != $i.id) ? statePromise.then(state => state.isWatching ? { | ||||||
| 			icon: 'fas fa-eye-slash', | 			icon: 'fas fa-eye-slash', | ||||||
| 			text: i18n.locale.unwatch, | 			text: i18n.ts.unwatch, | ||||||
| 			action: () => toggleWatch(false) | 			action: () => toggleWatch(false) | ||||||
| 		} : { | 		} : { | ||||||
| 			icon: 'fas fa-eye', | 			icon: 'fas fa-eye', | ||||||
| 			text: i18n.locale.watch, | 			text: i18n.ts.watch, | ||||||
| 			action: () => toggleWatch(true) | 			action: () => toggleWatch(true) | ||||||
| 		}) : undefined, | 		}) : undefined, | ||||||
| 		statePromise.then(state => state.isMutedThread ? { | 		statePromise.then(state => state.isMutedThread ? { | ||||||
| 			icon: 'fas fa-comment-slash', | 			icon: 'fas fa-comment-slash', | ||||||
| 			text: i18n.locale.unmuteThread, | 			text: i18n.ts.unmuteThread, | ||||||
| 			action: () => toggleThreadMute(false) | 			action: () => toggleThreadMute(false) | ||||||
| 		} : { | 		} : { | ||||||
| 			icon: 'fas fa-comment-slash', | 			icon: 'fas fa-comment-slash', | ||||||
| 			text: i18n.locale.muteThread, | 			text: i18n.ts.muteThread, | ||||||
| 			action: () => toggleThreadMute(true) | 			action: () => toggleThreadMute(true) | ||||||
| 		}), | 		}), | ||||||
| 		appearNote.userId == $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? { | 		appearNote.userId == $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? { | ||||||
| 			icon: 'fas fa-thumbtack', | 			icon: 'fas fa-thumbtack', | ||||||
| 			text: i18n.locale.unpin, | 			text: i18n.ts.unpin, | ||||||
| 			action: () => togglePin(false) | 			action: () => togglePin(false) | ||||||
| 		} : { | 		} : { | ||||||
| 			icon: 'fas fa-thumbtack', | 			icon: 'fas fa-thumbtack', | ||||||
| 			text: i18n.locale.pin, | 			text: i18n.ts.pin, | ||||||
| 			action: () => togglePin(true) | 			action: () => togglePin(true) | ||||||
| 		} : undefined, | 		} : undefined, | ||||||
| 		/* | 		/* | ||||||
|  | @ -241,7 +241,7 @@ export function getNoteMenu(props: { | ||||||
| 			null, | 			null, | ||||||
| 			{ | 			{ | ||||||
| 				icon: 'fas fa-bullhorn', | 				icon: 'fas fa-bullhorn', | ||||||
| 				text: i18n.locale.promote, | 				text: i18n.ts.promote, | ||||||
| 				action: promote | 				action: promote | ||||||
| 			}] | 			}] | ||||||
| 			: [] | 			: [] | ||||||
|  | @ -250,7 +250,7 @@ export function getNoteMenu(props: { | ||||||
| 			null, | 			null, | ||||||
| 			{ | 			{ | ||||||
| 				icon: 'fas fa-exclamation-circle', | 				icon: 'fas fa-exclamation-circle', | ||||||
| 				text: i18n.locale.reportAbuse, | 				text: i18n.ts.reportAbuse, | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`; | 					const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`; | ||||||
| 					os.popup(import('@/components/abuse-report-window.vue'), { | 					os.popup(import('@/components/abuse-report-window.vue'), { | ||||||
|  | @ -265,12 +265,12 @@ export function getNoteMenu(props: { | ||||||
| 			null, | 			null, | ||||||
| 			appearNote.userId == $i.id ? { | 			appearNote.userId == $i.id ? { | ||||||
| 				icon: 'fas fa-edit', | 				icon: 'fas fa-edit', | ||||||
| 				text: i18n.locale.deleteAndEdit, | 				text: i18n.ts.deleteAndEdit, | ||||||
| 				action: delEdit | 				action: delEdit | ||||||
| 			} : undefined, | 			} : undefined, | ||||||
| 			{ | 			{ | ||||||
| 				icon: 'fas fa-trash-alt', | 				icon: 'fas fa-trash-alt', | ||||||
| 				text: i18n.locale.delete, | 				text: i18n.ts.delete, | ||||||
| 				danger: true, | 				danger: true, | ||||||
| 				action: del | 				action: del | ||||||
| 			}] | 			}] | ||||||
|  | @ -280,15 +280,15 @@ export function getNoteMenu(props: { | ||||||
| 	} else { | 	} else { | ||||||
| 		menu = [{ | 		menu = [{ | ||||||
| 			icon: 'fas fa-copy', | 			icon: 'fas fa-copy', | ||||||
| 			text: i18n.locale.copyContent, | 			text: i18n.ts.copyContent, | ||||||
| 			action: copyContent | 			action: copyContent | ||||||
| 		}, { | 		}, { | ||||||
| 			icon: 'fas fa-link', | 			icon: 'fas fa-link', | ||||||
| 			text: i18n.locale.copyLink, | 			text: i18n.ts.copyLink, | ||||||
| 			action: copyLink | 			action: copyLink | ||||||
| 		}, (appearNote.url || appearNote.uri) ? { | 		}, (appearNote.url || appearNote.uri) ? { | ||||||
| 			icon: 'fas fa-external-link-square-alt', | 			icon: 'fas fa-external-link-square-alt', | ||||||
| 			text: i18n.locale.showOnRemote, | 			text: i18n.ts.showOnRemote, | ||||||
| 			action: () => { | 			action: () => { | ||||||
| 				window.open(appearNote.url || appearNote.uri, '_blank'); | 				window.open(appearNote.url || appearNote.uri, '_blank'); | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -7,11 +7,11 @@ import { i18n } from '@/i18n'; | ||||||
|  */ |  */ | ||||||
| export const getNoteSummary = (note: misskey.entities.Note): string => { | export const getNoteSummary = (note: misskey.entities.Note): string => { | ||||||
| 	if (note.deletedAt) { | 	if (note.deletedAt) { | ||||||
| 		return `(${i18n.locale.deletedNote})`; | 		return `(${i18n.ts.deletedNote})`; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (note.isHidden) { | 	if (note.isHidden) { | ||||||
| 		return `(${i18n.locale.invisibleNote})`; | 		return `(${i18n.ts.invisibleNote})`; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	let summary = ''; | 	let summary = ''; | ||||||
|  | @ -30,7 +30,7 @@ export const getNoteSummary = (note: misskey.entities.Note): string => { | ||||||
| 
 | 
 | ||||||
| 	// 投票が添付されているとき
 | 	// 投票が添付されているとき
 | ||||||
| 	if (note.poll) { | 	if (note.poll) { | ||||||
| 		summary += ` (${i18n.locale.poll})`; | 		summary += ` (${i18n.ts.poll})`; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// 返信のとき
 | 	// 返信のとき
 | ||||||
|  |  | ||||||
|  | @ -11,12 +11,12 @@ export function getUserMenu(user) { | ||||||
| 	const meId = $i ? $i.id : null; | 	const meId = $i ? $i.id : null; | ||||||
| 
 | 
 | ||||||
| 	async function pushList() { | 	async function pushList() { | ||||||
| 		const t = i18n.locale.selectList; // なぜか後で参照すると null になるので最初にメモリに確保しておく
 | 		const t = i18n.ts.selectList; // なぜか後で参照すると null になるので最初にメモリに確保しておく
 | ||||||
| 		const lists = await os.api('users/lists/list'); | 		const lists = await os.api('users/lists/list'); | ||||||
| 		if (lists.length === 0) { | 		if (lists.length === 0) { | ||||||
| 			os.alert({ | 			os.alert({ | ||||||
| 				type: 'error', | 				type: 'error', | ||||||
| 				text: i18n.locale.youHaveNoLists | 				text: i18n.ts.youHaveNoLists | ||||||
| 			}); | 			}); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
|  | @ -38,12 +38,12 @@ export function getUserMenu(user) { | ||||||
| 		if (groups.length === 0) { | 		if (groups.length === 0) { | ||||||
| 			os.alert({ | 			os.alert({ | ||||||
| 				type: 'error', | 				type: 'error', | ||||||
| 				text: i18n.locale.youHaveNoGroups | 				text: i18n.ts.youHaveNoGroups | ||||||
| 			}); | 			}); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 		const { canceled, result: groupId } = await os.select({ | 		const { canceled, result: groupId } = await os.select({ | ||||||
| 			title: i18n.locale.group, | 			title: i18n.ts.group, | ||||||
| 			items: groups.map(group => ({ | 			items: groups.map(group => ({ | ||||||
| 				value: group.id, text: group.name | 				value: group.id, text: group.name | ||||||
| 			})) | 			})) | ||||||
|  | @ -64,7 +64,7 @@ export function getUserMenu(user) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	async function toggleBlock() { | 	async function toggleBlock() { | ||||||
| 		if (!await getConfirmed(user.isBlocking ? i18n.locale.unblockConfirm : i18n.locale.blockConfirm)) return; | 		if (!await getConfirmed(user.isBlocking ? i18n.ts.unblockConfirm : i18n.ts.blockConfirm)) return; | ||||||
| 
 | 
 | ||||||
| 		os.apiWithDialog(user.isBlocking ? 'blocking/delete' : 'blocking/create', { | 		os.apiWithDialog(user.isBlocking ? 'blocking/delete' : 'blocking/create', { | ||||||
| 			userId: user.id | 			userId: user.id | ||||||
|  | @ -119,70 +119,70 @@ export function getUserMenu(user) { | ||||||
| 
 | 
 | ||||||
| 	let menu = [{ | 	let menu = [{ | ||||||
| 		icon: 'fas fa-at', | 		icon: 'fas fa-at', | ||||||
| 		text: i18n.locale.copyUsername, | 		text: i18n.ts.copyUsername, | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			copyToClipboard(`@${user.username}@${user.host || host}`); | 			copyToClipboard(`@${user.username}@${user.host || host}`); | ||||||
| 		} | 		} | ||||||
| 	}, { | 	}, { | ||||||
| 		icon: 'fas fa-info-circle', | 		icon: 'fas fa-info-circle', | ||||||
| 		text: i18n.locale.info, | 		text: i18n.ts.info, | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			os.pageWindow(`/user-info/${user.id}`); | 			os.pageWindow(`/user-info/${user.id}`); | ||||||
| 		} | 		} | ||||||
| 	}, { | 	}, { | ||||||
| 		icon: 'fas fa-envelope', | 		icon: 'fas fa-envelope', | ||||||
| 		text: i18n.locale.sendMessage, | 		text: i18n.ts.sendMessage, | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			os.post({ specified: user }); | 			os.post({ specified: user }); | ||||||
| 		} | 		} | ||||||
| 	}, meId != user.id ? { | 	}, meId != user.id ? { | ||||||
| 		type: 'link', | 		type: 'link', | ||||||
| 		icon: 'fas fa-comments', | 		icon: 'fas fa-comments', | ||||||
| 		text: i18n.locale.startMessaging, | 		text: i18n.ts.startMessaging, | ||||||
| 		to: '/my/messaging/' + Acct.toString(user), | 		to: '/my/messaging/' + Acct.toString(user), | ||||||
| 	} : undefined, null, { | 	} : undefined, null, { | ||||||
| 		icon: 'fas fa-list-ul', | 		icon: 'fas fa-list-ul', | ||||||
| 		text: i18n.locale.addToList, | 		text: i18n.ts.addToList, | ||||||
| 		action: pushList | 		action: pushList | ||||||
| 	}, meId != user.id ? { | 	}, meId != user.id ? { | ||||||
| 		icon: 'fas fa-users', | 		icon: 'fas fa-users', | ||||||
| 		text: i18n.locale.inviteToGroup, | 		text: i18n.ts.inviteToGroup, | ||||||
| 		action: inviteGroup | 		action: inviteGroup | ||||||
| 	} : undefined] as any; | 	} : undefined] as any; | ||||||
| 
 | 
 | ||||||
| 	if ($i && meId != user.id) { | 	if ($i && meId != user.id) { | ||||||
| 		menu = menu.concat([null, { | 		menu = menu.concat([null, { | ||||||
| 			icon: user.isMuted ? 'fas fa-eye' : 'fas fa-eye-slash', | 			icon: user.isMuted ? 'fas fa-eye' : 'fas fa-eye-slash', | ||||||
| 			text: user.isMuted ? i18n.locale.unmute : i18n.locale.mute, | 			text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute, | ||||||
| 			action: toggleMute | 			action: toggleMute | ||||||
| 		}, { | 		}, { | ||||||
| 			icon: 'fas fa-ban', | 			icon: 'fas fa-ban', | ||||||
| 			text: user.isBlocking ? i18n.locale.unblock : i18n.locale.block, | 			text: user.isBlocking ? i18n.ts.unblock : i18n.ts.block, | ||||||
| 			action: toggleBlock | 			action: toggleBlock | ||||||
| 		}]); | 		}]); | ||||||
| 
 | 
 | ||||||
| 		if (user.isFollowed) { | 		if (user.isFollowed) { | ||||||
| 			menu = menu.concat([{ | 			menu = menu.concat([{ | ||||||
| 				icon: 'fas fa-unlink', | 				icon: 'fas fa-unlink', | ||||||
| 				text: i18n.locale.breakFollow, | 				text: i18n.ts.breakFollow, | ||||||
| 				action: invalidateFollow | 				action: invalidateFollow | ||||||
| 			}]); | 			}]); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		menu = menu.concat([null, { | 		menu = menu.concat([null, { | ||||||
| 			icon: 'fas fa-exclamation-circle', | 			icon: 'fas fa-exclamation-circle', | ||||||
| 			text: i18n.locale.reportAbuse, | 			text: i18n.ts.reportAbuse, | ||||||
| 			action: reportAbuse | 			action: reportAbuse | ||||||
| 		}]); | 		}]); | ||||||
| 
 | 
 | ||||||
| 		if (iAmModerator) { | 		if (iAmModerator) { | ||||||
| 			menu = menu.concat([null, { | 			menu = menu.concat([null, { | ||||||
| 				icon: 'fas fa-microphone-slash', | 				icon: 'fas fa-microphone-slash', | ||||||
| 				text: user.isSilenced ? i18n.locale.unsilence : i18n.locale.silence, | 				text: user.isSilenced ? i18n.ts.unsilence : i18n.ts.silence, | ||||||
| 				action: toggleSilence | 				action: toggleSilence | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-snowflake', | 				icon: 'fas fa-snowflake', | ||||||
| 				text: user.isSuspended ? i18n.locale.unsuspend : i18n.locale.suspend, | 				text: user.isSuspended ? i18n.ts.unsuspend : i18n.ts.suspend, | ||||||
| 				action: toggleSuspend | 				action: toggleSuspend | ||||||
| 			}]); | 			}]); | ||||||
| 		} | 		} | ||||||
|  | @ -191,7 +191,7 @@ export function getUserMenu(user) { | ||||||
| 	if ($i && meId === user.id) { | 	if ($i && meId === user.id) { | ||||||
| 		menu = menu.concat([null, { | 		menu = menu.concat([null, { | ||||||
| 			icon: 'fas fa-pencil-alt', | 			icon: 'fas fa-pencil-alt', | ||||||
| 			text: i18n.locale.editProfile, | 			text: i18n.ts.editProfile, | ||||||
| 			action: () => { | 			action: () => { | ||||||
| 				router.push('/settings/profile'); | 				router.push('/settings/profile'); | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| export class I18n<T extends Record<string, any>> { | export class I18n<T extends Record<string, any>> { | ||||||
| 	public locale: T; | 	public ts: T; | ||||||
| 
 | 
 | ||||||
| 	constructor(locale: T) { | 	constructor(locale: T) { | ||||||
| 		this.locale = locale; | 		this.ts = locale; | ||||||
| 
 | 
 | ||||||
| 		//#region BIND
 | 		//#region BIND
 | ||||||
| 		this.t = this.t.bind(this); | 		this.t = this.t.bind(this); | ||||||
|  | @ -11,9 +11,9 @@ export class I18n<T extends Record<string, any>> { | ||||||
| 
 | 
 | ||||||
| 	// string にしているのは、ドット区切りでのパス指定を許可するため
 | 	// string にしているのは、ドット区切りでのパス指定を許可するため
 | ||||||
| 	// なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも
 | 	// なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも
 | ||||||
| 	public t(key: string, args?: Record<string, any>): string { | 	public t(key: string, args?: Record<string, string>): string { | ||||||
| 		try { | 		try { | ||||||
| 			let str = key.split('.').reduce((o, i) => o[i], this.locale) as string; | 			let str = key.split('.').reduce((o, i) => o[i], this.ts) as unknown as string; | ||||||
| 
 | 
 | ||||||
| 			if (args) { | 			if (args) { | ||||||
| 				for (const [k, v] of Object.entries(args)) { | 				for (const [k, v] of Object.entries(args)) { | ||||||
|  | @ -21,7 +21,7 @@ export class I18n<T extends Record<string, any>> { | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			return str; | 			return str; | ||||||
| 		} catch (e) { | 		} catch (err) { | ||||||
| 			console.warn(`missing localization '${key}'`); | 			console.warn(`missing localization '${key}'`); | ||||||
| 			return key; | 			return key; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ import * as os from '@/os'; | ||||||
| 
 | 
 | ||||||
| export async function lookupUser() { | export async function lookupUser() { | ||||||
| 	const { canceled, result } = await os.inputText({ | 	const { canceled, result } = await os.inputText({ | ||||||
| 		title: i18n.locale.usernameOrUserId, | 		title: i18n.ts.usernameOrUserId, | ||||||
| 	}); | 	}); | ||||||
| 	if (canceled) return; | 	if (canceled) return; | ||||||
| 
 | 
 | ||||||
|  | @ -19,7 +19,7 @@ export async function lookupUser() { | ||||||
| 		if (_notFound) { | 		if (_notFound) { | ||||||
| 			os.alert({ | 			os.alert({ | ||||||
| 				type: 'error', | 				type: 'error', | ||||||
| 				text: i18n.locale.noSuchUser | 				text: i18n.ts.noSuchUser | ||||||
| 			}); | 			}); | ||||||
| 		} else { | 		} else { | ||||||
| 			_notFound = true; | 			_notFound = true; | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ export function pleaseLogin() { | ||||||
| 	if ($i) return; | 	if ($i) return; | ||||||
| 
 | 
 | ||||||
| 	alert({ | 	alert({ | ||||||
| 		title: i18n.locale.signinRequired, | 		title: i18n.ts.signinRequired, | ||||||
| 		text: null | 		text: null | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ import { router } from '@/router'; | ||||||
| 
 | 
 | ||||||
| export async function search() { | export async function search() { | ||||||
| 	const { canceled, result: query } = await os.inputText({ | 	const { canceled, result: query } = await os.inputText({ | ||||||
| 		title: i18n.locale.search, | 		title: i18n.ts.search, | ||||||
| 	}); | 	}); | ||||||
| 	if (canceled || query == null || query === '') return; | 	if (canceled || query == null || query === '') return; | ||||||
| 
 | 
 | ||||||
|  | @ -46,7 +46,7 @@ export async function search() { | ||||||
| 			uri: q | 			uri: q | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		os.promiseDialog(promise, null, null, i18n.locale.fetchingAsApObject); | 		os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject); | ||||||
| 
 | 
 | ||||||
| 		const res = await promise; | 		const res = await promise; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -41,9 +41,9 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv | ||||||
| 
 | 
 | ||||||
| 		const chooseFileFromUrl = () => { | 		const chooseFileFromUrl = () => { | ||||||
| 			os.inputText({ | 			os.inputText({ | ||||||
| 				title: i18n.locale.uploadFromUrl, | 				title: i18n.ts.uploadFromUrl, | ||||||
| 				type: 'url', | 				type: 'url', | ||||||
| 				placeholder: i18n.locale.uploadFromUrlDescription | 				placeholder: i18n.ts.uploadFromUrlDescription | ||||||
| 			}).then(({ canceled, result: url }) => { | 			}).then(({ canceled, result: url }) => { | ||||||
| 				if (canceled) return; | 				if (canceled) return; | ||||||
| 
 | 
 | ||||||
|  | @ -64,8 +64,8 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv | ||||||
| 				}); | 				}); | ||||||
| 
 | 
 | ||||||
| 				os.alert({ | 				os.alert({ | ||||||
| 					title: i18n.locale.uploadFromUrlRequested, | 					title: i18n.ts.uploadFromUrlRequested, | ||||||
| 					text: i18n.locale.uploadFromUrlMayTakeTime | 					text: i18n.ts.uploadFromUrlMayTakeTime | ||||||
| 				}); | 				}); | ||||||
| 			}); | 			}); | ||||||
| 		}; | 		}; | ||||||
|  | @ -74,15 +74,15 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv | ||||||
| 			text: label, | 			text: label, | ||||||
| 			type: 'label' | 			type: 'label' | ||||||
| 		} : undefined, { | 		} : undefined, { | ||||||
| 			text: i18n.locale.upload, | 			text: i18n.ts.upload, | ||||||
| 			icon: 'fas fa-upload', | 			icon: 'fas fa-upload', | ||||||
| 			action: chooseFileFromPc | 			action: chooseFileFromPc | ||||||
| 		}, { | 		}, { | ||||||
| 			text: i18n.locale.fromDrive, | 			text: i18n.ts.fromDrive, | ||||||
| 			icon: 'fas fa-cloud', | 			icon: 'fas fa-cloud', | ||||||
| 			action: chooseFileFromDrive | 			action: chooseFileFromDrive | ||||||
| 		}, { | 		}, { | ||||||
| 			text: i18n.locale.fromUrl, | 			text: i18n.ts.fromUrl, | ||||||
| 			icon: 'fas fa-link', | 			icon: 'fas fa-link', | ||||||
| 			action: chooseFileFromUrl | 			action: chooseFileFromUrl | ||||||
| 		}], src); | 		}], src); | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ import { i18n } from '@/i18n'; | ||||||
| export function showSuspendedDialog() { | export function showSuspendedDialog() { | ||||||
| 	return os.alert({ | 	return os.alert({ | ||||||
| 		type: 'error', | 		type: 'error', | ||||||
| 		title: i18n.locale.yourAccountSuspendedTitle, | 		title: i18n.ts.yourAccountSuspendedTitle, | ||||||
| 		text: i18n.locale.yourAccountSuspendedDescription | 		text: i18n.ts.yourAccountSuspendedDescription | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ export function useLeaveGuard(enabled: Ref<boolean>) { | ||||||
| 
 | 
 | ||||||
| 			const { canceled } = await os.confirm({ | 			const { canceled } = await os.confirm({ | ||||||
| 				type: 'warning', | 				type: 'warning', | ||||||
| 				text: i18n.locale.leaveConfirm, | 				text: i18n.ts.leaveConfirm, | ||||||
| 			}); | 			}); | ||||||
| 
 | 
 | ||||||
| 			return canceled; | 			return canceled; | ||||||
|  | @ -23,7 +23,7 @@ export function useLeaveGuard(enabled: Ref<boolean>) { | ||||||
| 
 | 
 | ||||||
| 			const { canceled } = await os.confirm({ | 			const { canceled } = await os.confirm({ | ||||||
| 				type: 'warning', | 				type: 'warning', | ||||||
| 				text: i18n.locale.leaveConfirm, | 				text: i18n.ts.leaveConfirm, | ||||||
| 			}); | 			}); | ||||||
| 
 | 
 | ||||||
| 			return !canceled; | 			return !canceled; | ||||||
|  |  | ||||||
|  | @ -104,7 +104,7 @@ export default defineComponent({ | ||||||
| 			]; | 			]; | ||||||
| 
 | 
 | ||||||
| 			const { canceled, result: column } = await os.select({ | 			const { canceled, result: column } = await os.select({ | ||||||
| 				title: i18n.locale._deck.addColumn, | 				title: i18n.ts._deck.addColumn, | ||||||
| 				items: columns.map(column => ({ | 				items: columns.map(column => ({ | ||||||
| 					value: column, text: i18n.t('_deck._columns.' + column) | 					value: column, text: i18n.t('_deck._columns.' + column) | ||||||
| 				})) | 				})) | ||||||
|  | @ -121,7 +121,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 		const onContextmenu = (ev) => { | 		const onContextmenu = (ev) => { | ||||||
| 			os.contextMenu([{ | 			os.contextMenu([{ | ||||||
| 				text: i18n.locale._deck.addColumn, | 				text: i18n.ts._deck.addColumn, | ||||||
| 				icon: null, | 				icon: null, | ||||||
| 				action: addColumn | 				action: addColumn | ||||||
| 			}], ev); | 			}], ev); | ||||||
|  |  | ||||||
|  | @ -77,12 +77,12 @@ export const loadDeck = async () => { | ||||||
| 			deckStore.set('columns', [{ | 			deckStore.set('columns', [{ | ||||||
| 				id: 'a', | 				id: 'a', | ||||||
| 				type: 'main', | 				type: 'main', | ||||||
| 				name: i18n.locale._deck._columns.main, | 				name: i18n.ts._deck._columns.main, | ||||||
| 				width: 350, | 				width: 350, | ||||||
| 			}, { | 			}, { | ||||||
| 				id: 'b', | 				id: 'b', | ||||||
| 				type: 'notifications', | 				type: 'notifications', | ||||||
| 				name: i18n.locale._deck._columns.notifications, | 				name: i18n.ts._deck._columns.notifications, | ||||||
| 				width: 330, | 				width: 330, | ||||||
| 			}]); | 			}]); | ||||||
| 			deckStore.set('layout', [['a'], ['b']]); | 			deckStore.set('layout', [['a'], ['b']]); | ||||||
|  |  | ||||||
|  | @ -171,13 +171,13 @@ export default defineComponent({ | ||||||
| 				text: path, | 				text: path, | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-columns', | 				icon: 'fas fa-columns', | ||||||
| 				text: i18n.locale.openInSideView, | 				text: i18n.ts.openInSideView, | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					this.$refs.side.navigate(path); | 					this.$refs.side.navigate(path); | ||||||
| 				} | 				} | ||||||
| 			}, { | 			}, { | ||||||
| 				icon: 'fas fa-window-maximize', | 				icon: 'fas fa-window-maximize', | ||||||
| 				text: i18n.locale.openInWindow, | 				text: i18n.ts.openInWindow, | ||||||
| 				action: () => { | 				action: () => { | ||||||
| 					os.pageWindow(path); | 					os.pageWindow(path); | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | @ -79,13 +79,13 @@ const tick = () => { | ||||||
| 	month.value = nm + 1; | 	month.value = nm + 1; | ||||||
| 	day.value = nd; | 	day.value = nd; | ||||||
| 	weekDay.value = [ | 	weekDay.value = [ | ||||||
| 		i18n.locale._weekday.sunday, | 		i18n.ts._weekday.sunday, | ||||||
| 		i18n.locale._weekday.monday, | 		i18n.ts._weekday.monday, | ||||||
| 		i18n.locale._weekday.tuesday, | 		i18n.ts._weekday.tuesday, | ||||||
| 		i18n.locale._weekday.wednesday, | 		i18n.ts._weekday.wednesday, | ||||||
| 		i18n.locale._weekday.thursday, | 		i18n.ts._weekday.thursday, | ||||||
| 		i18n.locale._weekday.friday, | 		i18n.ts._weekday.friday, | ||||||
| 		i18n.locale._weekday.saturday | 		i18n.ts._weekday.saturday | ||||||
| 	][now.getDay()]; | 	][now.getDay()]; | ||||||
| 
 | 
 | ||||||
| 	const dayNumer   = now.getTime() - new Date(ny, nm, nd).getTime(); | 	const dayNumer   = now.getTime() - new Date(ny, nm, nd).getTime(); | ||||||
|  |  | ||||||
|  | @ -101,22 +101,22 @@ const choose = async (ev) => { | ||||||
| 		} | 		} | ||||||
| 	})); | 	})); | ||||||
| 	os.popupMenu([{ | 	os.popupMenu([{ | ||||||
| 		text: i18n.locale._timelines.home, | 		text: i18n.ts._timelines.home, | ||||||
| 		icon: 'fas fa-home', | 		icon: 'fas fa-home', | ||||||
| 		action: () => { setSrc('home') } | 		action: () => { setSrc('home') } | ||||||
| 	}, { | 	}, { | ||||||
| 		text: i18n.locale._timelines.local, | 		text: i18n.ts._timelines.local, | ||||||
| 		icon: 'fas fa-comments', | 		icon: 'fas fa-comments', | ||||||
| 		action: () => { setSrc('local') } | 		action: () => { setSrc('local') } | ||||||
| 	}, { | 	}, { | ||||||
| 		text: i18n.locale._timelines.social, | 		text: i18n.ts._timelines.social, | ||||||
| 		icon: 'fas fa-share-alt', | 		icon: 'fas fa-share-alt', | ||||||
| 		action: () => { setSrc('social') } | 		action: () => { setSrc('social') } | ||||||
| 	}, { | 	}, { | ||||||
| 		text: i18n.locale._timelines.global, | 		text: i18n.ts._timelines.global, | ||||||
| 		icon: 'fas fa-globe', | 		icon: 'fas fa-globe', | ||||||
| 		action: () => { setSrc('global') } | 		action: () => { setSrc('global') } | ||||||
| 	}, antennaItems.length > 0 ? null : undefined, ...antennaItems, listItems.length > 0 ? null : undefined, ...listItems], ev.currentTarget || ev.target).then(() => { | 	}, antennaItems.length > 0 ? null : undefined, ...antennaItems, listItems.length > 0 ? null : undefined, ...listItems], ev.currentTarget ?? ev.target).then(() => { | ||||||
| 		menuOpened.value = false; | 		menuOpened.value = false; | ||||||
| 	}); | 	}); | ||||||
| }; | }; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue