diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2024-11-11 16:35:23 +0900 |
|---|---|---|
| committer | syuilo <4439005+syuilo@users.noreply.github.com> | 2024-11-11 16:35:23 +0900 |
| commit | 1bc4f400c0efc4a9430a7bbb1a7268aff2a6c455 (patch) | |
| tree | 486455bb779a168fd06e2c4758489fb51eb8b865 /packages/frontend/src | |
| parent | Update about-misskey.vue (diff) | |
| parent | Update CHANGELOG.md (diff) | |
| download | misskey-1bc4f400c0efc4a9430a7bbb1a7268aff2a6c455.tar.gz misskey-1bc4f400c0efc4a9430a7bbb1a7268aff2a6c455.tar.bz2 misskey-1bc4f400c0efc4a9430a7bbb1a7268aff2a6c455.zip | |
Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop
Diffstat (limited to 'packages/frontend/src')
| -rw-r--r-- | packages/frontend/src/boot/common.ts | 6 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkMediaVideo.vue | 43 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkNote.vue | 15 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkPostForm.vue | 6 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkSignupDialog.form.vue | 4 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkTimeline.vue | 1 | ||||
| -rw-r--r-- | packages/frontend/src/pages/announcement.vue | 2 | ||||
| -rw-r--r-- | packages/frontend/src/pages/auth.vue | 2 | ||||
| -rw-r--r-- | packages/frontend/src/pages/miauth.vue | 2 | ||||
| -rw-r--r-- | packages/frontend/src/pages/timeline.vue | 2 | ||||
| -rw-r--r-- | packages/frontend/src/pizzax.ts | 12 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/device-kind.ts | 24 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/fullscreen.ts | 46 | ||||
| -rw-r--r-- | packages/frontend/src/store.ts | 9 |
14 files changed, 116 insertions, 58 deletions
diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index 90ae49ee59..bfe5c4f5f7 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -15,7 +15,7 @@ import { updateI18n, i18n } from '@/i18n.js'; import { $i, refreshAccount, login } from '@/account.js'; import { defaultStore, ColdDeviceStorage } from '@/store.js'; import { fetchInstance, instance } from '@/instance.js'; -import { deviceKind } from '@/scripts/device-kind.js'; +import { deviceKind, updateDeviceKind } from '@/scripts/device-kind.js'; import { reloadChannel } from '@/scripts/unison-reload.js'; import { getUrlWithoutLoginId } from '@/scripts/login-id.js'; import { getAccountFromId } from '@/scripts/get-account-from-id.js'; @@ -185,6 +185,10 @@ export async function common(createVue: () => App<Element>) { } }); + watch(defaultStore.reactiveState.overridedDeviceKind, (kind) => { + updateDeviceKind(kind); + }, { immediate: true }); + watch(defaultStore.reactiveState.useBlurEffectForModal, v => { document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none'); }, { immediate: true }); diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue index d3a12ca734..65e4a1eb12 100644 --- a/packages/frontend/src/components/MkMediaVideo.vue +++ b/packages/frontend/src/components/MkMediaVideo.vue @@ -118,7 +118,7 @@ import { hms } from '@/filters/hms.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; -import { isFullscreenNotSupported } from '@/scripts/device-kind.js'; +import { exitFullscreen, requestFullscreen } from '@/scripts/fullscreen.js'; import hasAudio from '@/scripts/media-has-audio.js'; import MkMediaRange from '@/components/MkMediaRange.vue'; import { $i, iAmModerator } from '@/account.js'; @@ -334,26 +334,21 @@ function togglePlayPause() { } function toggleFullscreen() { - if (isFullscreenNotSupported && videoEl.value) { - if (isFullscreen.value) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - videoEl.value.webkitExitFullscreen(); - isFullscreen.value = false; - } else { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - videoEl.value.webkitEnterFullscreen(); - isFullscreen.value = true; - } - } else if (playerEl.value) { - if (isFullscreen.value) { - document.exitFullscreen(); - isFullscreen.value = false; - } else { - playerEl.value.requestFullscreen({ navigationUI: 'hide' }); - isFullscreen.value = true; - } + if (playerEl.value == null || videoEl.value == null) return; + if (isFullscreen.value) { + exitFullscreen({ + videoEl: videoEl.value, + }); + isFullscreen.value = false; + } else { + requestFullscreen({ + videoEl: videoEl.value, + playerEl: playerEl.value, + options: { + navigationUI: 'hide', + }, + }); + isFullscreen.value = true; } } @@ -454,8 +449,10 @@ watch(loop, (to) => { }); watch(hide, (to) => { - if (to && isFullscreen.value) { - document.exitFullscreen(); + if (videoEl.value && to && isFullscreen.value) { + exitFullscreen({ + videoEl: videoEl.value, + }); isFullscreen.value = false; } }); diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 3de69d6d09..cf0d0787b1 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -292,15 +292,18 @@ function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: false): boolean | 'sensitiveMute'; */ function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly = false): boolean | 'sensitiveMute' { - if (mutedWords == null) return false; - - if (checkWordMute(noteToCheck, $i, mutedWords)) return true; - if (noteToCheck.reply && checkWordMute(noteToCheck.reply, $i, mutedWords)) return true; - if (noteToCheck.renote && checkWordMute(noteToCheck.renote, $i, mutedWords)) return true; + if (mutedWords != null) { + if (checkWordMute(noteToCheck, $i, mutedWords)) return true; + if (noteToCheck.reply && checkWordMute(noteToCheck.reply, $i, mutedWords)) return true; + if (noteToCheck.renote && checkWordMute(noteToCheck.renote, $i, mutedWords)) return true; + } if (checkOnly) return false; - if (inTimeline && !tl_withSensitive.value && noteToCheck.files?.some((v) => v.isSensitive)) return 'sensitiveMute'; + if (inTimeline && tl_withSensitive.value === false && noteToCheck.files?.some((v) => v.isSensitive)) { + return 'sensitiveMute'; + } + return false; } diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index f2fe048449..0b5794d1e3 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -1108,7 +1108,7 @@ defineExpose({ &:focus-visible { outline: none; - .submitInner { + > .submitInner { outline: 2px solid var(--MI_THEME-fgOnAccent); outline-offset: -4px; } @@ -1123,13 +1123,13 @@ defineExpose({ } &:not(:disabled):hover { - > .inner { + > .submitInner { background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5))); } } &:not(:disabled):active { - > .inner { + > .submitInner { background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5))); } } diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index 3d1c44fc90..e1f4e26d62 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -277,7 +277,7 @@ async function onSubmit(): Promise<void> { return null; }); - if (res) { + if (res && res.ok) { if (res.status === 204 || instance.emailRequiredForSignup) { os.alert({ type: 'success', @@ -295,6 +295,8 @@ async function onSubmit(): Promise<void> { await login(resJson.token); } } + } else { + onSignupApiError(); } submitting.value = false; diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 226faac291..fb8eb4ae37 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -43,6 +43,7 @@ const props = withDefaults(defineProps<{ }>(), { withRenotes: true, withReplies: false, + withSensitive: true, onlyFiles: false, }); diff --git a/packages/frontend/src/pages/announcement.vue b/packages/frontend/src/pages/announcement.vue index 01c29cf02d..56c10fb292 100644 --- a/packages/frontend/src/pages/announcement.vue +++ b/packages/frontend/src/pages/announcement.vue @@ -103,7 +103,7 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); definePageMetadata(() => ({ - title: announcement.value ? `${i18n.ts.announcements}: ${announcement.value.title}` : i18n.ts.announcements, + title: announcement.value ? announcement.value.title : i18n.ts.announcements, icon: 'ti ti-speakerphone', })); </script> diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue index d8f8d0b428..4170b4f73e 100644 --- a/packages/frontend/src/pages/auth.vue +++ b/packages/frontend/src/pages/auth.vue @@ -62,7 +62,7 @@ function accepted() { state.value = 'accepted'; if (session.value && session.value.app.callbackUrl) { const url = new URL(session.value.app.callbackUrl); - if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url'); + if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:', 'vbscript:'].includes(url.protocol)) throw new Error('invalid url'); location.href = `${session.value.app.callbackUrl}?token=${session.value.token}`; } } diff --git a/packages/frontend/src/pages/miauth.vue b/packages/frontend/src/pages/miauth.vue index e89dd5c4a5..e85d2c29c1 100644 --- a/packages/frontend/src/pages/miauth.vue +++ b/packages/frontend/src/pages/miauth.vue @@ -65,7 +65,7 @@ async function onAccept(token: string) { if (props.callback && props.callback !== '') { const cbUrl = new URL(props.callback); - if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(cbUrl.protocol)) throw new Error('invalid url'); + if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:', 'vbscript:'].includes(cbUrl.protocol)) throw new Error('invalid url'); cbUrl.searchParams.set('session', props.session); location.href = cbUrl.toString(); } else { diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 7a3195304b..044a1908ab 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.tl"> <MkTimeline ref="tlComponent" - :key="src + withRenotes + withReplies + onlyFiles" + :key="src + withRenotes + withReplies + onlyFiles + withSensitive" :src="src.split(':')[0]" :list="src.split(':')[1]" :withRenotes="withRenotes" diff --git a/packages/frontend/src/pizzax.ts b/packages/frontend/src/pizzax.ts index ac325e923f..7740fe0d39 100644 --- a/packages/frontend/src/pizzax.ts +++ b/packages/frontend/src/pizzax.ts @@ -241,9 +241,13 @@ export class Storage<T extends StateDef> { * 特定のキーの、簡易的なgetter/setterを作ります * 主にvue上で設定コントロールのmodelとして使う用 */ - public makeGetterSetter<K extends keyof T>(key: K, getter?: (v: T[K]) => unknown, setter?: (v: unknown) => T[K]): { - get: () => T[K]['default']; - set: (value: T[K]['default']) => void; + public makeGetterSetter<K extends keyof T, R = T[K]['default']>( + key: K, + getter?: (v: T[K]['default']) => R, + setter?: (v: R) => T[K]['default'], + ): { + get: () => R; + set: (value: R) => void; } { const valueRef = ref(this.state[key]); @@ -265,7 +269,7 @@ export class Storage<T extends StateDef> { return valueRef.value; } }, - set: (value: unknown) => { + set: (value) => { const val = setter ? setter(value) : value; this.set(key, val); valueRef.value = val; diff --git a/packages/frontend/src/scripts/device-kind.ts b/packages/frontend/src/scripts/device-kind.ts index 7c33f8ccee..7aadb617ca 100644 --- a/packages/frontend/src/scripts/device-kind.ts +++ b/packages/frontend/src/scripts/device-kind.ts @@ -3,22 +3,22 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { defaultStore } from '@/store.js'; - -await defaultStore.ready; +export type DeviceKind = 'smartphone' | 'tablet' | 'desktop'; const ua = navigator.userAgent.toLowerCase(); const isTablet = /ipad/.test(ua) || (/mobile|iphone|android/.test(ua) && window.innerWidth > 700); const isSmartphone = !isTablet && /mobile|iphone|android/.test(ua); -const isIPhone = /iphone|ipod/gi.test(ua) && navigator.maxTouchPoints > 1; -// navigator.platform may be deprecated but this check is still required -const isIPadOS = navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1; -const isIos = /ipad|iphone|ipod/gi.test(ua) && navigator.maxTouchPoints > 1; +export const DEFAULT_DEVICE_KIND: DeviceKind = ( + isSmartphone + ? 'smartphone' + : isTablet + ? 'tablet' + : 'desktop' +); -export const isFullscreenNotSupported = isIPhone || isIos; +export let deviceKind: DeviceKind = DEFAULT_DEVICE_KIND; -export const deviceKind: 'smartphone' | 'tablet' | 'desktop' = defaultStore.state.overridedDeviceKind ? defaultStore.state.overridedDeviceKind - : isSmartphone ? 'smartphone' - : isTablet ? 'tablet' - : 'desktop'; +export function updateDeviceKind(kind: DeviceKind | null) { + deviceKind = kind ?? DEFAULT_DEVICE_KIND; +} diff --git a/packages/frontend/src/scripts/fullscreen.ts b/packages/frontend/src/scripts/fullscreen.ts new file mode 100644 index 0000000000..7a0a018ef3 --- /dev/null +++ b/packages/frontend/src/scripts/fullscreen.ts @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +type PartiallyPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>; + +type VideoEl = PartiallyPartial<HTMLVideoElement, 'requestFullscreen'> & { + webkitEnterFullscreen?(): void; + webkitExitFullscreen?(): void; +}; + +type PlayerEl = PartiallyPartial<HTMLElement, 'requestFullscreen'>; + +type RequestFullscreenProps = { + readonly videoEl: VideoEl; + readonly playerEl: PlayerEl; + readonly options?: FullscreenOptions | null; +}; + +type ExitFullscreenProps = { + readonly videoEl: VideoEl; +}; + +export const requestFullscreen = ({ videoEl, playerEl, options }: RequestFullscreenProps) => { + if (playerEl.requestFullscreen != null) { + playerEl.requestFullscreen(options ?? undefined); + return; + } + if (videoEl.webkitEnterFullscreen != null) { + videoEl.webkitEnterFullscreen(); + return; + } +}; + +export const exitFullscreen = ({ videoEl }: ExitFullscreenProps) => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (document.exitFullscreen != null) { + document.exitFullscreen(); + return; + } + if (videoEl.webkitExitFullscreen != null) { + videoEl.webkitExitFullscreen(); + return; + } +}; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 911a463636..1d981e897b 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -8,8 +8,9 @@ import * as Misskey from 'misskey-js'; import { hemisphere } from '@@/js/intl-const.js'; import lightTheme from '@@/themes/l-light.json5'; import darkTheme from '@@/themes/d-green-lime.json5'; -import { miLocalStorage } from './local-storage.js'; import type { SoundType } from '@/scripts/sound.js'; +import { DEFAULT_DEVICE_KIND, type DeviceKind } from '@/scripts/device-kind.js'; +import { miLocalStorage } from '@/local-storage.js'; import { Storage } from '@/pizzax.js'; import type { Ast } from '@syuilo/aiscript'; @@ -207,7 +208,7 @@ export const defaultStore = markRaw(new Storage('base', { overridedDeviceKind: { where: 'device', - default: null as null | 'smartphone' | 'tablet' | 'desktop', + default: null as DeviceKind | null, }, serverDisconnectedBehavior: { where: 'device', @@ -263,11 +264,11 @@ export const defaultStore = markRaw(new Storage('base', { }, useBlurEffectForModal: { where: 'device', - default: !/mobile|iphone|android/.test(navigator.userAgent.toLowerCase()), // 循環参照するのでdevice-kind.tsは参照できない + default: DEFAULT_DEVICE_KIND === 'desktop', }, useBlurEffect: { where: 'device', - default: !/mobile|iphone|android/.test(navigator.userAgent.toLowerCase()), // 循環参照するのでdevice-kind.tsは参照できない + default: DEFAULT_DEVICE_KIND === 'desktop', }, showFixedPostForm: { where: 'device', |