diff options
| -rw-r--r-- | locales/en-US.yml | 1 | ||||
| -rw-r--r-- | locales/index.d.ts | 4 | ||||
| -rw-r--r-- | locales/ja-JP.yml | 1 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkDialog.vue | 3 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkLink.vue | 12 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkMediaBanner.vue | 4 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkNote.vue | 6 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkNoteDetailed.vue | 6 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkNoteSub.vue | 6 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkPostFormDialog.vue | 14 | ||||
| -rw-r--r-- | packages/frontend/src/components/SkNote.vue | 6 | ||||
| -rw-r--r-- | packages/frontend/src/components/SkNoteDetailed.vue | 6 | ||||
| -rw-r--r-- | packages/frontend/src/components/SkNoteSub.vue | 6 | ||||
| -rw-r--r-- | packages/frontend/src/os.ts | 7 | ||||
| -rw-r--r-- | packages/frontend/src/pages/drive.file.info.vue | 6 | ||||
| -rw-r--r-- | packages/frontend/src/plugin.ts | 15 | ||||
| -rw-r--r-- | packages/frontend/src/ui/classic.sidebar.vue | 1 |
17 files changed, 78 insertions, 26 deletions
diff --git a/locales/en-US.yml b/locales/en-US.yml index c18e1281df..13ddcc4b5c 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -291,6 +291,7 @@ removeAreYouSure: "Are you sure that you want to remove \"{x}\"?" deleteAreYouSure: "Are you sure that you want to delete \"{x}\"?" resetAreYouSure: "Really reset?" areYouSure: "Are you sure?" +confirmRemoteUrl: "Are you sure that you want to go to \"{x}\"?" saved: "Saved" messaging: "Chat" upload: "Upload" diff --git a/locales/index.d.ts b/locales/index.d.ts index 9f0378a23f..0239da7071 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1181,6 +1181,10 @@ export interface Locale extends ILocale { */ "areYouSure": string; /** + * 「{x}」を開きますか? + */ + "confirmRemoteUrl": ParameterizedString<"x">; + /** * 保存しました */ "saved": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 57fdeb461f..caed03910f 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -291,6 +291,7 @@ removeAreYouSure: "「{x}」を削除しますか?" deleteAreYouSure: "「{x}」を削除しますか?" resetAreYouSure: "リセットしますか?" areYouSure: "よろしいですか?" +confirmRemoteUrl: "「{x}」を開きますか?" saved: "保存しました" messaging: "チャット" upload: "アップロード" diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index 825c1d0513..7dc381b662 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkLoading v-else-if="type === 'waiting'" :class="$style.iconInner" :em="true"/> </div> <header v-if="title" :class="$style.title"><Mfm :text="title"/></header> - <div v-if="text" :class="$style.text"><Mfm :text="text" :isBlock="true" /></div> + <div v-if="text" :class="$style.text"><Mfm :text="text" :isBlock="true" :plain="plain" /></div> <MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown"> <template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template> <template #caption> @@ -105,6 +105,7 @@ const props = withDefaults(defineProps<{ cancelableByBgClick?: boolean; okText?: string; cancelText?: string; + plain?: boolean; }>(), { type: 'info', showOkButton: true, diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue index 07cf9e0c37..d2819f9f4c 100644 --- a/packages/frontend/src/components/MkLink.vue +++ b/packages/frontend/src/components/MkLink.vue @@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only :is="self ? 'MkA' : 'a'" ref="el" style="word-break: break-all;" class="_link" :[attr]="self ? url.substring(local.length) : url" :rel="rel ?? 'nofollow noopener'" :target="target" :behavior="props.navigationBehavior" :title="url" + @click.prevent="self ? true : promptConfirm()" @click.stop > <slot></slot> @@ -22,6 +23,7 @@ import { useTooltip } from '@/scripts/use-tooltip.js'; import * as os from '@/os.js'; import { isEnabledUrlPreview } from '@/instance.js'; import { MkABehavior } from '@/components/global/MkA.vue'; +import { i18n } from '@/i18n.js'; const props = withDefaults(defineProps<{ url: string; @@ -47,6 +49,16 @@ if (isEnabledUrlPreview.value) { }); }); } + +async function promptConfirm() { + const { canceled } = await os.confirm({ + type: 'question', + text: i18n.tsx.confirmRemoteUrl({ x: props.url }), + plain: true, + }); + if (canceled) return; + window.open(props.url, '_blank', 'nofollow noopener popup=false'); +} </script> <style lang="scss" module> diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue index ed8d43273f..77a86ff2fb 100644 --- a/packages/frontend/src/components/MkMediaBanner.vue +++ b/packages/frontend/src/components/MkMediaBanner.vue @@ -5,12 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div :class="$style.root"> - <div v-if="media.isSensitive && hide" :class="$style.sensitive" @click="show"> + <MkMediaAudio v-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/> + <div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="show"> <span style="font-size: 1.6em;"><i class="ti ti-alert-triangle"></i></span> <b>{{ i18n.ts.sensitive }}</b> <span>{{ i18n.ts.clickToShow }}</span> </div> - <MkMediaAudio v-else-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/> <a v-else :class="$style.download" :href="media.url" diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index e2f0a4e492..edae1e91b2 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -565,7 +565,8 @@ function quote() { os.post({ renote: appearNote.value, channel: appearNote.value.channel, - }).then(() => { + }).then((cancelled) => { + if (cancelled) return; misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i?.id, @@ -589,7 +590,8 @@ function quote() { } else { os.post({ renote: appearNote.value, - }).then(() => { + }).then((cancelled) => { + if (cancelled) return; misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i?.id, diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 64559ef265..123e94c3e0 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -550,7 +550,8 @@ function quote() { os.post({ renote: appearNote.value, channel: appearNote.value.channel, - }).then(() => { + }).then((cancelled) => { + if (cancelled) return; misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i?.id, @@ -574,7 +575,8 @@ function quote() { } else { os.post({ renote: appearNote.value, - }).then(() => { + }).then((cancelled) => { + if (cancelled) return; misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i?.id, diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue index 9caed62ce2..45276839ad 100644 --- a/packages/frontend/src/components/MkNoteSub.vue +++ b/packages/frontend/src/components/MkNoteSub.vue @@ -339,7 +339,8 @@ function quote() { os.post({ renote: appearNote.value, channel: appearNote.value.channel, - }).then(() => { + }).then((cancelled) => { + if (cancelled) return; misskeyApi('notes/renotes', { noteId: props.note.id, userId: $i.id, @@ -363,7 +364,8 @@ function quote() { } else { os.post({ renote: appearNote.value, - }).then(() => { + }).then((cancelled) => { + if (cancelled) return; misskeyApi('notes/renotes', { noteId: props.note.id, userId: $i.id, diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue index 947c0ee4d0..811a6378f2 100644 --- a/packages/frontend/src/components/MkPostFormDialog.vue +++ b/packages/frontend/src/components/MkPostFormDialog.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkModal ref="modal" :preferType="'dialog'" @click="modal?.close()" @closed="onModalClosed()" @esc="modal?.close()"> - <MkPostForm ref="form" :class="$style.form" v-bind="props" autofocus freezeAfterPosted @posted="onPosted" @cancel="modal?.close()" @esc="modal?.close()"/> + <MkPostForm ref="form" :class="$style.form" v-bind="props" autofocus freezeAfterPosted @posted="onPosted" @cancel="onCancel" @esc="onCancel"/> </MkModal> </template> @@ -37,7 +37,7 @@ const props = withDefaults(defineProps<{ }); const emit = defineEmits<{ - (ev: 'closed'): void; + (ev: 'closed', cancelled: boolean): void; }>(); const modal = shallowRef<InstanceType<typeof MkModal>>(); @@ -47,10 +47,18 @@ function onPosted() { modal.value?.close({ useSendAnimation: true, }); + emit('closed', false); +} + +function onCancel() { + // for some reason onModalClosed does not get called properly when closing the model through other functions. + modal.value?.close(); + // emit is required so that the dialog gets properly disposed otherwise it will float around as a "zombie" + emit('closed', true); } function onModalClosed() { - emit('closed'); + emit('closed', true); } </script> diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue index b02d902482..3d5c5f5fae 100644 --- a/packages/frontend/src/components/SkNote.vue +++ b/packages/frontend/src/components/SkNote.vue @@ -565,7 +565,8 @@ function quote() { os.post({ renote: appearNote.value, channel: appearNote.value.channel, - }).then(() => { + }).then((cancelled) => { + if (cancelled) return; misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i?.id, @@ -589,7 +590,8 @@ function quote() { } else { os.post({ renote: appearNote.value, - }).then(() => { + }).then((cancelled) => { + if (cancelled) return; misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i?.id, diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue index cca6c7a40c..5b85e21bac 100644 --- a/packages/frontend/src/components/SkNoteDetailed.vue +++ b/packages/frontend/src/components/SkNoteDetailed.vue @@ -559,7 +559,8 @@ function quote() { os.post({ renote: appearNote.value, channel: appearNote.value.channel, - }).then(() => { + }).then((cancelled) => { + if (cancelled) return; misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i?.id, @@ -583,7 +584,8 @@ function quote() { } else { os.post({ renote: appearNote.value, - }).then(() => { + }).then((cancelled) => { + if (cancelled) return; misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i?.id, diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue index c2986b2524..fac35191b9 100644 --- a/packages/frontend/src/components/SkNoteSub.vue +++ b/packages/frontend/src/components/SkNoteSub.vue @@ -353,7 +353,8 @@ function quote() { os.post({ renote: appearNote.value, channel: appearNote.value.channel, - }).then(() => { + }).then((cancelled) => { + if (cancelled) return; misskeyApi('notes/renotes', { noteId: props.note.id, userId: $i.id, @@ -377,7 +378,8 @@ function quote() { } else { os.post({ renote: appearNote.value, - }).then(() => { + }).then((cancelled) => { + if (cancelled) return; misskeyApi('notes/renotes', { noteId: props.note.id, userId: $i.id, diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index f6f4d62d50..bd96a0655e 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -245,6 +245,7 @@ export function confirm(props: { text?: string; okText?: string; cancelText?: string; + plain?: boolean; }): Promise<{ canceled: boolean }> { return new Promise(resolve => { const { dispose } = popup(MkDialog, { @@ -691,7 +692,7 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> { })); } -export function post(props: Record<string, any> = {}): Promise<void> { +export function post(props: Record<string, any> = {}): Promise<void | boolean> { pleaseLogin(undefined, (props.initialText || props.initialNote ? { type: 'share', params: { @@ -709,8 +710,8 @@ export function post(props: Record<string, any> = {}): Promise<void> { // 複数のpost formを開いたときに場合によってはエラーになる // もちろん複数のpost formを開けること自体Misskeyサイドのバグなのだが const { dispose } = popup(MkPostFormDialog, props, { - closed: () => { - resolve(); + closed: (cancelled) => { + resolve(cancelled); dispose(); }, }); diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue index 4ed2a67678..39e61aa218 100644 --- a/packages/frontend/src/pages/drive.file.info.vue +++ b/packages/frontend/src/pages/drive.file.info.vue @@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkKeyValue> </button> <button class="_button" :class="$style.kvEditBtn" @click="describe()"> - <MkKeyValue> + <MkKeyValue :class="$style.multiline"> <template #key>{{ i18n.ts.description }}</template> <template #value>{{ file.comment ? file.comment : `(${i18n.ts.none})` }}<i class="ti ti-pencil" :class="$style.kvEditIcon"></i></template> </MkKeyValue> @@ -313,6 +313,10 @@ onMounted(async () => { padding: .5rem 1rem; } +.multiline { + white-space: pre-wrap; +} + .kvEditBtn { text-align: start; display: block; diff --git a/packages/frontend/src/plugin.ts b/packages/frontend/src/plugin.ts index 81233a5a5e..9640c988eb 100644 --- a/packages/frontend/src/plugin.ts +++ b/packages/frontend/src/plugin.ts @@ -6,7 +6,8 @@ import { ref } from 'vue'; import { Interpreter, Parser, utils, values } from '@syuilo/aiscript'; import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js'; -import { inputText } from '@/os.js'; +import * as os from '@/os.js'; +import { i18n } from '@/i18n.js'; import { Plugin, noteActions, notePostInterruptors, noteViewInterruptors, postFormActions, userActions, pageViewInterruptors } from '@/store.js'; const parser = new Parser(); @@ -91,8 +92,16 @@ function createPluginEnv(opts: { plugin: Plugin; storageKey: string }): Record<s registerPageViewInterruptor({ pluginId: opts.plugin.id, handler }); }), 'Plugin:open_url': values.FN_NATIVE(([url]) => { - utils.assertString(url); - window.open(url.value, '_blank', 'noopener'); + (async () => { + utils.assertString(url); + const { canceled } = await os.confirm({ + type: 'question', + text: i18n.tsx.confirmRemoteUrl({ x: url.value }), + plain: true, + }); + if (canceled) return; + window.open(url.value, '_blank', 'noopener'); + })(); }), 'Plugin:config': values.OBJ(config), }; diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue index d1cc10558e..dbb7590978 100644 --- a/packages/frontend/src/ui/classic.sidebar.vue +++ b/packages/frontend/src/ui/classic.sidebar.vue @@ -166,7 +166,6 @@ watch(defaultStore.reactiveState.menuDisplay, () => { top: 0; z-index: 1; padding: 16px 0; - background: var(--bg); > .button { min-width: 0; |