From 4341f0161abfafa288840bf1b4426df271e5f924 Mon Sep 17 00:00:00 2001 From: Denis Bykhov <bykhov.denis@gmail.com> Date: Sun, 18 Feb 2024 21:23:04 +0600 Subject: [PATCH] ONB-23 (#4690) Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com> --- plugins/login-resources/src/actions.ts | 30 +++++++++ .../src/components/Confirmation.svelte | 32 +++------ .../src/components/ConfirmationSend.svelte | 17 ++--- .../src/components/CreateWorkspaceForm.svelte | 13 ++-- .../src/components/Form.svelte | 30 ++++----- .../src/components/InviteLink.svelte | 2 +- .../src/components/Join.svelte | 38 +++-------- .../src/components/LoginApp.svelte | 35 +++------- .../src/components/LoginForm.svelte | 33 +++------- .../src/components/PasswordRequest.svelte | 29 +++----- .../src/components/PasswordRestore.svelte | 13 ++-- .../src/components/SelectWorkspace.svelte | 66 +++++++------------ .../src/components/SignupForm.svelte | 9 +-- plugins/login-resources/src/index.ts | 22 +++++++ plugins/login-resources/src/utils.ts | 54 +++++++++++++-- .../src/components/SelectWorkspaceMenu.svelte | 1 + server/account/src/index.ts | 2 +- .../tests/model/select-workspace-page.ts | 1 + tests/sanity/tests/workspace/create.spec.ts | 5 -- 19 files changed, 207 insertions(+), 225 deletions(-) create mode 100644 plugins/login-resources/src/actions.ts diff --git a/plugins/login-resources/src/actions.ts b/plugins/login-resources/src/actions.ts new file mode 100644 index 0000000000..4a0bab82bc --- /dev/null +++ b/plugins/login-resources/src/actions.ts @@ -0,0 +1,30 @@ +import { goTo } from './utils' +import login from './plugin' +import { type BottomAction } from '.' + +export const signUpAction: BottomAction = { + caption: login.string.DoNotHaveAnAccount, + i18n: login.string.SignUp, + page: 'signup', + func: () => { + goTo('signup') + } +} + +export const loginAction: BottomAction = { + caption: login.string.AlreadyJoined, + i18n: login.string.LogIn, + page: 'login', + func: () => { + goTo('login', true) + } +} + +export const recoveryAction: BottomAction = { + caption: login.string.ForgotPassword, + i18n: login.string.Recover, + page: 'password', + func: () => { + goTo('password', true) + } +} diff --git a/plugins/login-resources/src/components/Confirmation.svelte b/plugins/login-resources/src/components/Confirmation.svelte index 43a250ed94..4f2d9366c7 100644 --- a/plugins/login-resources/src/components/Confirmation.svelte +++ b/plugins/login-resources/src/components/Confirmation.svelte @@ -13,32 +13,16 @@ // limitations under the License. --> <script lang="ts"> - import { OK, setMetadata, Severity, Status } from '@hcengineering/platform' + import { OK, Severity, Status, setMetadata } from '@hcengineering/platform' - import { getCurrentLocation, navigate, setMetadataLocalStorage } from '@hcengineering/ui' - import login from '../plugin' - import { confirm } from '../utils' - import presentation from '@hcengineering/presentation' + import { getCurrentLocation, setMetadataLocalStorage } from '@hcengineering/ui' import { onMount } from 'svelte' + import login from '../plugin' + import { afterConfirm, confirm, goTo } from '../utils' + import presentation from '@hcengineering/presentation' export let status: Status<any> = OK - function goToWorkspaces () { - const loc = getCurrentLocation() - loc.query = undefined - loc.path[1] = 'selectWorkspace' - loc.path.length = 2 - navigate(loc) - } - - function goToLogin (): void { - const loc = getCurrentLocation() - loc.query = undefined - loc.path[1] = 'login' - loc.path.length = 2 - navigate(loc) - } - async function check (): Promise<void> { const location = getCurrentLocation() if (location.query?.id === undefined || location.query?.id === null) return @@ -53,13 +37,13 @@ setMetadataLocalStorage(login.metadata.LastToken, result.token) setMetadataLocalStorage(login.metadata.LoginEndpoint, result.endpoint) setMetadataLocalStorage(login.metadata.LoginEmail, result.email) - goToWorkspaces() + await afterConfirm() } else { - goToLogin() + goTo('login') } } onMount(() => { - check() + void check() }) </script> diff --git a/plugins/login-resources/src/components/ConfirmationSend.svelte b/plugins/login-resources/src/components/ConfirmationSend.svelte index c793e52036..90800e3023 100644 --- a/plugins/login-resources/src/components/ConfirmationSend.svelte +++ b/plugins/login-resources/src/components/ConfirmationSend.svelte @@ -13,26 +13,23 @@ // limitations under the License. --> <script lang="ts"> - import { Label, getCurrentLocation, navigate } from '@hcengineering/ui' - import login from '../plugin' - import { getAccount } from '../utils' + import { Label } from '@hcengineering/ui' import { onMount } from 'svelte' + import login from '../plugin' + import { afterConfirm, getAccount } from '../utils' const CHECK_INTERVAL = 1000 - async function checkAccountStatus () { + async function checkAccountStatus (): Promise<void> { const account = await getAccount() if (account?.confirmed === true) { - const loc = getCurrentLocation() - loc.path[1] = 'selectWorkspace' - loc.path.length = 2 - navigate(loc) + await afterConfirm() } } let weAreHere = false - async function check () { + async function check (): Promise<void> { try { await checkAccountStatus() } catch (e) { @@ -45,7 +42,7 @@ onMount(() => { weAreHere = true - check() + void check() return () => { weAreHere = false } diff --git a/plugins/login-resources/src/components/CreateWorkspaceForm.svelte b/plugins/login-resources/src/components/CreateWorkspaceForm.svelte index 11c384111d..60b7910a3a 100644 --- a/plugins/login-resources/src/components/CreateWorkspaceForm.svelte +++ b/plugins/login-resources/src/components/CreateWorkspaceForm.svelte @@ -17,7 +17,7 @@ import { Status, Severity, OK, setMetadata } from '@hcengineering/platform' import Form from './Form.svelte' - import { createWorkspace, getAccount } from '../utils' + import { createWorkspace, getAccount, goTo } from '../utils' import { fetchMetadataLocalStorage, getCurrentLocation, navigate, setMetadataLocalStorage } from '@hcengineering/ui' import login from '../plugin' import { workbenchId } from '@hcengineering/workbench' @@ -26,6 +26,7 @@ const fields = [ { + id: 'workspace', name: 'workspace', i18n: login.string.Workspace, rules: [] @@ -40,7 +41,7 @@ onMount(async () => { const account = await getAccount() - if (account?.confirmed !== true) { + if (account?.confirmed === false) { const loc = getCurrentLocation() loc.path[1] = 'confirmationSend' loc.path.length = 2 @@ -60,7 +61,7 @@ setMetadata(presentation.metadata.Token, result.token) setMetadataLocalStorage(login.metadata.LastToken, result.token) const tokens: Record<string, string> = fetchMetadataLocalStorage(login.metadata.LoginTokens) ?? {} - tokens[object.workspace] = result.token + tokens[result.workspace] = result.token setMetadataLocalStorage(login.metadata.LoginTokens, tokens) setMetadataLocalStorage(login.metadata.LoginEndpoint, result.endpoint) setMetadataLocalStorage(login.metadata.LoginEmail, result.email) @@ -80,11 +81,9 @@ { caption: login.string.HaveWorkspace, i18n: login.string.SelectWorkspace, + page: 'selectWorkspace', func: () => { - const loc = getCurrentLocation() - loc.path[1] = 'selectWorkspace' - loc.path.length = 2 - navigate(loc) + goTo('selectWorkspace') } } ]} diff --git a/plugins/login-resources/src/components/Form.svelte b/plugins/login-resources/src/components/Form.svelte index ca0fac03bb..d423f4e891 100644 --- a/plugins/login-resources/src/components/Form.svelte +++ b/plugins/login-resources/src/components/Form.svelte @@ -14,21 +14,23 @@ // limitations under the License. --> <script lang="ts"> + import type { IntlString } from '@hcengineering/platform' + import { OK, Severity, Status, translate } from '@hcengineering/platform' import { + Button, + Label, + StylishEdit, + deviceOptionsStore as deviceInfo, getCurrentLocation, navigate, - StylishEdit, - Label, - Button, - deviceOptionsStore as deviceInfo, themeStore } from '@hcengineering/ui' import StatusControl from './StatusControl.svelte' - import { OK, Status, Severity, translate } from '@hcengineering/platform' - import type { IntlString } from '@hcengineering/platform' - import login from '../plugin' + import { NavLink } from '@hcengineering/presentation' import { onMount } from 'svelte' + import { BottomAction, getHref } from '..' + import login from '../plugin' interface Field { id?: string @@ -49,12 +51,6 @@ func: () => Promise<void> } - interface BottomAction { - i18n: IntlString - func: () => void - caption: IntlString - } - export let caption: IntlString export let status: Status export let fields: Field[] @@ -107,7 +103,7 @@ let inAction = false - function performAction (action: Action) { + function performAction (action: Action): void { for (const field of fields) { trim(field.name) } @@ -223,7 +219,11 @@ {#each bottomActions as action} <div> <span><Label label={action.caption} /></span> - <a href="." on:click|preventDefault={action.func}><Label label={action.i18n} /></a> + {#if action.page} + <NavLink href={getHref(action.page)}><Label label={action.i18n} /></NavLink> + {:else} + <a href="." on:click|preventDefault={action.func}><Label label={action.i18n} /></a> + {/if} </div> {/each} </div> diff --git a/plugins/login-resources/src/components/InviteLink.svelte b/plugins/login-resources/src/components/InviteLink.svelte index eb8ec77fbb..4a32140135 100644 --- a/plugins/login-resources/src/components/InviteLink.svelte +++ b/plugins/login-resources/src/components/InviteLink.svelte @@ -61,7 +61,7 @@ } }) - function setToDefault () { + function setToDefault (): void { expHours = defaultValues.expirationTime emailMask = defaultValues.emailMask limit = defaultValues.limit diff --git a/plugins/login-resources/src/components/Join.svelte b/plugins/login-resources/src/components/Join.svelte index 5a7213ecb8..617f8a89b8 100644 --- a/plugins/login-resources/src/components/Join.svelte +++ b/plugins/login-resources/src/components/Join.svelte @@ -23,6 +23,8 @@ import presentation from '@hcengineering/presentation' import { workbenchId } from '@hcengineering/workbench' import { onMount } from 'svelte' + import { BottomAction } from '..' + import { loginAction, recoveryAction } from '../actions' import login from '../plugin' const location = getCurrentLocation() @@ -88,46 +90,22 @@ } } - $: bottom = page === 'login' ? [signUpAction] : [loginJoinAction] - $: secondaryButtonLabel = page === 'login' ? login.string.SignUp : undefined - $: secondaryButtonAction = () => { - page = 'signUp' - } - - const signUpAction = { + const signUpAction: BottomAction = { caption: login.string.DoNotHaveAnAccount, i18n: login.string.SignUp, func: () => (page = 'signUp') } - const loginJoinAction = { + const loginJoinAction: BottomAction = { caption: login.string.HaveAccount, i18n: login.string.LogIn, func: () => (page = 'login') } - const loginAction = { - caption: login.string.AlreadyJoined, - i18n: login.string.LogIn, - func: () => { - const loc = getCurrentLocation() - loc.path[1] = 'login' - loc.query = undefined - loc.path.length = 2 - navigate(loc) - } - } - - const recoveryAction = { - caption: login.string.ForgotPassword, - i18n: login.string.Recover, - func: () => { - const loc = getCurrentLocation() - loc.path[1] = 'password' - loc.query = undefined - loc.path.length = 2 - navigate(loc) - } + $: bottom = page === 'login' ? [signUpAction] : [loginJoinAction] + $: secondaryButtonLabel = page === 'login' ? login.string.SignUp : undefined + $: secondaryButtonAction = () => { + page = 'signUp' } onMount(() => { diff --git a/plugins/login-resources/src/components/LoginApp.svelte b/plugins/login-resources/src/components/LoginApp.svelte index afaeb9546a..0576872b3b 100644 --- a/plugins/login-resources/src/components/LoginApp.svelte +++ b/plugins/login-resources/src/components/LoginApp.svelte @@ -14,7 +14,7 @@ // limitations under the License. --> <script lang="ts"> - import { Popup, Scroller, deviceOptionsStore as deviceInfo, location, ticker, themeStore } from '@hcengineering/ui' + import { Popup, Scroller, deviceOptionsStore as deviceInfo, location, themeStore } from '@hcengineering/ui' import { getMetadata } from '@hcengineering/platform' import presentation from '@hcengineering/presentation' @@ -37,40 +37,23 @@ import loginBackAvif from '../../img/login_back.avif' import loginBack2xAvif from '../../img/login_back_2x.avif' + import { Pages, pages } from '..' import loginBackWebp from '../../img/login_back.webp' import loginBack2xWebp from '../../img/login_back_2x.webp' - export let page: string = 'login' + export let page: Pages = 'login' let navigateUrl: string | undefined - function getToken (timer: number): string | undefined { - return getMetadata(presentation.metadata.Token) - } - $: token = getToken($ticker) - - const pages = [ - 'login', - 'signup', - 'createWorkspace', - 'password', - 'recovery', - 'selectWorkspace', - 'join', - 'confirm', - 'confirmationSend' - ] onDestroy( location.subscribe((loc) => { - void (async (loc) => { - token = getMetadata(presentation.metadata.Token) - page = loc.path[1] ?? (token ? 'selectWorkspace' : 'login') - if (!pages.includes(page)) { - page = 'login' - } + const token = getMetadata(presentation.metadata.Token) + page = (loc.path[1] as Pages) ?? (token != null ? 'selectWorkspace' : 'login') + if (!pages.includes(page)) { + page = 'login' + } - navigateUrl = loc.query?.navigateUrl ?? undefined - })(loc) + navigateUrl = loc.query?.navigateUrl ?? undefined }) ) </script> diff --git a/plugins/login-resources/src/components/LoginForm.svelte b/plugins/login-resources/src/components/LoginForm.svelte index 2c7aa383f0..5e347307e6 100644 --- a/plugins/login-resources/src/components/LoginForm.svelte +++ b/plugins/login-resources/src/components/LoginForm.svelte @@ -30,6 +30,7 @@ import Form from './Form.svelte' import { LoginInfo } from '@hcengineering/login' + import { recoveryAction } from '../actions' import login from '../plugin' export let navigateUrl: string | undefined = undefined @@ -51,15 +52,11 @@ async function doLoginNavigate ( result: LoginInfo | undefined, - updateStatus: (status: Status<any>) => void, - token?: string + updateStatus: (status: Status<any>) => void ): Promise<boolean> { if (result !== undefined) { - if (result.token != null && getMetadata(presentation.metadata.Token) === result.token) { - return false - } - setMetadata(presentation.metadata.Token, token ?? result.token) - setMetadataLocalStorage(login.metadata.LastToken, token ?? result.token) + setMetadata(presentation.metadata.Token, result.token) + setMetadataLocalStorage(login.metadata.LastToken, result.token) setMetadataLocalStorage(login.metadata.LoginEndpoint, result.endpoint) setMetadataLocalStorage(login.metadata.LoginEmail, result.email) @@ -108,16 +105,6 @@ } } - const recoveryAction = { - caption: login.string.ForgotPassword, - i18n: login.string.Recover, - func: () => { - const loc = getCurrentLocation() - loc.path[1] = 'password' - loc.path.length = 2 - navigate(loc) - } - } let loading = true async function chooseToken (time: number): Promise<void> { @@ -125,15 +112,11 @@ const lastToken = fetchMetadataLocalStorage(login.metadata.LastToken) if (lastToken != null) { try { - const info = await getAccount(false, lastToken) + const info = await getAccount(false) if (info !== undefined) { - await doLoginNavigate( - info, - (st) => { - status = st - }, - lastToken - ) + await doLoginNavigate(info, (st) => { + status = st + }) } } catch (err: any) { setMetadataLocalStorage(login.metadata.LastToken, null) diff --git a/plugins/login-resources/src/components/PasswordRequest.svelte b/plugins/login-resources/src/components/PasswordRequest.svelte index 4e69042a60..8072bc16d8 100644 --- a/plugins/login-resources/src/components/PasswordRequest.svelte +++ b/plugins/login-resources/src/components/PasswordRequest.svelte @@ -16,10 +16,12 @@ import { OK, Severity, Status } from '@hcengineering/platform' import { MessageBox } from '@hcengineering/presentation' - import { getCurrentLocation, navigate, showPopup } from '@hcengineering/ui' + import { showPopup } from '@hcengineering/ui' import login from '../plugin' - import { requestPassword } from '../utils' + import { goTo, requestPassword } from '../utils' import Form from './Form.svelte' + import { BottomAction } from '..' + import { signUpAction } from '../actions' const fields = [{ id: 'email', name: 'username', i18n: login.string.Email }] @@ -46,35 +48,20 @@ }, undefined, () => { - const loc = getCurrentLocation() - loc.path[1] = 'login' - navigate(loc) + goTo('login') } ) } } } - const signUpAction = { - caption: login.string.DoNotHaveAnAccount, - i18n: login.string.SignUp, - func: () => { - const loc = getCurrentLocation() - loc.path[1] = 'signup' - loc.path.length = 2 - navigate(loc) - } - } - - const bottomActions = [ + const bottomActions: BottomAction[] = [ { caption: login.string.KnowPassword, i18n: login.string.LogIn, + page: 'login', func: () => { - const loc = getCurrentLocation() - loc.path[1] = 'login' - loc.path.length = 2 - navigate(loc) + goTo('login') } }, signUpAction diff --git a/plugins/login-resources/src/components/PasswordRestore.svelte b/plugins/login-resources/src/components/PasswordRestore.svelte index afb0cab33e..bed8c838b1 100644 --- a/plugins/login-resources/src/components/PasswordRestore.svelte +++ b/plugins/login-resources/src/components/PasswordRestore.svelte @@ -15,11 +15,11 @@ <script lang="ts"> import { OK, setMetadata, Severity, Status } from '@hcengineering/platform' - import { getCurrentLocation, navigate, setMetadataLocalStorage } from '@hcengineering/ui' - import login from '../plugin' - import { restorePassword } from '../utils' - import Form from './Form.svelte' import presentation from '@hcengineering/presentation' + import { getCurrentLocation, setMetadataLocalStorage } from '@hcengineering/ui' + import login from '../plugin' + import { goTo, restorePassword } from '../utils' + import Form from './Form.svelte' const fields = [ { id: 'new-password', name: 'password', i18n: login.string.Password, password: true }, @@ -48,10 +48,7 @@ setMetadata(presentation.metadata.Token, result.token) setMetadataLocalStorage(login.metadata.LoginEndpoint, result.endpoint) setMetadataLocalStorage(login.metadata.LoginEmail, result.email) - const loc = getCurrentLocation() - loc.path[1] = 'selectWorkspace' - loc.path.length = 2 - navigate(loc) + goTo('selectWorkspace') } } } diff --git a/plugins/login-resources/src/components/SelectWorkspace.svelte b/plugins/login-resources/src/components/SelectWorkspace.svelte index c5f2b095df..12594f2920 100644 --- a/plugins/login-resources/src/components/SelectWorkspace.svelte +++ b/plugins/login-resources/src/components/SelectWorkspace.svelte @@ -14,22 +14,14 @@ // limitations under the License. --> <script lang="ts"> - import { OK, Severity, Status } from '@hcengineering/platform' - import presentation from '@hcengineering/presentation' - import { - Button, - Label, - Scroller, - deviceOptionsStore as deviceInfo, - getCurrentLocation, - navigate, - setMetadataLocalStorage - } from '@hcengineering/ui' - import login from '../plugin' - import { getAccount, getWorkspaces, navigateToWorkspace, selectWorkspace } from '../utils' - import StatusControl from './StatusControl.svelte' import { LoginInfo, Workspace } from '@hcengineering/login' + import { OK, Severity, Status } from '@hcengineering/platform' + import presentation, { NavLink } from '@hcengineering/presentation' + import { Button, Label, Scroller, deviceOptionsStore as deviceInfo, setMetadataLocalStorage } from '@hcengineering/ui' import { onMount } from 'svelte' + import login from '../plugin' + import { getAccount, getHref, getWorkspaces, goTo, navigateToWorkspace, selectWorkspace } from '../utils' + import StatusControl from './StatusControl.svelte' const CHECK_INTERVAL = 1000 @@ -41,11 +33,11 @@ let account: LoginInfo | undefined = undefined - async function loadAccount () { + async function loadAccount (): Promise<void> { account = await getAccount() } - async function updateWorkspaces () { + async function updateWorkspaces (): Promise<void> { try { workspaces = await getWorkspaces() } catch (e) { @@ -57,14 +49,14 @@ } onMount(() => { - loadAccount() + void loadAccount() return () => { flagToUpdateWorkspaces = false } }) - async function select (workspace: string) { + async function select (workspace: string): Promise<void> { status = new Status(Severity.INFO, login.status.ConnectingToServer, {}) const [loginStatus, result] = await selectWorkspace(workspace) @@ -73,42 +65,25 @@ navigateToWorkspace(workspace, result, navigateUrl) } - async function _getWorkspaces () { + async function _getWorkspaces (): Promise<void> { try { const res = await getWorkspaces() if (res.length === 0 && account?.confirmed === false) { - const loc = getCurrentLocation() - loc.path[1] = 'confirmationSend' - loc.path.length = 2 - navigate(loc) + goTo('confirmationSend') } workspaces = res flagToUpdateWorkspaces = true - updateWorkspaces() + await updateWorkspaces() } catch (err: any) { setMetadataLocalStorage(presentation.metadata.Token, null) setMetadataLocalStorage(login.metadata.LoginEndpoint, null) setMetadataLocalStorage(login.metadata.LoginEmail, null) - changeAccount() + goTo('login') throw err } } - - function createWorkspace (): void { - const loc = getCurrentLocation() - loc.path[1] = 'createWorkspace' - loc.path.length = 2 - navigate(loc) - } - - function changeAccount (): void { - const loc = getCurrentLocation() - loc.path[1] = 'login' - loc.path.length = 2 - navigate(loc) - } </script> <form class="container" style:padding={$deviceInfo.docWidth <= 480 ? '1.25rem' : '5rem'}> @@ -134,7 +109,14 @@ {/each} {#if workspaces.length === 0 && account?.confirmed === true} <div class="form-row send"> - <Button label={login.string.CreateWorkspace} kind={'primary'} width="100%" on:click={createWorkspace} /> + <Button + label={login.string.CreateWorkspace} + kind={'primary'} + width="100%" + on:click={() => { + goTo('createWorkspace') + }} + /> </div> {/if} </div> @@ -144,12 +126,12 @@ {#if workspaces.length} <div> <span><Label label={login.string.WantAnotherWorkspace} /></span> - <a href="." on:click|preventDefault={createWorkspace}><Label label={login.string.CreateWorkspace} /></a> + <NavLink href={getHref('createWorkspace')}><Label label={login.string.CreateWorkspace} /></NavLink> </div> {/if} <div> <span><Label label={login.string.NotSeeingWorkspace} /></span> - <a href="." on:click|preventDefault={changeAccount}><Label label={login.string.ChangeAccount} /></a> + <NavLink href={getHref('login')}><Label label={login.string.ChangeAccount} /></NavLink> </div> </div> {/await} diff --git a/plugins/login-resources/src/components/SignupForm.svelte b/plugins/login-resources/src/components/SignupForm.svelte index f91d0bd3a2..3be00e5918 100644 --- a/plugins/login-resources/src/components/SignupForm.svelte +++ b/plugins/login-resources/src/components/SignupForm.svelte @@ -16,9 +16,9 @@ <script lang="ts"> import { OK, Severity, Status, setMetadata } from '@hcengineering/platform' import presentation from '@hcengineering/presentation' - import { getCurrentLocation, navigate, setMetadataLocalStorage } from '@hcengineering/ui' + import { setMetadataLocalStorage } from '@hcengineering/ui' import login from '../plugin' - import { signUp } from '../utils' + import { goTo, signUp } from '../utils' import Form from './Form.svelte' const fields = [ @@ -51,10 +51,7 @@ if (result !== undefined) { setMetadata(presentation.metadata.Token, result.token) setMetadataLocalStorage(login.metadata.LastToken, result.token) - const loc = getCurrentLocation() - loc.path[1] = 'confirmationSend' - loc.path.length = 2 - navigate(loc) + goTo('confirmationSend') } } } diff --git a/plugins/login-resources/src/index.ts b/plugins/login-resources/src/index.ts index df59f701f9..dc12877c9f 100644 --- a/plugins/login-resources/src/index.ts +++ b/plugins/login-resources/src/index.ts @@ -14,6 +14,7 @@ // limitations under the License. // +import { type IntlString } from '@hcengineering/platform' import InviteLink from './components/InviteLink.svelte' import LoginApp from './components/LoginApp.svelte' import { changePassword, getWorkspaces, leaveWorkspace, selectWorkspace, sendInvite } from './utils' @@ -38,4 +39,25 @@ export default async () => ({ } }) +export const pages = [ + 'login', + 'signup', + 'createWorkspace', + 'password', + 'recovery', + 'selectWorkspace', + 'join', + 'confirm', + 'confirmationSend' +] as const + +export type Pages = (typeof pages)[number] + +export interface BottomAction { + i18n: IntlString + page?: Pages + func: () => void + caption: IntlString +} + export * from './utils' diff --git a/plugins/login-resources/src/utils.ts b/plugins/login-resources/src/utils.ts index 47a685a651..2c9049a50b 100644 --- a/plugins/login-resources/src/utils.ts +++ b/plugins/login-resources/src/utils.ts @@ -20,21 +20,23 @@ import { PlatformError, getMetadata, setMetadata, + translate, unknownError, unknownStatus, - type Status, - translate + type Status } from '@hcengineering/platform' import presentation from '@hcengineering/presentation' import { fetchMetadataLocalStorage, getCurrentLocation, locationStorageKeyId, + locationToUrl, navigate, setMetadataLocalStorage, type Location } from '@hcengineering/ui' import { workbenchId } from '@hcengineering/workbench' +import { type Pages } from './index' const DEV_WORKSPACE = 'DEV WORKSPACE' @@ -239,7 +241,7 @@ export async function getWorkspaces (): Promise<Workspace[]> { } } -export async function getAccount (doNavigate: boolean = true, token?: string): Promise<LoginInfo | undefined> { +export async function getAccount (doNavigate: boolean = true): Promise<LoginInfo | undefined> { const accountsUrl = getMetadata(login.metadata.AccountsUrl) if (accountsUrl === undefined) { @@ -255,7 +257,7 @@ export async function getAccount (doNavigate: boolean = true, token?: string): P } } - token = token ?? getMetadata(presentation.metadata.Token) + const token = getMetadata(presentation.metadata.Token) ?? fetchMetadataLocalStorage(login.metadata.LastToken) if (token === undefined) { if (doNavigate) { const loc = getCurrentLocation() @@ -770,3 +772,47 @@ async function handleStatusError (message: string, err: Status): Promise<void> { const label = await translate(err.code, err.params, 'en') Analytics.handleError(new Error(`${message}: ${label}`)) } + +export function getLoc (path: Pages): Location { + const loc = getCurrentLocation() + loc.path[1] = path + loc.path.length = 2 + return loc +} + +export function goTo (path: Pages, clearQuery: boolean = false): void { + const loc = getLoc(path) + if (clearQuery) { + loc.query = undefined + } + navigate(loc) +} + +export function getHref (path: Pages): string { + const url = locationToUrl(getLoc(path)) + const frontUrl = getMetadata(presentation.metadata.FrontUrl) + const host = frontUrl ?? document.location.origin + return host + url +} + +export async function afterConfirm (): Promise<void> { + const joinedWS = await getWorkspaces() + if (joinedWS.length === 0) { + goTo('createWorkspace') + } else if (joinedWS.length === 1) { + const result = (await selectWorkspace(joinedWS[0].workspace))[1] + if (result !== undefined) { + setMetadata(presentation.metadata.Token, result.token) + setMetadataLocalStorage(login.metadata.LastToken, result.token) + setMetadataLocalStorage(login.metadata.LoginEndpoint, result.endpoint) + setMetadataLocalStorage(login.metadata.LoginEmail, result.email) + const tokens: Record<string, string> = fetchMetadataLocalStorage(login.metadata.LoginTokens) ?? {} + tokens[result.workspace] = result.token + setMetadataLocalStorage(login.metadata.LoginTokens, tokens) + + navigateToWorkspace(joinedWS[0].workspace, result) + } + } else { + goTo('selectWorkspace') + } +} diff --git a/plugins/workbench-resources/src/components/SelectWorkspaceMenu.svelte b/plugins/workbench-resources/src/components/SelectWorkspaceMenu.svelte index 3a36a7fa75..51ede1f4ee 100644 --- a/plugins/workbench-resources/src/components/SelectWorkspaceMenu.svelte +++ b/plugins/workbench-resources/src/components/SelectWorkspaceMenu.svelte @@ -61,6 +61,7 @@ if (loginInfo !== undefined) { tokens[ws] = loginInfo?.token } + setMetadataLocalStorage(login.metadata.LoginTokens, tokens) } const last = localStorage.getItem(`${locationStorageKeyId}_${ws}`) if (last !== null) { diff --git a/server/account/src/index.ts b/server/account/src/index.ts index ea8192c88d..b243eb71f9 100644 --- a/server/account/src/index.ts +++ b/server/account/src/index.ts @@ -787,7 +787,7 @@ export const createUserWorkspace = if (info === null) { throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email })) } - if (info.confirmed !== true) { + if (info.confirmed === false) { throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotConfirmed, { account: email })) } diff --git a/tests/sanity/tests/model/select-workspace-page.ts b/tests/sanity/tests/model/select-workspace-page.ts index 51aaab4303..c81bfa31bf 100644 --- a/tests/sanity/tests/model/select-workspace-page.ts +++ b/tests/sanity/tests/model/select-workspace-page.ts @@ -22,6 +22,7 @@ export class SelectWorkspacePage extends CommonPage { } async createWorkspace (workspaceName: string): Promise<void> { + await this.buttonCreateWorkspace.waitFor({ state: 'visible' }) await this.buttonWorkspaceName.fill(workspaceName) expect(await this.buttonCreateNewWorkspace.isEnabled()).toBe(true) await this.buttonCreateNewWorkspace.click() diff --git a/tests/sanity/tests/workspace/create.spec.ts b/tests/sanity/tests/workspace/create.spec.ts index 9aab9638ae..9070fbfa66 100644 --- a/tests/sanity/tests/workspace/create.spec.ts +++ b/tests/sanity/tests/workspace/create.spec.ts @@ -29,7 +29,6 @@ test.describe('Workspace tests', () => { await signUpPage.signUp(newUser) const selectWorkspacePage = new SelectWorkspacePage(page) - await selectWorkspacePage.buttonCreateWorkspace.click() await selectWorkspacePage.createWorkspace(newWorkspaceName) const leftSideMenuPage = new LeftSideMenuPage(page) @@ -67,7 +66,6 @@ test.describe('Workspace tests', () => { await signUpPage.signUp(newUser) const selectWorkspacePage = new SelectWorkspacePage(page) - await selectWorkspacePage.buttonCreateWorkspace.click() await selectWorkspacePage.createWorkspace(newWorkspaceName) const leftSideMenuPage = new LeftSideMenuPage(page) @@ -118,7 +116,6 @@ test.describe('Workspace tests', () => { await signUpPage.buttonSignUp.click() const selectWorkspacePage = new SelectWorkspacePage(page) - await selectWorkspacePage.buttonCreateWorkspace.click() await selectWorkspacePage.checkInfo(page, 'Required field Workspace name') await selectWorkspacePage.buttonWorkspaceName.fill(newWorkspaceName) await selectWorkspacePage.checkInfoSectionNotExist(page) @@ -141,7 +138,6 @@ test.describe('Workspace tests', () => { await signUpPage.signUp(newUser) const selectWorkspacePage = new SelectWorkspacePage(page) - await selectWorkspacePage.buttonCreateWorkspace.click() await selectWorkspacePage.createWorkspace(newWorkspaceName) const leftSideMenuPage = new LeftSideMenuPage(page) @@ -191,7 +187,6 @@ test.describe('Workspace tests', () => { await signUpPage.signUp(newUser) const selectWorkspacePage = new SelectWorkspacePage(page) - await selectWorkspacePage.buttonCreateWorkspace.click() await selectWorkspacePage.createWorkspace(newWorkspaceName) const leftSideMenuPage = new LeftSideMenuPage(page)