refactor(client):

This commit is contained in:
syuilo
2022-07-20 22:24:26 +09:00
parent 9a1f5afde9
commit 50e3ca19bc
92 changed files with 793 additions and 756 deletions

View File

@ -6,7 +6,7 @@
<XNoteHeader class="header" :note="note" :mini="true"/>
<div class="body">
<p v-if="note.cw != null" class="cw">
<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis" />
<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis"/>
<XCwButton v-model="showContent" :note="note"/>
</p>
<div v-show="note.cw == null || showContent" class="content">
@ -19,7 +19,7 @@
<MkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" class="reply" :detail="true" :depth="depth + 1"/>
</template>
<div v-else class="more">
<MkA class="text _link" :to="notePage(note)">{{ $ts.continueThread }} <i class="fas fa-angle-double-right"></i></MkA>
<MkA class="text _link" :to="notePage(note)">{{ i18n.ts.continueThread }} <i class="fas fa-angle-double-right"></i></MkA>
</div>
</div>
</template>
@ -27,11 +27,12 @@
<script lang="ts" setup>
import { } from 'vue';
import * as misskey from 'misskey-js';
import { notePage } from '@/filters/note';
import XNoteHeader from './note-header.vue';
import MkNoteSubNoteContent from './sub-note-content.vue';
import XCwButton from './cw-button.vue';
import { notePage } from '@/filters/note';
import * as os from '@/os';
import { i18n } from '@/i18n';
const props = withDefaults(defineProps<{
note: misskey.entities.Note;
@ -49,7 +50,7 @@ let replies: misskey.entities.Note[] = $ref([]);
if (props.detail) {
os.api('notes/children', {
noteId: props.note.id,
limit: 5
limit: 5,
}).then(res => {
replies = res;
});

View File

@ -9,7 +9,7 @@
</div>
</MkA>
<MkKeyValue class="_formBlock">
<template #key>{{ $ts.registeredDate }}</template>
<template #key>{{ i18n.ts.registeredDate }}</template>
<template #value>{{ new Date(report.targetUser.createdAt).toLocaleString() }} (<MkTime :time="report.targetUser.createdAt"/>)</template>
</MkKeyValue>
</div>
@ -18,18 +18,18 @@
<Mfm :text="report.comment"/>
</div>
<hr/>
<div>{{ $ts.reporter }}: <MkAcct :user="report.reporter"/></div>
<div>{{ i18n.ts.reporter }}: <MkAcct :user="report.reporter"/></div>
<div v-if="report.assignee">
{{ $ts.moderator }}:
{{ i18n.ts.moderator }}:
<MkAcct :user="report.assignee"/>
</div>
<div><MkTime :time="report.createdAt"/></div>
<div class="action">
<MkSwitch v-model="forward" :disabled="report.targetUser.host == null || report.resolved">
{{ $ts.forwardReport }}
<template #caption>{{ $ts.forwardReportIsAnonymous }}</template>
{{ i18n.ts.forwardReport }}
<template #caption>{{ i18n.ts.forwardReportIsAnonymous }}</template>
</MkSwitch>
<MkButton v-if="!report.resolved" primary @click="resolve">{{ $ts.abuseMarkAsResolved }}</MkButton>
<MkButton v-if="!report.resolved" primary @click="resolve">{{ i18n.ts.abuseMarkAsResolved }}</MkButton>
</div>
</div>
</div>
@ -41,6 +41,7 @@ import MkSwitch from '@/components/form/switch.vue';
import MkKeyValue from '@/components/key-value.vue';
import { acct, userPage } from '@/filters/user';
import * as os from '@/os';
import { i18n } from '@/i18n';
const props = defineProps<{
report: any;

View File

@ -9,7 +9,7 @@
@ok="ok()"
@closed="$emit('closed')"
>
<template #header>{{ $ts.cropImage }}</template>
<template #header>{{ i18n.ts.cropImage }}</template>
<template #default="{ width, height }">
<div class="mk-cropper-dialog" :style="`--vw: ${width}px; --vh: ${height}px;`">
<Transition name="fade">
@ -36,6 +36,7 @@ import { $i } from '@/account';
import { defaultStore } from '@/store';
import { apiUrl, url } from '@/config';
import { query } from '@/scripts/url';
import { i18n } from '@/i18n';
const emit = defineEmits<{
(ev: 'ok', cropped: misskey.entities.DriveFile): void;

View File

@ -9,7 +9,7 @@
:disabled="disabled"
@keydown.enter="toggle"
>
<span ref="button" v-adaptive-border v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button" @click.prevent="toggle">
<span ref="button" v-adaptive-border v-tooltip="checked ? i18n.ts.itsOn : i18n.ts.itsOff" class="button" @click.prevent="toggle">
<i class="check fas fa-check"></i>
</span>
<span class="label">
@ -24,6 +24,7 @@
import { toRefs, Ref } from 'vue';
import * as os from '@/os';
import Ripple from '@/components/ripple.vue';
import { i18n } from '@/i18n';
const props = defineProps<{
modelValue: boolean | Ref<boolean>;

View File

@ -29,7 +29,7 @@
</div>
<div class="caption"><slot name="caption"></slot></div>
<MkButton v-if="manualSave && changed" primary class="save" @click="updated"><i class="fas fa-check"></i> {{ $ts.save }}</MkButton>
<MkButton v-if="manualSave && changed" primary class="save" @click="updated"><i class="fas fa-check"></i> {{ i18n.ts.save }}</MkButton>
</div>
</template>
@ -38,6 +38,7 @@ import { onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from '
import { debounce } from 'throttle-debounce';
import MkButton from '@/components/ui/button.vue';
import { useInterval } from '@/scripts/use-interval';
import { i18n } from '@/i18n';
const props = defineProps<{
modelValue: string | number;

View File

@ -22,7 +22,7 @@
</div>
<div class="caption"><slot name="caption"></slot></div>
<MkButton v-if="manualSave && changed" primary @click="updated"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
<MkButton v-if="manualSave && changed" primary @click="updated"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
</div>
</template>
@ -31,6 +31,7 @@ import { onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, VNode,
import MkButton from '@/components/ui/button.vue';
import * as os from '@/os';
import { useInterval } from '@/scripts/use-interval';
import { i18n } from '@/i18n';
const props = defineProps<{
modelValue: string;

View File

@ -9,7 +9,7 @@
:disabled="disabled"
@keydown.enter="toggle"
>
<span ref="button" v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button" @click.prevent="toggle">
<span ref="button" v-tooltip="checked ? i18n.ts.itsOn : i18n.ts.itsOff" class="button" @click.prevent="toggle">
<div class="knob"></div>
</span>
<span class="label">
@ -23,6 +23,7 @@
<script lang="ts" setup>
import { toRefs, Ref } from 'vue';
import * as os from '@/os';
import { i18n } from '@/i18n';
const props = defineProps<{
modelValue: boolean | Ref<boolean>;

View File

@ -2,7 +2,8 @@
<div class="adhpbeos">
<div class="label" @click="focus"><slot name="label"></slot></div>
<div class="input" :class="{ disabled, focused, tall, pre }">
<textarea ref="inputEl"
<textarea
ref="inputEl"
v-model="v"
v-adaptive-border
:class="{ code, _monospace: code }"
@ -21,14 +22,15 @@
</div>
<div class="caption"><slot name="caption"></slot></div>
<MkButton v-if="manualSave && changed" primary class="save" @click="updated"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
<MkButton v-if="manualSave && changed" primary class="save" @click="updated"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
import MkButton from '@/components/ui/button.vue';
import { debounce } from 'throttle-debounce';
import MkButton from '@/components/ui/button.vue';
import { i18n } from '@/i18n';
export default defineComponent({
components: {
@ -37,66 +39,66 @@ export default defineComponent({
props: {
modelValue: {
required: true
required: true,
},
type: {
type: String,
required: false
required: false,
},
required: {
type: Boolean,
required: false
required: false,
},
readonly: {
type: Boolean,
required: false
required: false,
},
disabled: {
type: Boolean,
required: false
required: false,
},
pattern: {
type: String,
required: false
required: false,
},
placeholder: {
type: String,
required: false
required: false,
},
autofocus: {
type: Boolean,
required: false,
default: false
default: false,
},
autocomplete: {
required: false
required: false,
},
spellcheck: {
required: false
required: false,
},
code: {
type: Boolean,
required: false
required: false,
},
tall: {
type: Boolean,
required: false,
default: false
default: false,
},
pre: {
type: Boolean,
required: false,
default: false
default: false,
},
debounce: {
type: Boolean,
required: false,
default: false
default: false,
},
manualSave: {
type: Boolean,
required: false,
default: false
default: false,
},
},

View File

@ -2,14 +2,15 @@
<transition :name="$store.state.animation ? 'zoom' : ''" appear>
<div class="mjndxjcg">
<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
<p><i class="fas fa-exclamation-triangle"></i> {{ $ts.somethingHappened }}</p>
<MkButton class="button" @click="() => $emit('retry')">{{ $ts.retry }}</MkButton>
<p><i class="fas fa-exclamation-triangle"></i> {{ i18n.ts.somethingHappened }}</p>
<MkButton class="button" @click="() => $emit('retry')">{{ i18n.ts.retry }}</MkButton>
</div>
</transition>
</template>
<script lang="ts" setup>
import MkButton from '@/components/ui/button.vue';
import { i18n } from '@/i18n';
</script>
<style lang="scss" scoped>

View File

@ -4,29 +4,29 @@
<div class="body">
<div class="selects" style="display: flex;">
<MkSelect v-model="chartSrc" style="margin: 0; flex: 1;">
<optgroup :label="$ts.federation">
<option value="federation">{{ $ts._charts.federation }}</option>
<option value="ap-request">{{ $ts._charts.apRequest }}</option>
<optgroup :label="i18n.ts.federation">
<option value="federation">{{ i18n.ts._charts.federation }}</option>
<option value="ap-request">{{ i18n.ts._charts.apRequest }}</option>
</optgroup>
<optgroup :label="$ts.users">
<option value="users">{{ $ts._charts.usersIncDec }}</option>
<option value="users-total">{{ $ts._charts.usersTotal }}</option>
<option value="active-users">{{ $ts._charts.activeUsers }}</option>
<optgroup :label="i18n.ts.users">
<option value="users">{{ i18n.ts._charts.usersIncDec }}</option>
<option value="users-total">{{ i18n.ts._charts.usersTotal }}</option>
<option value="active-users">{{ i18n.ts._charts.activeUsers }}</option>
</optgroup>
<optgroup :label="$ts.notes">
<option value="notes">{{ $ts._charts.notesIncDec }}</option>
<option value="local-notes">{{ $ts._charts.localNotesIncDec }}</option>
<option value="remote-notes">{{ $ts._charts.remoteNotesIncDec }}</option>
<option value="notes-total">{{ $ts._charts.notesTotal }}</option>
<optgroup :label="i18n.ts.notes">
<option value="notes">{{ i18n.ts._charts.notesIncDec }}</option>
<option value="local-notes">{{ i18n.ts._charts.localNotesIncDec }}</option>
<option value="remote-notes">{{ i18n.ts._charts.remoteNotesIncDec }}</option>
<option value="notes-total">{{ i18n.ts._charts.notesTotal }}</option>
</optgroup>
<optgroup :label="$ts.drive">
<option value="drive-files">{{ $ts._charts.filesIncDec }}</option>
<option value="drive">{{ $ts._charts.storageUsageIncDec }}</option>
<optgroup :label="i18n.ts.drive">
<option value="drive-files">{{ i18n.ts._charts.filesIncDec }}</option>
<option value="drive">{{ i18n.ts._charts.storageUsageIncDec }}</option>
</optgroup>
</MkSelect>
<MkSelect v-model="chartSpan" style="margin: 0 0 0 10px;">
<option value="hour">{{ $ts.perHour }}</option>
<option value="day">{{ $ts.perDay }}</option>
<option value="hour">{{ i18n.ts.perHour }}</option>
<option value="day">{{ i18n.ts.perDay }}</option>
</MkSelect>
</div>
<div class="chart">
@ -71,6 +71,7 @@ import MkSelect from '@/components/form/select.vue';
import MkChart from '@/components/chart.vue';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import * as os from '@/os';
import { i18n } from '@/i18n';
Chart.register(
ArcElement,

View File

@ -5,7 +5,7 @@
</div>
<div class="value">
<slot name="value"></slot>
<button v-if="copy" v-tooltip="$ts.copy" class="_textButton" style="margin-left: 0.5em;" @click="copy_"><i class="far fa-copy"></i></button>
<button v-if="copy" v-tooltip="i18n.ts.copy" class="_textButton" style="margin-left: 0.5em;" @click="copy_"><i class="far fa-copy"></i></button>
</div>
</div>
</template>
@ -14,6 +14,7 @@
import { } from 'vue';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import * as os from '@/os';
import { i18n } from '@/i18n';
const props = withDefaults(defineProps<{
copy?: string | null;

View File

@ -14,7 +14,7 @@
<div v-if="isRenote" class="renote">
<MkAvatar class="avatar" :user="note.user"/>
<i class="fas fa-retweet"></i>
<I18n :src="$ts.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"/>
@ -54,7 +54,7 @@
</p>
<div v-show="appearNote.cw == null || showContent" class="content">
<div class="text">
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $ts.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>
@ -103,7 +103,7 @@
<MkNoteSub v-for="note in replies" :key="note.id" :note="note" class="reply" :detail="true"/>
</div>
<div v-else class="_panel muted" @click="muted = false">
<I18n :src="$ts.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"/>

View File

@ -3,7 +3,7 @@
<template #empty>
<div class="_fullinfo">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $ts.noNotes }}</div>
<div>{{ i18n.ts.noNotes }}</div>
</div>
</template>
@ -21,8 +21,8 @@
import { ref } from 'vue';
import XNote from '@/components/note.vue';
import XList from '@/components/date-separated-list.vue';
import MkPagination from '@/components/ui/pagination.vue';
import { Paging } from '@/components/ui/pagination.vue';
import MkPagination, { Paging } from '@/components/ui/pagination.vue';
import { i18n } from '@/i18n';
const props = defineProps<{
pagination: Paging;

View File

@ -61,10 +61,10 @@
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
<i class="fas fa-quote-right"></i>
</MkA>
<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ $ts.youGotNewFollower }}<div v-if="full"><MkFollowButton :user="notification.user" :full="true"/></div></span>
<span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">{{ $ts.followRequestAccepted }}</span>
<span v-if="notification.type === 'receiveFollowRequest'" class="text" style="opacity: 0.6;">{{ $ts.receiveFollowRequest }}<div v-if="full && !followRequestDone"><button class="_textButton" @click="acceptFollowRequest()">{{ $ts.accept }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ $ts.reject }}</button></div></span>
<span v-if="notification.type === 'groupInvited'" class="text" style="opacity: 0.6;">{{ $ts.groupInvited }}: <b>{{ notification.invitation.group.name }}</b><div v-if="full && !groupInviteDone"><button class="_textButton" @click="acceptGroupInvitation()">{{ $ts.accept }}</button> | <button class="_textButton" @click="rejectGroupInvitation()">{{ $ts.reject }}</button></div></span>
<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}<div v-if="full"><MkFollowButton :user="notification.user" :full="true"/></div></span>
<span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">{{ i18n.ts.followRequestAccepted }}</span>
<span v-if="notification.type === 'receiveFollowRequest'" class="text" style="opacity: 0.6;">{{ i18n.ts.receiveFollowRequest }}<div v-if="full && !followRequestDone"><button class="_textButton" @click="acceptFollowRequest()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ i18n.ts.reject }}</button></div></span>
<span v-if="notification.type === 'groupInvited'" class="text" style="opacity: 0.6;">{{ i18n.ts.groupInvited }}: <b>{{ notification.invitation.group.name }}</b><div v-if="full && !groupInviteDone"><button class="_textButton" @click="acceptGroupInvitation()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectGroupInvitation()">{{ i18n.ts.reject }}</button></div></span>
<span v-if="notification.type === 'app'" class="text">
<Mfm :text="notification.body" :nowrap="!full"/>
</span>

View File

@ -3,7 +3,7 @@
<template #empty>
<div class="_fullinfo">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $ts.noNotifications }}</div>
<div>{{ i18n.ts.noNotifications }}</div>
</div>
</template>
@ -26,6 +26,7 @@ import XNote from '@/components/note.vue';
import * as os from '@/os';
import { stream } from '@/stream';
import { $i } from '@/account';
import { i18n } from '@/i18n';
const props = defineProps<{
includeTypes?: typeof notificationTypes[number][];

View File

@ -1,7 +1,7 @@
<template>
<div class="zmdxowus">
<p v-if="choices.length < 2" class="caution">
<i class="fas fa-exclamation-triangle"></i>{{ $ts._poll.noOnlyOneChoice }}
<i class="fas fa-exclamation-triangle"></i>{{ i18n.ts._poll.noOnlyOneChoice }}
</p>
<ul>
<li v-for="(choice, i) in choices" :key="i">
@ -12,34 +12,34 @@
</button>
</li>
</ul>
<MkButton v-if="choices.length < 10" class="add" @click="add">{{ $ts.add }}</MkButton>
<MkButton v-else class="add" disabled>{{ $ts._poll.noMore }}</MkButton>
<MkSwitch v-model="multiple">{{ $ts._poll.canMultipleVote }}</MkSwitch>
<MkButton v-if="choices.length < 10" class="add" @click="add">{{ i18n.ts.add }}</MkButton>
<MkButton v-else class="add" disabled>{{ i18n.ts._poll.noMore }}</MkButton>
<MkSwitch v-model="multiple">{{ i18n.ts._poll.canMultipleVote }}</MkSwitch>
<section>
<div>
<MkSelect v-model="expiration" small>
<template #label>{{ $ts._poll.expiration }}</template>
<option value="infinite">{{ $ts._poll.infinite }}</option>
<option value="at">{{ $ts._poll.at }}</option>
<option value="after">{{ $ts._poll.after }}</option>
<template #label>{{ i18n.ts._poll.expiration }}</template>
<option value="infinite">{{ i18n.ts._poll.infinite }}</option>
<option value="at">{{ i18n.ts._poll.at }}</option>
<option value="after">{{ i18n.ts._poll.after }}</option>
</MkSelect>
<section v-if="expiration === 'at'">
<MkInput v-model="atDate" small type="date" class="input">
<template #label>{{ $ts._poll.deadlineDate }}</template>
<template #label>{{ i18n.ts._poll.deadlineDate }}</template>
</MkInput>
<MkInput v-model="atTime" small type="time" class="input">
<template #label>{{ $ts._poll.deadlineTime }}</template>
<template #label>{{ i18n.ts._poll.deadlineTime }}</template>
</MkInput>
</section>
<section v-else-if="expiration === 'after'">
<MkInput v-model="after" small type="number" class="input">
<template #label>{{ $ts._poll.duration }}</template>
<template #label>{{ i18n.ts._poll.duration }}</template>
</MkInput>
<MkSelect v-model="unit" small>
<option value="second">{{ $ts._time.second }}</option>
<option value="minute">{{ $ts._time.minute }}</option>
<option value="hour">{{ $ts._time.hour }}</option>
<option value="day">{{ $ts._time.day }}</option>
<option value="second">{{ i18n.ts._time.second }}</option>
<option value="minute">{{ i18n.ts._time.minute }}</option>
<option value="hour">{{ i18n.ts._time.hour }}</option>
<option value="day">{{ i18n.ts._time.day }}</option>
</MkSelect>
</section>
</div>
@ -55,6 +55,7 @@ import MkSwitch from './form/switch.vue';
import MkButton from './ui/button.vue';
import { formatDateTimeString } from '@/scripts/format-time-string';
import { addTime } from '@/scripts/time';
import { i18n } from '@/i18n';
const props = defineProps<{
modelValue: {

View File

@ -13,97 +13,77 @@
<p v-if="!readOnly">
<span>{{ $t('_poll.totalVotes', { n: total }) }}</span>
<span> · </span>
<a v-if="!closed && !isVoted" @click="showResult = !showResult">{{ showResult ? $ts._poll.vote : $ts._poll.showResult }}</a>
<span v-if="isVoted">{{ $ts._poll.voted }}</span>
<span v-else-if="closed">{{ $ts._poll.closed }}</span>
<a v-if="!closed && !isVoted" @click="showResult = !showResult">{{ showResult ? i18n.ts._poll.vote : i18n.ts._poll.showResult }}</a>
<span v-if="isVoted">{{ i18n.ts._poll.voted }}</span>
<span v-else-if="closed">{{ i18n.ts._poll.closed }}</span>
<span v-if="remaining > 0"> · {{ timer }}</span>
</p>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, onUnmounted, ref, toRef } from 'vue';
<script lang="ts" setup>
import { computed, onUnmounted, ref, toRef } from 'vue';
import * as misskey from 'misskey-js';
import { sum } from '@/scripts/array';
import { pleaseLogin } from '@/scripts/please-login';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { useInterval } from '@/scripts/use-interval';
export default defineComponent({
props: {
note: {
type: Object,
required: true,
},
readOnly: {
type: Boolean,
required: false,
default: false,
},
},
const props = defineProps<{
note: misskey.entities.Note;
readOnly?: boolean;
}>();
setup(props) {
const remaining = ref(-1);
const remaining = ref(-1);
const total = computed(() => sum(props.note.poll.choices.map(x => x.votes)));
const closed = computed(() => remaining.value === 0);
const isVoted = computed(() => !props.note.poll.multiple && props.note.poll.choices.some(c => c.isVoted));
const timer = computed(() => i18n.t(
remaining.value >= 86400 ? '_poll.remainingDays' :
remaining.value >= 3600 ? '_poll.remainingHours' :
remaining.value >= 60 ? '_poll.remainingMinutes' : '_poll.remainingSeconds', {
s: Math.floor(remaining.value % 60),
m: Math.floor(remaining.value / 60) % 60,
h: Math.floor(remaining.value / 3600) % 24,
d: Math.floor(remaining.value / 86400),
}));
const total = computed(() => sum(props.note.poll.choices.map(x => x.votes)));
const closed = computed(() => remaining.value === 0);
const isVoted = computed(() => !props.note.poll.multiple && props.note.poll.choices.some(c => c.isVoted));
const timer = computed(() => i18n.t(
remaining.value >= 86400 ? '_poll.remainingDays' :
remaining.value >= 3600 ? '_poll.remainingHours' :
remaining.value >= 60 ? '_poll.remainingMinutes' : '_poll.remainingSeconds', {
s: Math.floor(remaining.value % 60),
m: Math.floor(remaining.value / 60) % 60,
h: Math.floor(remaining.value / 3600) % 24,
d: Math.floor(remaining.value / 86400),
}));
const showResult = ref(props.readOnly || isVoted.value);
const showResult = ref(props.readOnly || isVoted.value);
// 期限付きアンケート
if (props.note.poll.expiresAt) {
const tick = () => {
remaining.value = Math.floor(Math.max(new Date(props.note.poll.expiresAt).getTime() - Date.now(), 0) / 1000);
if (remaining.value === 0) {
showResult.value = true;
}
};
useInterval(tick, 3000, {
immediate: true,
afterMounted: false,
});
// 期限付きアンケート
if (props.note.poll.expiresAt) {
const tick = () => {
remaining.value = Math.floor(Math.max(new Date(props.note.poll.expiresAt).getTime() - Date.now(), 0) / 1000);
if (remaining.value === 0) {
showResult.value = true;
}
};
const vote = async (id) => {
pleaseLogin();
useInterval(tick, 3000, {
immediate: true,
afterMounted: false,
});
}
if (props.readOnly || closed.value || isVoted.value) return;
const vote = async (id) => {
pleaseLogin();
const { canceled } = await os.confirm({
type: 'question',
text: i18n.t('voteConfirm', { choice: props.note.poll.choices[id].text }),
});
if (canceled) return;
if (props.readOnly || closed.value || isVoted.value) return;
await os.api('notes/polls/vote', {
noteId: props.note.id,
choice: id,
});
if (!showResult.value) showResult.value = !props.note.poll.multiple;
};
const { canceled } = await os.confirm({
type: 'question',
text: i18n.t('voteConfirm', { choice: props.note.poll.choices[id].text }),
});
if (canceled) return;
return {
remaining,
showResult,
total,
isVoted,
closed,
timer,
vote,
};
},
});
await os.api('notes/polls/vote', {
noteId: props.note.id,
choice: id,
});
if (!showResult.value) showResult.value = !props.note.poll.multiple;
};
</script>
<style lang="scss" scoped>

View File

@ -1,8 +1,10 @@
<template>
<div class="jmgmzlwq _block"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i>{{ $ts.remoteUserCaution }}<a class="link" :href="href" rel="nofollow noopener" target="_blank">{{ $ts.showOnRemote }}</a></div>
<div class="jmgmzlwq _block"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i>{{ i18n.ts.remoteUserCaution }}<a class="link" :href="href" rel="nofollow noopener" target="_blank">{{ i18n.ts.showOnRemote }}</a></div>
</template>
<script lang="ts" setup>
import { i18n } from '@/i18n';
defineProps<{
href: string;
}>();

View File

@ -1,11 +1,12 @@
<template>
<XModalWindow ref="dialog"
<XModalWindow
ref="dialog"
:width="370"
:height="400"
@close="onClose"
@closed="emit('closed')"
>
<template #header>{{ $ts.login }}</template>
<template #header>{{ i18n.ts.login }}</template>
<MkSignin :auto-set="autoSet" :message="message" @login="onLogin"/>
</XModalWindow>
@ -13,15 +14,16 @@
<script lang="ts" setup>
import { } from 'vue';
import XModalWindow from '@/components/ui/modal-window.vue';
import MkSignin from './signin.vue';
import XModalWindow from '@/components/ui/modal-window.vue';
import { i18n } from '@/i18n';
const props = withDefaults(defineProps<{
autoSet?: boolean;
message?: string,
}>(), {
autoSet: false,
message: ''
message: '',
});
const emit = defineEmits<{

View File

@ -1,11 +1,12 @@
<template>
<XModalWindow ref="dialog"
<XModalWindow
ref="dialog"
:width="366"
:height="500"
@close="dialog.close()"
@closed="$emit('closed')"
>
<template #header>{{ $ts.signup }}</template>
<template #header>{{ i18n.ts.signup }}</template>
<div class="_monolithic_">
<div class="_section">
@ -17,8 +18,9 @@
<script lang="ts" setup>
import { } from 'vue';
import XModalWindow from '@/components/ui/modal-window.vue';
import XSignup from './signup.vue';
import XModalWindow from '@/components/ui/modal-window.vue';
import { i18n } from '@/i18n';
const props = withDefaults(defineProps<{
autoSet?: boolean;

View File

@ -1,65 +1,65 @@
<template>
<form class="qlvuhzng _formRoot" autocomplete="new-password" @submit.prevent="onSubmit">
<MkInput v-if="instance.disableRegistration" v-model="invitationCode" class="_formBlock" type="text" :spellcheck="false" required>
<template #label>{{ $ts.invitationCode }}</template>
<template #label>{{ i18n.ts.invitationCode }}</template>
<template #prefix><i class="fas fa-key"></i></template>
</MkInput>
<MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-signup-username @update:modelValue="onChangeUsername">
<template #label>{{ $ts.username }} <div v-tooltip:dialog="$ts.usernameInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
<template #label>{{ i18n.ts.username }} <div v-tooltip:dialog="i18n.ts.usernameInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
<template #caption>
<span v-if="usernameState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span>
<span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span>
<span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span>
<span v-else-if="usernameState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span>
<span v-else-if="usernameState === 'invalid-format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.usernameInvalidFormat }}</span>
<span v-else-if="usernameState === 'min-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooShort }}</span>
<span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span>
<span v-if="usernameState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ i18n.ts.checking }}</span>
<span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ i18n.ts.available }}</span>
<span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.unavailable }}</span>
<span v-else-if="usernameState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.error }}</span>
<span v-else-if="usernameState === 'invalid-format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.usernameInvalidFormat }}</span>
<span v-else-if="usernameState === 'min-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.tooShort }}</span>
<span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.tooLong }}</span>
</template>
</MkInput>
<MkInput v-if="instance.emailRequiredForSignup" v-model="email" class="_formBlock" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail">
<template #label>{{ $ts.emailAddress }} <div v-tooltip:dialog="$ts._signup.emailAddressInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
<template #label>{{ i18n.ts.emailAddress }} <div v-tooltip:dialog="i18n.ts._signup.emailAddressInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
<template #prefix><i class="fas fa-envelope"></i></template>
<template #caption>
<span v-if="emailState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span>
<span v-else-if="emailState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span>
<span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts._emailUnavailable.used }}</span>
<span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts._emailUnavailable.format }}</span>
<span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts._emailUnavailable.disposable }}</span>
<span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts._emailUnavailable.mx }}</span>
<span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts._emailUnavailable.smtp }}</span>
<span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span>
<span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span>
<span v-if="emailState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ i18n.ts.checking }}</span>
<span v-else-if="emailState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ i18n.ts.available }}</span>
<span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span>
<span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span>
<span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span>
<span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span>
<span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span>
<span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.unavailable }}</span>
<span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.error }}</span>
</template>
</MkInput>
<MkInput v-model="password" class="_formBlock" type="password" autocomplete="new-password" required data-cy-signup-password @update:modelValue="onChangePassword">
<template #label>{{ $ts.password }}</template>
<template #label>{{ i18n.ts.password }}</template>
<template #prefix><i class="fas fa-lock"></i></template>
<template #caption>
<span v-if="passwordStrength == 'low'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.weakPassword }}</span>
<span v-if="passwordStrength == 'medium'" style="color: var(--warn)"><i class="fas fa-check fa-fw"></i> {{ $ts.normalPassword }}</span>
<span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.strongPassword }}</span>
<span v-if="passwordStrength == 'low'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.weakPassword }}</span>
<span v-if="passwordStrength == 'medium'" style="color: var(--warn)"><i class="fas fa-check fa-fw"></i> {{ i18n.ts.normalPassword }}</span>
<span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ i18n.ts.strongPassword }}</span>
</template>
</MkInput>
<MkInput v-model="retypedPassword" class="_formBlock" type="password" autocomplete="new-password" required data-cy-signup-password-retype @update:modelValue="onChangePasswordRetype">
<template #label>{{ $ts.password }} ({{ $ts.retype }})</template>
<template #label>{{ i18n.ts.password }} ({{ i18n.ts.retype }})</template>
<template #prefix><i class="fas fa-lock"></i></template>
<template #caption>
<span v-if="passwordRetypeState == 'match'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.passwordMatched }}</span>
<span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.passwordNotMatched }}</span>
<span v-if="passwordRetypeState == 'match'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ i18n.ts.passwordMatched }}</span>
<span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.passwordNotMatched }}</span>
</template>
</MkInput>
<MkSwitch v-if="instance.tosUrl" v-model="ToSAgreement" class="_formBlock tou">
<I18n :src="$ts.agreeTo">
<I18n :src="i18n.ts.agreeTo">
<template #0>
<a :href="instance.tosUrl" class="_link" target="_blank">{{ $ts.tos }}</a>
<a :href="instance.tosUrl" class="_link" target="_blank">{{ i18n.ts.tos }}</a>
</template>
</I18n>
</MkSwitch>
<MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/>
<MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/>
<MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ $ts.start }}</MkButton>
<MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ i18n.ts.start }}</MkButton>
</form>
</template>

View File

@ -1,8 +1,8 @@
<template>
<div class="wrmlmaau" :class="{ collapsed }">
<div class="body">
<span v-if="note.isHidden" style="opacity: 0.5">({{ $ts.private }})</span>
<span v-if="note.deletedAt" style="opacity: 0.5">({{ $ts.deleted }})</span>
<span v-if="note.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
<span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deleted }})</span>
<MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="fas fa-reply"></i></MkA>
<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i" :custom-emojis="note.emojis"/>
<MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
@ -12,20 +12,21 @@
<XMediaList :media-list="note.files"/>
</details>
<details v-if="note.poll">
<summary>{{ $ts.poll }}</summary>
<summary>{{ i18n.ts.poll }}</summary>
<XPoll :note="note"/>
</details>
<button v-if="collapsed" class="fade _button" @click="collapsed = false">
<span>{{ $ts.showMore }}</span>
<span>{{ i18n.ts.showMore }}</span>
</button>
</div>
</template>
<script lang="ts" setup>
import { } from 'vue';
import * as misskey from 'misskey-js';
import XPoll from './poll.vue';
import XMediaList from './media-list.vue';
import * as misskey from 'misskey-js';
import { i18n } from '@/i18n';
const props = defineProps<{
note: misskey.entities.Note;

View File

@ -46,7 +46,7 @@
</button>
</template>
<span v-if="items2.length === 0" class="none item">
<span>{{ $ts.none }}</span>
<span>{{ i18n.ts.none }}</span>
</span>
</div>
<div v-if="childMenu" class="child">
@ -61,6 +61,8 @@ import { focusPrev, focusNext } from '@/scripts/focus';
import FormSwitch from '@/components/form/switch.vue';
import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from '@/types/menu';
import * as os from '@/os';
import { i18n } from '@/i18n';
const XChild = defineAsyncComponent(() => import('./menu.child.vue'));
const props = defineProps<{

View File

@ -8,7 +8,7 @@
<slot name="empty">
<div class="_fullinfo">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $ts.nothing }}</div>
<div>{{ i18n.ts.nothing }}</div>
</div>
</slot>
</div>
@ -16,14 +16,14 @@
<div v-else ref="rootEl">
<div v-show="pagination.reversed && more" key="_more_" class="cxiknjgy _gap">
<MkButton v-if="!moreFetching" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMoreAhead">
{{ $ts.loadMore }}
{{ i18n.ts.loadMore }}
</MkButton>
<MkLoading v-else class="loading"/>
</div>
<slot :items="items"></slot>
<div v-show="!pagination.reversed && more" key="_more_" class="cxiknjgy _gap">
<MkButton v-if="!moreFetching" v-appear="($store.state.enableInfiniteScroll && !disableAutoLoad) ? fetchMore : null" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMore">
{{ $ts.loadMore }}
{{ i18n.ts.loadMore }}
</MkButton>
<MkLoading v-else class="loading"/>
</div>
@ -37,6 +37,7 @@ import * as misskey from 'misskey-js';
import * as os from '@/os';
import { onScrollTop, isTopVisible, getScrollPosition, getScrollContainer } from '@/scripts/scroll';
import MkButton from '@/components/ui/button.vue';
import { i18n } from '@/i18n';
const SECOND_FETCH_LIMIT = 30;

View File

@ -1,10 +1,10 @@
<template>
<MkModal ref="modal" :z-priority="'middle'" @click="$refs.modal.close()" @closed="$emit('closed')">
<div class="ewlycnyt">
<div class="title"><MkSparkle>{{ $ts.misskeyUpdated }}</MkSparkle></div>
<div class="title"><MkSparkle>{{ i18n.ts.misskeyUpdated }}</MkSparkle></div>
<div class="version">{{ version }}🚀</div>
<MkButton full @click="whatIsNew">{{ $ts.whatIsNew }}</MkButton>
<MkButton class="gotIt" primary full @click="$refs.modal.close()">{{ $ts.gotIt }}</MkButton>
<MkButton full @click="whatIsNew">{{ i18n.ts.whatIsNew }}</MkButton>
<MkButton class="gotIt" primary full @click="$refs.modal.close()">{{ i18n.ts.gotIt }}</MkButton>
</div>
</MkModal>
</template>
@ -15,8 +15,9 @@ import MkModal from '@/components/ui/modal.vue';
import MkButton from '@/components/ui/button.vue';
import MkSparkle from '@/components/sparkle.vue';
import { version } from '@/config';
import { i18n } from '@/i18n';
const modal = ref();
const modal = ref<InstanceType<typeof MkModal>>();
const whatIsNew = () => {
modal.value.close();

View File

@ -1,6 +1,6 @@
<template>
<div v-if="playerEnabled" class="player" :style="`padding: ${(player.height || 0) / (player.width || 1) * 100}% 0 0`">
<button class="disablePlayer" :title="$ts.disablePlayer" @click="playerEnabled = false"><i class="fas fa-times"></i></button>
<button class="disablePlayer" :title="i18n.ts.disablePlayer" @click="playerEnabled = false"><i class="fas fa-times"></i></button>
<iframe :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" :width="player.width || '100%'" :heigth="player.height || 250" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen/>
</div>
<div v-else-if="tweetId && tweetExpanded" ref="twitter" class="twitter">
@ -10,7 +10,7 @@
<transition :name="$store.state.animation ? 'zoom' : ''" mode="out-in">
<component :is="self ? 'MkA' : 'a'" v-if="!fetching" class="link" :class="{ compact }" :[attr]="self ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url">
<div v-if="thumbnail" class="thumbnail" :style="`background-image: url('${thumbnail}')`">
<button v-if="!playerEnabled && player.url" class="_button" :title="$ts.enablePlayer" @click.prevent="playerEnabled = true"><i class="fas fa-play-circle"></i></button>
<button v-if="!playerEnabled && player.url" class="_button" :title="i18n.ts.enablePlayer" @click.prevent="playerEnabled = true"><i class="fas fa-play-circle"></i></button>
</div>
<article>
<header>
@ -26,7 +26,7 @@
</transition>
<div v-if="tweetId" class="expandTweet">
<a @click="tweetExpanded = true">
<i class="fab fa-twitter"></i> {{ $ts.expandTweet }}
<i class="fab fa-twitter"></i> {{ i18n.ts.expandTweet }}
</a>
</div>
</div>
@ -35,6 +35,7 @@
<script lang="ts" setup>
import { onMounted, onUnmounted } from 'vue';
import { url as local, lang } from '@/config';
import { i18n } from '@/i18n';
const props = withDefaults(defineProps<{
url: string;

View File

@ -10,17 +10,17 @@
<div v-if="user.description" class="mfm">
<Mfm :text="user.description" :author="user" :i="$i" :custom-emojis="user.emojis"/>
</div>
<span v-else style="opacity: 0.7;">{{ $ts.noAccountDescription }}</span>
<span v-else style="opacity: 0.7;">{{ i18n.ts.noAccountDescription }}</span>
</div>
<div class="status">
<div>
<p>{{ $ts.notes }}</p><span>{{ user.notesCount }}</span>
<p>{{ i18n.ts.notes }}</p><span>{{ user.notesCount }}</span>
</div>
<div>
<p>{{ $ts.following }}</p><span>{{ user.followingCount }}</span>
<p>{{ i18n.ts.following }}</p><span>{{ user.followingCount }}</span>
</div>
<div>
<p>{{ $ts.followers }}</p><span>{{ user.followersCount }}</span>
<p>{{ i18n.ts.followers }}</p><span>{{ user.followersCount }}</span>
</div>
</div>
<MkFollowButton v-if="$i && user.id != $i.id" class="koudoku-button" :user="user" mini/>
@ -31,6 +31,7 @@
import * as misskey from 'misskey-js';
import MkFollowButton from './follow-button.vue';
import { userPage } from '@/filters/user';
import { i18n } from '@/i18n';
defineProps<{
user: misskey.entities.UserDetailed;

View File

@ -3,7 +3,7 @@
<template #empty>
<div class="_fullinfo">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $ts.noUsers }}</div>
<div>{{ i18n.ts.noUsers }}</div>
</div>
</template>
@ -18,9 +18,9 @@
<script lang="ts" setup>
import { ref } from 'vue';
import MkUserInfo from '@/components/user-info.vue';
import MkPagination from '@/components/ui/pagination.vue';
import { Paging } from '@/components/ui/pagination.vue';
import MkPagination, { Paging } from '@/components/ui/pagination.vue';
import { userPage } from '@/filters/user';
import { i18n } from '@/i18n';
const props = defineProps<{
pagination: Paging;

View File

@ -1,5 +1,6 @@
<template>
<XModalWindow ref="dialogEl"
<XModalWindow
ref="dialogEl"
:with-ok-button="true"
:ok-button-disabled="selected == null"
@click="cancel()"
@ -7,16 +8,16 @@
@ok="ok()"
@closed="$emit('closed')"
>
<template #header>{{ $ts.selectUser }}</template>
<template #header>{{ i18n.ts.selectUser }}</template>
<div class="tbhwbxda">
<div class="form">
<FormSplit :min-width="170">
<MkInput v-model="username" :autofocus="true" @update:modelValue="search">
<template #label>{{ $ts.username }}</template>
<template #label>{{ i18n.ts.username }}</template>
<template #prefix>@</template>
</MkInput>
<MkInput v-model="host" @update:modelValue="search">
<template #label>{{ $ts.host }}</template>
<template #label>{{ i18n.ts.host }}</template>
<template #prefix>@</template>
</MkInput>
</FormSplit>
@ -32,7 +33,7 @@
</div>
</div>
<div v-else class="empty">
<span>{{ $ts.noUsers }}</span>
<span>{{ i18n.ts.noUsers }}</span>
</div>
</div>
<div v-if="username == '' && host == ''" class="recent">
@ -58,6 +59,7 @@ import FormSplit from '@/components/form/split.vue';
import XModalWindow from '@/components/ui/modal-window.vue';
import * as os from '@/os';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
const emit = defineEmits<{
(ev: 'ok', selected: misskey.entities.UserDetailed): void;
@ -81,7 +83,7 @@ const search = () => {
username: username,
host: host,
limit: 10,
detail: false
detail: false,
}).then(_users => {
users = _users;
});

View File

@ -4,37 +4,37 @@
<button key="public" class="_button" :class="{ active: v === 'public' }" data-index="1" @click="choose('public')">
<div><i class="fas fa-globe"></i></div>
<div>
<span>{{ $ts._visibility.public }}</span>
<span>{{ $ts._visibility.publicDescription }}</span>
<span>{{ i18n.ts._visibility.public }}</span>
<span>{{ i18n.ts._visibility.publicDescription }}</span>
</div>
</button>
<button key="home" class="_button" :class="{ active: v === 'home' }" data-index="2" @click="choose('home')">
<div><i class="fas fa-home"></i></div>
<div>
<span>{{ $ts._visibility.home }}</span>
<span>{{ $ts._visibility.homeDescription }}</span>
<span>{{ i18n.ts._visibility.home }}</span>
<span>{{ i18n.ts._visibility.homeDescription }}</span>
</div>
</button>
<button key="followers" class="_button" :class="{ active: v === 'followers' }" data-index="3" @click="choose('followers')">
<div><i class="fas fa-unlock"></i></div>
<div>
<span>{{ $ts._visibility.followers }}</span>
<span>{{ $ts._visibility.followersDescription }}</span>
<span>{{ i18n.ts._visibility.followers }}</span>
<span>{{ i18n.ts._visibility.followersDescription }}</span>
</div>
</button>
<button key="specified" :disabled="localOnly" class="_button" :class="{ active: v === 'specified' }" data-index="4" @click="choose('specified')">
<div><i class="fas fa-envelope"></i></div>
<div>
<span>{{ $ts._visibility.specified }}</span>
<span>{{ $ts._visibility.specifiedDescription }}</span>
<span>{{ i18n.ts._visibility.specified }}</span>
<span>{{ i18n.ts._visibility.specifiedDescription }}</span>
</div>
</button>
<div class="divider"></div>
<button key="localOnly" class="_button localOnly" :class="{ active: localOnly }" data-index="5" @click="localOnly = !localOnly">
<div><i class="fas fa-biohazard"></i></div>
<div>
<span>{{ $ts._visibility.localOnly }}</span>
<span>{{ $ts._visibility.localOnlyDescription }}</span>
<span>{{ i18n.ts._visibility.localOnly }}</span>
<span>{{ i18n.ts._visibility.localOnlyDescription }}</span>
</div>
<div><i :class="localOnly ? 'fas fa-toggle-on' : 'fas fa-toggle-off'"></i></div>
</button>
@ -46,6 +46,7 @@
import { nextTick, watch } from 'vue';
import * as misskey from 'misskey-js';
import MkModal from '@/components/ui/modal.vue';
import { i18n } from '@/i18n';
const modal = $ref<InstanceType<typeof MkModal>>();

View File

@ -3,11 +3,11 @@
<template v-if="edit">
<header>
<MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)" class="mk-widget-select">
<template #label>{{ $ts.selectWidget }}</template>
<template #label>{{ i18n.ts.selectWidget }}</template>
<option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ i18n.t(`_widgets.${widget}`) }}</option>
</MkSelect>
<MkButton inline primary class="mk-widget-add" @click="addWidget"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
<MkButton inline @click="$emit('exit')">{{ $ts.close }}</MkButton>
<MkButton inline primary class="mk-widget-add" @click="addWidget"><i class="fas fa-plus"></i> {{ i18n.ts.add }}</MkButton>
<MkButton inline @click="$emit('exit')">{{ i18n.ts.close }}</MkButton>
</header>
<XDraggable
v-model="widgets_"