diff options
| author | かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> | 2024-07-14 15:27:52 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-14 15:27:52 +0900 |
| commit | 3c032dd5b917c97bf8fb1b87a69f36d56537f493 (patch) | |
| tree | dacd9e664ed11cc77a351a81d48c52c6f7116c61 /packages/frontend/src/components | |
| parent | enhance(frontend): サーバー情報・お問い合わせページを改修 ... (diff) | |
| download | sharkey-3c032dd5b917c97bf8fb1b87a69f36d56537f493.tar.gz sharkey-3c032dd5b917c97bf8fb1b87a69f36d56537f493.tar.bz2 sharkey-3c032dd5b917c97bf8fb1b87a69f36d56537f493.zip | |
enhance: 非ログイン時には別サーバーに遷移できるように (#13089)
* enhance: 非ログイン時にはMisskey Hub経由で別サーバーに遷移できるように
* fix
* サーバーサイド照会を削除
* クライアント側の照会動作
* hubを経由せずにリモートで続行できるように
* fix と pleaseLogin誘導箇所の追加
* fix
* fix
* Update CHANGELOG.md
---------
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages/frontend/src/components')
| -rw-r--r-- | packages/frontend/src/components/MkFollowButton.vue | 6 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkNote.vue | 14 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkNoteDetailed.vue | 14 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkPoll.vue | 8 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkSignin.vue | 133 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkSigninDialog.vue | 9 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkUserInfo.vue | 2 |
7 files changed, 153 insertions, 33 deletions
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index ea76950c0d..d8ac8024b4 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -42,6 +42,8 @@ import { misskeyApi } from '@/scripts/misskey-api.js'; import { useStream } from '@/stream.js'; import { i18n } from '@/i18n.js'; import { claimAchievement } from '@/scripts/achievements.js'; +import { pleaseLogin } from '@/scripts/please-login.js'; +import { host } from '@/config.js'; import { $i } from '@/account.js'; import { defaultStore } from '@/store.js'; @@ -63,7 +65,7 @@ const hasPendingFollowRequestFromYou = ref(props.user.hasPendingFollowRequestFro const wait = ref(false); const connection = useStream().useChannel('main'); -if (props.user.isFollowing == null) { +if (props.user.isFollowing == null && $i) { misskeyApi('users/show', { userId: props.user.id, }) @@ -78,6 +80,8 @@ function onFollowChange(user: Misskey.entities.UserDetailed) { } async function onClick() { + pleaseLogin(undefined, { type: 'web', path: `/@${props.user.username}@${props.user.host ?? host}` }); + wait.value = true; try { diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 420ff2c651..c518c7dd41 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -196,6 +196,7 @@ import { MenuItem } from '@/types/menu.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import { shouldCollapsed } from '@/scripts/collapsed.js'; +import { host } from '@/config.js'; import { isEnabledUrlPreview } from '@/instance.js'; import { type Keymap } from '@/scripts/hotkey.js'; import { focusPrev, focusNext } from '@/scripts/focus.js'; @@ -278,6 +279,11 @@ const renoteCollapsed = ref( ), ); +const pleaseLoginContext = { + type: 'lookup', + path: `https://${host}/notes/${appearNote.value.id}`, +} as const; + /* Overload FunctionにLintが対応していないのでコメントアウト function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean; function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: false): boolean | 'sensitiveMute'; @@ -411,7 +417,7 @@ if (!props.mock) { } function renote(viaKeyboard = false) { - pleaseLogin(); + pleaseLogin(undefined, pleaseLoginContext); showMovedDialog(); const { menu } = getRenoteMenu({ note: note.value, renoteButton, mock: props.mock }); @@ -421,7 +427,7 @@ function renote(viaKeyboard = false) { } function reply(): void { - pleaseLogin(); + pleaseLogin(undefined, pleaseLoginContext); if (props.mock) { return; } @@ -434,7 +440,7 @@ function reply(): void { } function react(): void { - pleaseLogin(); + pleaseLogin(undefined, pleaseLoginContext); showMovedDialog(); if (appearNote.value.reactionAcceptance === 'likeOnly') { sound.playMisskeySfx('reaction'); @@ -565,7 +571,7 @@ function showRenoteMenu(): void { } if (isMyRenote) { - pleaseLogin(); + pleaseLogin(undefined, pleaseLoginContext); os.popupMenu([ getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote), { type: 'divider' }, diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index a8fed56c39..737e9a853a 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -222,6 +222,7 @@ import { reactionPicker } from '@/scripts/reaction-picker.js'; import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js'; import { $i } from '@/account.js'; import { i18n } from '@/i18n.js'; +import { host } from '@/config.js'; import { getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/scripts/get-note-menu.js'; import { useNoteCapture } from '@/scripts/use-note-capture.js'; import { deepClone } from '@/scripts/clone.js'; @@ -296,6 +297,11 @@ const conversation = ref<Misskey.entities.Note[]>([]); const replies = ref<Misskey.entities.Note[]>([]); const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || appearNote.value.userId === $i?.id); +const pleaseLoginContext = { + type: 'lookup', + path: `https://${host}/notes/${appearNote.value.id}`, +} as const; + const keymap = { 'r': () => reply(), 'e|a|plus': () => react(), @@ -396,7 +402,7 @@ if (appearNote.value.reactionAcceptance === 'likeOnly') { } function renote() { - pleaseLogin(); + pleaseLogin(undefined, pleaseLoginContext); showMovedDialog(); const { menu } = getRenoteMenu({ note: note.value, renoteButton }); @@ -404,7 +410,7 @@ function renote() { } function reply(): void { - pleaseLogin(); + pleaseLogin(undefined, pleaseLoginContext); showMovedDialog(); os.post({ reply: appearNote.value, @@ -415,7 +421,7 @@ function reply(): void { } function react(): void { - pleaseLogin(); + pleaseLogin(undefined, pleaseLoginContext); showMovedDialog(); if (appearNote.value.reactionAcceptance === 'likeOnly') { sound.playMisskeySfx('reaction'); @@ -499,7 +505,7 @@ async function clip(): Promise<void> { function showRenoteMenu(): void { if (!isMyRenote) return; - pleaseLogin(); + pleaseLogin(undefined, pleaseLoginContext); os.popupMenu([{ text: i18n.ts.unrenote, icon: 'ti ti-trash', diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue index a98690f1c3..82e2a605f1 100644 --- a/packages/frontend/src/components/MkPoll.vue +++ b/packages/frontend/src/components/MkPoll.vue @@ -34,6 +34,7 @@ import { pleaseLogin } from '@/scripts/please-login.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; +import { host } from '@/config.js'; import { useInterval } from '@/scripts/use-interval.js'; const props = defineProps<{ @@ -60,6 +61,11 @@ const timer = computed(() => i18n.tsx._poll[ const showResult = ref(props.readOnly || isVoted.value); +const pleaseLoginContext = { + type: 'lookup', + path: `https://${host}/notes/${props.note.id}`, +} as const; + // 期限付きアンケート if (props.poll.expiresAt) { const tick = () => { @@ -76,7 +82,7 @@ if (props.poll.expiresAt) { } const vote = async (id) => { - pleaseLogin(); + pleaseLogin(undefined, pleaseLoginContext); if (props.readOnly || closed.value || isVoted.value) return; diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue index db32cdd6a1..746ddd7154 100644 --- a/packages/frontend/src/components/MkSignin.vue +++ b/packages/frontend/src/components/MkSignin.vue @@ -6,10 +6,23 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <form :class="{ signing, totpLogin }" @submit.prevent="onSubmit"> <div class="_gaps_m"> - <div v-show="withAvatar" :class="$style.avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : undefined, marginBottom: message ? '1.5em' : undefined }"></div> + <div v-show="withAvatar" :class="$style.avatar" :style="{ backgroundImage: user ? `url('${user.avatarUrl}')` : undefined, marginBottom: message ? '1.5em' : undefined }"></div> <MkInfo v-if="message"> {{ message }} </MkInfo> + <div v-if="openOnRemote" class="_gaps_m"> + <div class="_gaps_s"> + <MkButton type="button" rounded primary style="margin: 0 auto;" @click="openRemote(openOnRemote)"> + {{ i18n.ts.continueOnRemote }} <i class="ti ti-external-link"></i> + </MkButton> + <button type="button" class="_button" :class="$style.instanceManualSelectButton" @click="specifyHostAndOpenRemote(openOnRemote)"> + {{ i18n.ts.specifyServerHost }} + </button> + </div> + <div :class="$style.orHr"> + <p :class="$style.orMsg">{{ i18n.ts.or }}</p> + </div> + </div> <div v-if="!totpLogin" class="normal-signin _gaps_m"> <MkInput v-model="username" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autocomplete="username webauthn" autofocus required data-cy-signin-username @update:modelValue="onUsernameChange"> <template #prefix>@</template> @@ -28,8 +41,8 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.retry }} </MkButton> </div> - <div v-if="user && user.securityKeys" class="or-hr"> - <p class="or-msg">{{ i18n.ts.or }}</p> + <div v-if="user && user.securityKeys" :class="$style.orHr"> + <p :class="$style.orMsg">{{ i18n.ts.or }}</p> </div> <div class="twofa-group totp-group _gaps"> <MkInput v-if="user && user.usePasswordLessLogin" v-model="password" type="password" autocomplete="current-password" :withPasswordToggle="true" required> @@ -53,6 +66,7 @@ import { defineAsyncComponent, ref } from 'vue'; import { toUnicode } from 'punycode/'; import * as Misskey from 'misskey-js'; import { supported as webAuthnSupported, get as webAuthnRequest, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill'; +import type { OpenOnRemoteOptions } from '@/scripts/please-login.js'; import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; @@ -60,6 +74,7 @@ import MkInfo from '@/components/MkInfo.vue'; import { host as configHost } from '@/config.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; +import { query, extractDomain } from '@/scripts/url.js'; import { login } from '@/account.js'; import { i18n } from '@/i18n.js'; @@ -78,22 +93,16 @@ const emit = defineEmits<{ (ev: 'login', v: any): void; }>(); -const props = defineProps({ - withAvatar: { - type: Boolean, - required: false, - default: true, - }, - autoSet: { - type: Boolean, - required: false, - default: false, - }, - message: { - type: String, - required: false, - default: '', - }, +const props = withDefaults(defineProps<{ + withAvatar?: boolean; + autoSet?: boolean; + message?: string, + openOnRemote?: OpenOnRemoteOptions, +}>(), { + withAvatar: true, + autoSet: false, + message: '', + openOnRemote: undefined, }); function onUsernameChange(): void { @@ -222,6 +231,60 @@ function resetPassword(): void { closed: () => dispose(), }); } + +function openRemote(options: OpenOnRemoteOptions, targetHost?: string): void { + switch (options.type) { + case 'web': + case 'lookup': { + let _path = options.path; + + if (options.type === 'lookup') { + // TODO: v2024.2.0以降が浸透してきたら正式なURLに変更する▼ + // _path = `/lookup?uri=${encodeURIComponent(_path)}`; + _path = `/authorize-follow?acct=${encodeURIComponent(_path)}`; + } + + if (targetHost) { + window.open(`https://${targetHost}${_path}`, '_blank', 'noopener'); + } else { + window.open(`https://misskey-hub.net/mi-web/?path=${encodeURIComponent(_path)}`, '_blank', 'noopener'); + } + break; + } + case 'share': { + const params = query(options.params); + if (targetHost) { + window.open(`https://${targetHost}/share?${params}`, '_blank', 'noopener'); + } else { + window.open(`https://misskey-hub.net/share/?${params}`, '_blank', 'noopener'); + } + break; + } + } +} + +async function specifyHostAndOpenRemote(options: OpenOnRemoteOptions): Promise<void> { + const { canceled, result: hostTemp } = await os.inputText({ + title: i18n.ts.inputHostName, + placeholder: 'misskey.example.com', + }); + + if (canceled) return; + + let targetHost: string | null = hostTemp; + + // ドメイン部分だけを取り出す + targetHost = extractDomain(targetHost); + if (targetHost == null) { + os.alert({ + type: 'error', + title: i18n.ts.invalidValue, + text: i18n.ts.tryAgain, + }); + return; + } + openRemote(options, targetHost); +} </script> <style lang="scss" module> @@ -234,4 +297,36 @@ function resetPassword(): void { background-size: cover; border-radius: 100%; } + +.instanceManualSelectButton { + display: block; + text-align: center; + opacity: .7; + font-size: .8em; + + &:hover { + text-decoration: underline; + } +} + +.orHr { + position: relative; + margin: .4em auto; + width: 100%; + height: 1px; + background: var(--divider); +} + +.orMsg { + position: absolute; + top: -.6em; + display: inline-block; + padding: 0 1em; + background: var(--panel); + font-size: 0.8em; + color: var(--fgOnPanel); + margin: 0; + left: 50%; + transform: translateX(-50%); +} </style> diff --git a/packages/frontend/src/components/MkSigninDialog.vue b/packages/frontend/src/components/MkSigninDialog.vue index 33355bb99e..524c62b4d3 100644 --- a/packages/frontend/src/components/MkSigninDialog.vue +++ b/packages/frontend/src/components/MkSigninDialog.vue @@ -6,21 +6,22 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkModalWindow ref="dialog" - :width="370" - :height="400" + :width="400" + :height="430" @close="onClose" @closed="emit('closed')" > <template #header>{{ i18n.ts.login }}</template> <MkSpacer :marginMin="20" :marginMax="28"> - <MkSignin :autoSet="autoSet" :message="message" @login="onLogin"/> + <MkSignin :autoSet="autoSet" :message="message" :openOnRemote="openOnRemote" @login="onLogin"/> </MkSpacer> </MkModalWindow> </template> <script lang="ts" setup> import { shallowRef } from 'vue'; +import type { OpenOnRemoteOptions } from '@/scripts/please-login.js'; import MkSignin from '@/components/MkSignin.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n.js'; @@ -28,9 +29,11 @@ import { i18n } from '@/i18n.js'; withDefaults(defineProps<{ autoSet?: boolean; message?: string, + openOnRemote?: OpenOnRemoteOptions, }>(), { autoSet: false, message: '', + openOnRemote: undefined, }); const emit = defineEmits<{ diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue index f247ba8fdd..d6f1ae453c 100644 --- a/packages/frontend/src/components/MkUserInfo.vue +++ b/packages/frontend/src/components/MkUserInfo.vue @@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only <p :class="$style.statusItemLabel">{{ i18n.ts.followers }}</p><span :class="$style.statusItemValue">{{ number(user.followersCount) }}</span> </div> </div> - <MkFollowButton v-if="$i && user.id != $i.id" :class="$style.follow" :user="user" mini/> + <MkFollowButton v-if="user.id != $i?.id" :class="$style.follow" :user="user" mini/> </div> </template> |