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