mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2024-11-22 01:47:39 -07:00
[client] Rework OAuth login page
This commit is contained in:
parent
be02bc78ad
commit
093f13646e
5 changed files with 164 additions and 52 deletions
|
@ -1032,6 +1032,7 @@ activeEmailValidationDescription: "Enables stricter validation of email addresse
|
|||
navbar: "Navigation bar"
|
||||
shuffle: "Shuffle"
|
||||
account: "Account"
|
||||
signedInAs: "Signed in as"
|
||||
move: "Move"
|
||||
pushNotification: "Push notifications"
|
||||
subscribePushNotification: "Enable push notifications"
|
||||
|
|
|
@ -150,6 +150,7 @@ export async function openAccountMenu(
|
|||
opts: {
|
||||
includeCurrentAccount?: boolean;
|
||||
withExtraOperation: boolean;
|
||||
withoutProfileLink?: boolean;
|
||||
active?: misskey.entities.UserDetailed["id"];
|
||||
onChoose?: (account: misskey.entities.UserDetailed) => void;
|
||||
},
|
||||
|
@ -227,53 +228,56 @@ export async function openAccountMenu(
|
|||
);
|
||||
|
||||
if (opts.withExtraOperation) {
|
||||
popupMenu(
|
||||
[
|
||||
...[
|
||||
const menu = [
|
||||
...(!opts.withoutProfileLink ? [
|
||||
{
|
||||
type: "link",
|
||||
text: i18n.ts.profile,
|
||||
to: `/@${$i.username}`,
|
||||
avatar: $i,
|
||||
},
|
||||
null,
|
||||
] : []),
|
||||
...(opts.includeCurrentAccount ? [createItem($i)] : []),
|
||||
...accountItemPromises,
|
||||
...(opts.withoutProfileLink ? [null] : []),
|
||||
{
|
||||
type: "parent",
|
||||
icon: "ph-plus ph-bold ph-lg",
|
||||
text: i18n.ts.addAccount,
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
text: i18n.ts.profile,
|
||||
to: `/@${$i.username}`,
|
||||
avatar: $i,
|
||||
},
|
||||
null,
|
||||
...(opts.includeCurrentAccount ? [createItem($i)] : []),
|
||||
...accountItemPromises,
|
||||
{
|
||||
type: "parent",
|
||||
icon: "ph-plus ph-bold ph-lg",
|
||||
text: i18n.ts.addAccount,
|
||||
children: [
|
||||
{
|
||||
text: i18n.ts.existingAccount,
|
||||
action: () => {
|
||||
showSigninDialog();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.ts.createAccount,
|
||||
action: () => {
|
||||
createAccount();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
icon: "ph-users ph-bold ph-lg",
|
||||
text: i18n.ts.manageAccounts,
|
||||
to: "/settings/accounts",
|
||||
},
|
||||
{
|
||||
type: "button",
|
||||
icon: "ph-sign-out ph-bold ph-lg",
|
||||
text: i18n.ts.logout,
|
||||
text: i18n.ts.existingAccount,
|
||||
action: () => {
|
||||
signout();
|
||||
showSigninDialog();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.ts.createAccount,
|
||||
action: () => {
|
||||
createAccount();
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
icon: "ph-users ph-bold ph-lg",
|
||||
text: i18n.ts.manageAccounts,
|
||||
to: "/settings/accounts",
|
||||
},
|
||||
{
|
||||
type: "button",
|
||||
icon: "ph-sign-out ph-bold ph-lg",
|
||||
text: i18n.ts.logout,
|
||||
action: () => {
|
||||
signout();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
popupMenu(
|
||||
menu,
|
||||
ev.currentTarget ?? ev.target,
|
||||
{
|
||||
align: "left",
|
||||
|
|
|
@ -188,7 +188,9 @@ function checkForSplash() {
|
|||
});
|
||||
|
||||
const app = createApp(
|
||||
window.location.search === "?zen"
|
||||
window.location.pathname === "/oauth/authorize"
|
||||
? defineAsyncComponent(() => import("@/ui/oauth.vue"))
|
||||
: window.location.search === "?zen"
|
||||
? defineAsyncComponent(() => import("@/ui/zen.vue"))
|
||||
: !$i
|
||||
? defineAsyncComponent(() => import("@/ui/visitor.vue"))
|
||||
|
|
|
@ -1,35 +1,55 @@
|
|||
<template>
|
||||
<MkSpacer :content-max="800">
|
||||
<div v-if="$i">
|
||||
<div v-if="state == 'waiting'" class="waiting _section">
|
||||
<div v-if="state == 'waiting'" class="waiting _section" :class="[$style.section]">
|
||||
<div class="_content">
|
||||
<MkLoading />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="state == 'denied'" class="denied _section">
|
||||
<div v-if="state == 'denied'" class="denied _section" :class="[$style.section]">
|
||||
<div class="_content">
|
||||
<p>{{ i18n.ts._auth.denied }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="state == 'error'" class="error _section">
|
||||
<div v-else-if="state == 'error'" class="error _section" :class="[$style.section]">
|
||||
<div class="_content">
|
||||
<p>{{ message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="state == 'accepted-oob'" class="accepted-oob _section">
|
||||
<div v-else-if="state == 'accepted-oob'" class="accepted-oob _section" :class="[$style.section]">
|
||||
<div class="_content">
|
||||
<p>{{ i18n.ts._auth.copyAsk }}</p>
|
||||
<pre>{{ code }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="state == 'accepted'" class="accepted _section">
|
||||
<div v-else-if="state == 'accepted'" class="accepted _section" :class="[$style.section]">
|
||||
<div class="_content">
|
||||
<p>
|
||||
{{ i18n.ts._auth.callback }}<MkEllipsis />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="_section">
|
||||
<div v-else class="_section" :class="[$style.section]">
|
||||
<div :class="[$style.container]">
|
||||
<button
|
||||
v-click-anime
|
||||
class="item _button"
|
||||
:class="[$style.account]"
|
||||
@click="openAccountMenu"
|
||||
>
|
||||
<MkAvatar
|
||||
:user="$i"
|
||||
:class="[$style.icon]"
|
||||
disableLink
|
||||
/><!-- <MkAcct class="text" :user="$i"/> -->
|
||||
</button>
|
||||
<div :class="[$style.left]">
|
||||
<div>{{ i18n.ts.signedInAs }}:</div>
|
||||
<div>@{{ $i.username }}<span :class="[$style.fade]">@{{ config.host }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<h2>Authorization required</h2>
|
||||
<div v-if="name" class="_title">
|
||||
{{ i18n.t("_auth.shareAccess", { name: name }) }}
|
||||
</div>
|
||||
|
@ -70,13 +90,13 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {} from "vue";
|
||||
import MkSignin from "@/components/MkSignin.vue";
|
||||
import MkButton from "@/components/MkButton.vue";
|
||||
import * as os from "@/os";
|
||||
import { $i, login } from "@/account";
|
||||
import { $i, login, openAccountMenu as openAccountMenu_ } from "@/account";
|
||||
import { appendQuery, query } from "@/scripts/url";
|
||||
import { i18n } from "@/i18n";
|
||||
import * as config from "@/config.js";
|
||||
|
||||
const props = defineProps<{
|
||||
response_type: string;
|
||||
|
@ -161,6 +181,17 @@ function deny(): void {
|
|||
async function onLogin(res): Promise<void> {
|
||||
await login(res.i);
|
||||
}
|
||||
|
||||
function openAccountMenu(ev: MouseEvent) {
|
||||
openAccountMenu_(
|
||||
{
|
||||
includeCurrentAccount: true,
|
||||
withExtraOperation: true,
|
||||
withoutProfileLink: true
|
||||
},
|
||||
ev,
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
@ -169,6 +200,8 @@ async function onLogin(res): Promise<void> {
|
|||
}
|
||||
|
||||
.permissions {
|
||||
justify-content: center;
|
||||
padding-top: var(--margin);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
|
@ -182,4 +215,36 @@ async function onLogin(res): Promise<void> {
|
|||
background-color: var(--buttonBg);
|
||||
color: var(--fg);
|
||||
}
|
||||
</style>
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.account {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 55px;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.section {
|
||||
background: var(--panel);
|
||||
padding: 20px 32px;
|
||||
border-radius: var(--radius);
|
||||
font-size: 1.05em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fade {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.left {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
|
|
40
packages/client/src/ui/oauth.vue
Normal file
40
packages/client/src/ui/oauth.vue
Normal file
|
@ -0,0 +1,40 @@
|
|||
<template>
|
||||
<div class="mk-app">
|
||||
<RouterView />
|
||||
|
||||
<XCommon />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ComputedRef } from "vue";
|
||||
import { provide } from "vue";
|
||||
import XCommon from "./_common_/common.vue";
|
||||
import { mainRouter } from "@/router";
|
||||
import type { PageMetadata } from "@/scripts/page-metadata";
|
||||
import {
|
||||
provideMetadataReceiver,
|
||||
setPageMetadata,
|
||||
} from "@/scripts/page-metadata";
|
||||
import { instanceName } from "@/config";
|
||||
|
||||
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
||||
|
||||
provide("router", mainRouter);
|
||||
provideMetadataReceiver((info) => {
|
||||
pageMetadata = info;
|
||||
if (pageMetadata.value) {
|
||||
document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||
}
|
||||
});
|
||||
|
||||
document.documentElement.style.overflowY = "scroll";
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mk-app {
|
||||
// ほんとは単に 100vh と書きたいところだが... https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
|
||||
min-height: calc(var(--vh, 1vh) * 100);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
Loading…
Reference in a new issue