Skip to content

Commit

Permalink
web/satellite/v2: update login UX
Browse files Browse the repository at this point in the history
Pre-create project for new users, with default name "My Storj Project".
If user logs in and only has one project (and no invitations), automatically go to the single project dashboard.
Show "my projects" page if user has more than one project, or if the user has a pending invitation.

Issue:
#6621

Change-Id: If8472951ec70272597de82803f554e98bfaeb78e
  • Loading branch information
VitaliiShpital committed Jan 8, 2024
1 parent a792e37 commit 3c96c86
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 26 deletions.
2 changes: 1 addition & 1 deletion web/satellite/src/store/modules/projectsStore.ts
Expand Up @@ -154,7 +154,7 @@ export const useProjectsStore = defineStore('projects', () => {
}

async function createDefaultProject(userID: string): Promise<void> {
const UNTITLED_PROJECT_NAME = 'My First Project';
const UNTITLED_PROJECT_NAME = 'My Storj Project';
const UNTITLED_PROJECT_DESCRIPTION = '___';

const project = new ProjectFields(
Expand Down
42 changes: 38 additions & 4 deletions web/satellite/vuetify-poc/src/layouts/default/AllProjects.vue
Expand Up @@ -3,7 +3,10 @@

<template>
<v-app>
<session-wrapper>
<div v-if="isLoading" class="d-flex align-center justify-center w-100 h-100">
<v-progress-circular color="primary" indeterminate size="64" />
</div>
<session-wrapper v-else>
<default-bar />
<default-view />

Expand All @@ -14,24 +17,33 @@
</template>

<script setup lang="ts">
import { VApp } from 'vuetify/components';
import { onBeforeMount } from 'vue';
import { VApp, VProgressCircular } from 'vuetify/components';
import { onBeforeMount, onBeforeUnmount, ref } from 'vue';
import { useRouter } from 'vue-router';
import DefaultBar from './AppBar.vue';
import DefaultView from './View.vue';
import { useAppStore } from '@poc/store/appStore';
import { useUsersStore } from '@/store/modules/usersStore';
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { useNotify } from '@/utils/hooks';
import { useProjectsStore } from '@/store/modules/projectsStore';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import SessionWrapper from '@poc/components/utils/SessionWrapper.vue';
import UpgradeAccountDialog from '@poc/components/dialogs/upgradeAccountFlow/UpgradeAccountDialog.vue';
import BrowserSnackbarComponent from '@poc/components/BrowserSnackbarComponent.vue';
const appStore = useAppStore();
const usersStore = useUsersStore();
const projectsStore = useProjectsStore();
const analyticsStore = useAnalyticsStore();
const notify = useNotify();
const router = useRouter();
const isLoading = ref<boolean>(true);
/**
* Lifecycle hook after initial render.
Expand All @@ -40,8 +52,30 @@ const notify = useNotify();
onBeforeMount(async () => {
try {
await usersStore.getSettings();
await usersStore.getUser();
await projectsStore.getProjects();
const invites = await projectsStore.getUserInvitations();
const projects = projectsStore.state.projects;
if (appStore.state.hasJustLoggedIn && !invites.length && projects.length <= 1) {
if (!projects.length) {
await projectsStore.createDefaultProject(usersStore.state.user.id);
} else {
projectsStore.selectProject(projects[0].id);
await router.push(`/projects/${projectsStore.state.selectedProject.urlId}/dashboard`);
analyticsStore.pageVisit('/projects/dashboard');
analyticsStore.eventTriggered(AnalyticsEvent.NAVIGATE_PROJECTS);
}
}
} catch (error) {
notify.notifyError(error, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
}
isLoading.value = false;
});
onBeforeUnmount(() => {
appStore.toggleHasJustLoggedIn(false);
});
</script>
12 changes: 9 additions & 3 deletions web/satellite/vuetify-poc/src/router/index.ts
Expand Up @@ -20,6 +20,7 @@ export enum RouteName {
Access = 'Access',
Team = 'Team',
ProjectSettings = 'Project Settings',
Login = 'Login',
}

const routes: RouteRecordRaw[] = [
Expand All @@ -33,7 +34,7 @@ const routes: RouteRecordRaw[] = [
children: [
{
path: '/login',
name: 'Login',
name: RouteName.Login,
component: () => import(/* webpackChunkName: "Login" */ '@poc/views/Login.vue'),
},
{
Expand Down Expand Up @@ -145,8 +146,13 @@ export const router = createRouter({
routes,
});

router.beforeEach((to, _, next) => {
useAppStore().setIsNavigating(true);
router.beforeEach((to, from, next) => {
const appStore = useAppStore();
appStore.setIsNavigating(true);

if (to.name === RouteName.Projects && from.name === RouteName.Login) {
appStore.toggleHasJustLoggedIn(true);
}

next();
});
Expand Down
10 changes: 10 additions & 0 deletions web/satellite/vuetify-poc/src/store/appStore.ts
Expand Up @@ -9,6 +9,7 @@ class AppState {
public isUpgradeFlowDialogShown = false;
public isAccountSetupDialogShown = false;
public pathBeforeAccountPage: string | null = null;
public hasJustLoggedIn = false;
public isNavigating = false;
}

Expand All @@ -27,6 +28,14 @@ export const useAppStore = defineStore('vuetifyApp', () => {
state.isAccountSetupDialogShown = isShown ?? !state.isAccountSetupDialogShown;
}

function toggleHasJustLoggedIn(hasJustLoggedIn: boolean | null = null): void {
if (hasJustLoggedIn === null) {
state.hasJustLoggedIn = !state.hasJustLoggedIn;
return;
}
state.hasJustLoggedIn = hasJustLoggedIn;
}

function setPathBeforeAccountPage(path: string) {
state.pathBeforeAccountPage = path;
}
Expand All @@ -43,6 +52,7 @@ export const useAppStore = defineStore('vuetifyApp', () => {

return {
state,
toggleHasJustLoggedIn,
toggleNavigationDrawer,
toggleUpgradeFlow,
toggleAccountSetup,
Expand Down
5 changes: 1 addition & 4 deletions web/satellite/vuetify-poc/src/views/Login.vue
Expand Up @@ -138,7 +138,7 @@
</template>

<script setup lang="ts">
import { VAlert, VBtn, VCard, VCardItem, VCardText, VCol, VContainer, VForm, VRow, VSelect, VTextField } from 'vuetify/components';
import { VAlert, VBtn, VCard, VCardText, VCol, VContainer, VForm, VRow, VSelect, VTextField } from 'vuetify/components';
import { computed, onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import VueHcaptcha from '@hcaptcha/vue3-hcaptcha';
Expand All @@ -161,8 +161,6 @@ import { ErrorBadRequest } from '@/api/errors/ErrorBadRequest';
import Login2FA from '@poc/views/Login2FA.vue';
import PasswordInputEyeIcons from '@poc/components/PasswordInputEyeIcons.vue';
import ErrorIcon from '@/../static/images/register/ErrorInfo.svg';
const auth = new AuthHttpApi();
const analyticsStore = useAnalyticsStore();
Expand Down Expand Up @@ -289,7 +287,6 @@ async function onLoginClick(): Promise<void> {
* Then changes location to project dashboard page.
*/
async function login(): Promise<void> {
try {
const tokenInfo: TokenInfo = await auth.token(email.value, password.value, captchaResponseToken.value, passcode.value, recoveryCode.value);
LocalData.setSessionExpirationDate(tokenInfo.expiresAt);
Expand Down
16 changes: 2 additions & 14 deletions web/satellite/vuetify-poc/src/views/Projects.vue
Expand Up @@ -67,11 +67,7 @@
</template>
</v-row>

<v-row v-if="isLoading" class="justify-center">
<v-progress-circular indeterminate color="primary" size="48" />
</v-row>

<v-row v-else-if="isTableView">
<v-row v-if="isTableView">
<!-- Table view -->
<v-col>
<ProjectsTableComponent :items="items" @join-click="onJoinClicked" @invite-click="(item) => onInviteClicked(item)" />
Expand Down Expand Up @@ -109,7 +105,6 @@ import {
VBtn,
VSpacer,
VBtnToggle,
VProgressCircular,
} from 'vuetify/components';
import { useRouter } from 'vue-router';
Expand Down Expand Up @@ -146,7 +141,6 @@ const billingStore = useBillingStore();
const router = useRouter();
const isLowBalance = useLowTokenBalance();
const isLoading = ref<boolean>(true);
const joiningItem = ref<ProjectItemModel | null>(null);
const isJoinProjectDialogShown = ref<boolean>(false);
const isCreateProjectDialogShown = ref<boolean>(false);
Expand Down Expand Up @@ -229,13 +223,7 @@ function onInviteClicked(item: ProjectItemModel): void {
isAddMemberDialogShown.value = true;
}
onMounted(async (): Promise<void> => {
await usersStore.getUser().catch(_ => {});
await projectsStore.getProjects().catch(_ => {});
await projectsStore.getUserInvitations().catch(_ => {});
isLoading.value = false;
onMounted(() => {
if (configStore.state.config.nativeTokenPaymentsEnabled && configStore.state.config.billingFeaturesEnabled) {
Promise.all([
billingStore.getBalance(),
Expand Down

0 comments on commit 3c96c86

Please sign in to comment.