web/satellite: added update your session timeout banner

Added new banner to inform user that they can update their session timeout now.

Issue:
https://github.com/storj/storj/issues/5772

Change-Id: Icdf2164b80b12954d004537a4f31d30ef6bb12b8
This commit is contained in:
Vitalii 2023-04-27 14:16:53 +03:00 committed by Vitalii Shpital
parent 98562d06c8
commit 3f1166b5aa
7 changed files with 149 additions and 35 deletions

View File

@ -83,8 +83,11 @@ watch(() => props.dashboardRef, () => {
border-radius: 10px;
box-shadow: 0 7px 20px rgba(0 0 0 / 15%);
@media screen and (max-width: 800px) {
margin: 0 1.5rem;
@media screen and (max-width: 450px) {
flex-direction: column;
align-items: flex-start;
row-gap: 10px;
position: relative;
}
&__icon {
@ -123,6 +126,7 @@ watch(() => props.dashboardRef, () => {
display: flex;
align-items: center;
justify-content: space-between;
column-gap: 10px;
}
&__close {
@ -130,6 +134,13 @@ watch(() => props.dashboardRef, () => {
height: 15px;
margin-left: 2.375rem;
cursor: pointer;
flex-shrink: 0;
@media screen and (max-width: 450px) {
position: absolute;
right: 20px;
top: 20px;
}
}
}
@ -146,11 +157,4 @@ watch(() => props.dashboardRef, () => {
text-decoration: underline !important;
cursor: pointer;
}
@media screen and (max-width: 500px) {
.notification-wrap {
right: 15px;
}
}
</style>

View File

@ -0,0 +1,56 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<v-banner
severity="info"
:dashboard-ref="dashboardRef"
:on-close="onCloseClick"
>
<template #text>
<p class="medium">
You can now update your session timeout from your
<span class="link" @click.stop.self="redirectToSettingsPage">account settings</span>
</p>
</template>
</v-banner>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
import { RouteConfig } from '@/router';
import { useRouter } from '@/utils/hooks';
import { useAppStore } from '@/store/modules/appStore';
import VBanner from '@/components/common/VBanner.vue';
const appStore = useAppStore();
const nativeRouter = useRouter();
const router = reactive(nativeRouter);
const props = defineProps<{
dashboardRef: HTMLElement
}>();
/**
* Redirects to settings page.
*/
function redirectToSettingsPage(): void {
onCloseClick();
if (router.currentRoute.path.includes(RouteConfig.AllProjectsDashboard.path)) {
router.push(RouteConfig.AccountSettings.with(RouteConfig.Settings2).path);
return;
}
router.push(RouteConfig.Account.with(RouteConfig.Settings).path);
}
/**
* Closes notification.
*/
function onCloseClick(): void {
appStore.closeUpdateSessionTimeoutBanner();
}
</script>

View File

@ -455,6 +455,7 @@ onBeforeUnmount((): void => {
<style scoped lang="scss">
.project-dashboard {
max-width: calc(100vw - 280px - 95px);
background-origin: content-box;
background-image: url('../../../../static/images/project/background.png');
background-position: top right;
background-size: 70%;

View File

@ -7,10 +7,12 @@ import { defineStore } from 'pinia';
import { OnboardingOS, PricingPlanInfo } from '@/types/common';
import { FetchState } from '@/utils/constants/fetchStateEnum';
import { ManageProjectPassphraseStep } from '@/types/managePassphrase';
import { LocalData } from '@/utils/localData';
class AppState {
public fetchState = FetchState.LOADING;
public isSuccessfulPasswordResetShown = false;
public isUpdateSessionTimeoutBanner = !LocalData.getSessionTimeoutBannerAcknowledged();
public hasJustLoggedIn = false;
public onbAGStepBackRoute = '';
public onbAPIKeyStepBackRoute = '';
@ -125,6 +127,12 @@ export const useAppStore = defineStore('app', () => {
state.isLargeUploadNotificationShown = value;
}
function closeUpdateSessionTimeoutBanner(): void {
LocalData.setSessionTimeoutBannerAcknowledged();
state.isUpdateSessionTimeoutBanner = false;
}
function closeDropdowns(): void {
state.activeDropdown = '';
}
@ -174,6 +182,7 @@ export const useAppStore = defineStore('app', () => {
setLargeUploadWarningNotification,
setLargeUploadNotification,
closeDropdowns,
closeUpdateSessionTimeoutBanner,
setErrorPage,
removeErrorPage,
clear,

View File

@ -9,6 +9,7 @@ export class LocalData {
private static bucketWasCreated = 'bucketWasCreated';
private static demoBucketCreated = 'demoBucketCreated';
private static bucketGuideHidden = 'bucketGuideHidden';
private static sessionTimeoutBannerAcknowledged = 'sessionTimeoutBannerAcknowledged';
private static serverSideEncryptionBannerHidden = 'serverSideEncryptionBannerHidden';
private static serverSideEncryptionModalHidden = 'serverSideEncryptionModalHidden';
private static largeUploadNotificationDismissed = 'largeUploadNotificationDismissed';
@ -61,6 +62,14 @@ export class LocalData {
return value === 'true';
}
public static getSessionTimeoutBannerAcknowledged(): boolean {
return Boolean(localStorage.getItem(LocalData.sessionTimeoutBannerAcknowledged));
}
public static setSessionTimeoutBannerAcknowledged(): void {
localStorage.setItem(LocalData.sessionTimeoutBannerAcknowledged, 'true');
}
/**
* "Disable" showing the server-side encryption banner on the bucket page
*/

View File

@ -16,6 +16,11 @@
<BetaSatBar v-if="isBetaSatellite" />
<MFARecoveryCodeBar v-if="showMFARecoveryCodeBar" :open-generate-modal="generateNewMFARecoveryCodes" />
<div class="banner-container dashboard__wrap__main-area__content-wrap__container__content">
<UpdateSessionTimeoutBanner
v-if="isUpdateSessionTimeoutBanner && dashboardContent"
:dashboard-ref="dashboardContent"
/>
<UpgradeNotification
v-if="isPaidTierBannerShown"
:open-add-p-m-modal="togglePMModal"
@ -167,6 +172,7 @@ import VBanner from '@/components/common/VBanner.vue';
import UpgradeNotification from '@/components/notifications/UpgradeNotification.vue';
import ProjectLimitBanner from '@/components/notifications/ProjectLimitBanner.vue';
import BrandedLoader from '@/components/common/BrandedLoader.vue';
import UpdateSessionTimeoutBanner from '@/components/notifications/UpdateSessionTimeoutBanner.vue';
import CloudIcon from '@/../static/images/notifications/cloudAlert.svg';
import WarningIcon from '@/../static/images/notifications/circleWarning.svg';
@ -226,6 +232,13 @@ const sessionRefreshInterval = computed((): number => {
return sessionDuration.value / 2;
});
/**
* Indicates whether the update session timeout notification should be shown.
*/
const isUpdateSessionTimeoutBanner = computed((): boolean => {
return router.currentRoute.name !== RouteConfig.Settings.name && appStore.state.isUpdateSessionTimeoutBanner;
});
/**
* Indicates whether to display the session timer for debugging.
*/
@ -713,10 +726,17 @@ onMounted(async () => {
}
try {
await usersStore.getUser();
await usersStore.getFrozenStatus();
await abTestingStore.fetchValues();
await usersStore.getSettings();
await Promise.all([
usersStore.getUser(),
usersStore.getFrozenStatus(),
abTestingStore.fetchValues(),
usersStore.getSettings(),
]);
if (usersStore.state.settings.sessionDuration && appStore.state.isUpdateSessionTimeoutBanner) {
appStore.closeUpdateSessionTimeoutBanner();
}
setupSessionTimers();
} catch (error) {
if (!(error instanceof ErrorUnauthorized)) {
@ -898,6 +918,10 @@ onBeforeUnmount(() => {
.dashboard__wrap__main-area__content-wrap__container__content {
padding: 32px 24px 50px;
}
.banner-container {
padding-bottom: 0;
}
}
@media screen and (max-width: 500px) {

View File

@ -18,6 +18,11 @@
<div class="all-dashboard__content__divider" />
<div class="all-dashboard__banners">
<UpdateSessionTimeoutBanner
v-if="isUpdateSessionTimeoutBanner && dashboardContent"
:dashboard-ref="dashboardContent"
/>
<UpgradeNotification
v-if="isPaidTierBannerShown"
class="all-dashboard__banners__upgrade"
@ -151,6 +156,7 @@ import LimitWarningModal from '@/components/modals/LimitWarningModal.vue';
import VBanner from '@/components/common/VBanner.vue';
import UpgradeNotification from '@/components/notifications/UpgradeNotification.vue';
import ProjectLimitBanner from '@/components/notifications/ProjectLimitBanner.vue';
import UpdateSessionTimeoutBanner from '@/components/notifications/UpdateSessionTimeoutBanner.vue';
import LoaderImage from '@/../static/images/common/loadIcon.svg';
@ -209,6 +215,13 @@ const sessionRefreshInterval = computed((): number => {
return sessionDuration.value / 2;
});
/**
* Indicates whether the update session timeout notification should be shown.
*/
const isUpdateSessionTimeoutBanner = computed((): boolean => {
return router.currentRoute.name !== RouteConfig.Settings2.name && appStore.state.isUpdateSessionTimeoutBanner;
});
/**
* Indicates whether to display the session timer for debugging.
*/
@ -427,7 +440,7 @@ async function handleInactive(): Promise<void> {
} catch (error) {
if (error instanceof ErrorUnauthorized) return;
await notify.error(error.message, AnalyticsErrorEventSource.OVERALL_SESSION_EXPIRED_ERROR);
notify.error(error.message, AnalyticsErrorEventSource.OVERALL_SESSION_EXPIRED_ERROR);
}
}
@ -439,7 +452,7 @@ async function generateNewMFARecoveryCodes(): Promise<void> {
await usersStore.generateUserMFARecoveryCodes();
toggleMFARecoveryModal();
} catch (error) {
await notify.error(error.message, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
notify.error(error.message, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
}
}
@ -520,7 +533,7 @@ function restartSessionTimers(): void {
inactivityModalShown.value = true;
inactivityTimerId.value = setTimeout(async () => {
await clearStoreAndTimers();
await notify.notify('Your session was timed out.');
notify.notify('Your session was timed out.');
}, inactivityModalTime);
}, sessionDuration.value - inactivityModalTime);
@ -553,7 +566,7 @@ async function refreshSession(): Promise<void> {
try {
LocalData.setSessionExpirationDate(await auth.refreshSession());
} catch (error) {
await notify.error((error instanceof ErrorUnauthorized) ? 'Your session was timed out.' : error.message, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
notify.error((error instanceof ErrorUnauthorized) ? 'Your session was timed out.' : error.message, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
await handleInactive();
isSessionRefreshing.value = false;
return;
@ -594,10 +607,17 @@ onMounted(async () => {
});
try {
await usersStore.getUser();
await usersStore.getFrozenStatus();
await abTestingStore.fetchValues();
await usersStore.getSettings();
await Promise.all([
usersStore.getUser(),
usersStore.getFrozenStatus(),
abTestingStore.fetchValues(),
usersStore.getSettings(),
]);
if (usersStore.state.settings.sessionDuration && appStore.state.isUpdateSessionTimeoutBanner) {
appStore.closeUpdateSessionTimeoutBanner();
}
setupSessionTimers();
} catch (error) {
if (!(error instanceof ErrorUnauthorized)) {
@ -614,26 +634,26 @@ onMounted(async () => {
agStore.stopWorker();
await agStore.startWorker();
} catch (error) {
await notify.error(`Unable to set access grants wizard. ${error.message}`, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
notify.error(`Unable to set access grants wizard. ${error.message}`, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
}
try {
const couponType = await billingStore.setupAccount();
if (couponType === CouponType.NoCoupon) {
await notify.error(`The coupon code was invalid, and could not be applied to your account`, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
notify.error(`The coupon code was invalid, and could not be applied to your account`, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
}
if (couponType === CouponType.SignupCoupon) {
await notify.success(`The coupon code was added successfully`);
notify.success(`The coupon code was added successfully`);
}
} catch (error) {
await notify.error(`Unable to setup account. ${error.message}`, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
notify.error(`Unable to setup account. ${error.message}`, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
}
try {
await billingStore.getCreditCards();
} catch (error) {
await notify.error(`Unable to get credit cards. ${error.message}`, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
notify.error(`Unable to get credit cards. ${error.message}`, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
}
try {
@ -718,15 +738,6 @@ onBeforeUnmount(() => {
&__banners {
margin-bottom: 20px;
&__billing {
position: initial;
margin-top: 20px;
& :deep(.notification-wrap__content) {
position: initial;
}
}
&__upgrade,
&__project-limit,
&__freeze,