web/satellite: added manage passphrase modal

Added manage passphrase modal where user can create new, switch or clear their project level passphrase.
Removed debug buttons from navigation.

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

Change-Id: I53f263a9697f197cee3f68634f5799d45b3eb193
This commit is contained in:
Vitalii 2022-12-02 17:39:06 +02:00
parent 4b72314e90
commit 9a0c2dd878
20 changed files with 762 additions and 88 deletions

View File

@ -22,6 +22,7 @@
<AddCouponCodeModal v-if="isAddCouponModal" />
<NewBillingAddCouponCodeModal v-if="isNewBillingAddCouponModal" />
<CreateProjectPassphraseModal v-if="isCreateProjectPassphraseModal" />
<ManageProjectPassphraseModal v-if="isManageProjectPassphraseModal" />
</div>
</template>
@ -47,10 +48,12 @@ import DeleteBucketModal from '@/components/modals/DeleteBucketModal.vue';
import AddCouponCodeModal from '@/components/modals/AddCouponCodeModal.vue';
import NewBillingAddCouponCodeModal from '@/components/modals/NewBillingAddCouponCodeModal.vue';
import CreateProjectPassphraseModal from '@/components/modals/createProjectPassphrase/CreateProjectPassphraseModal.vue';
import ManageProjectPassphraseModal from '@/components/modals/manageProjectPassphrase/ManageProjectPassphraseModal.vue';
// @vue/component
@Component({
components: {
ManageProjectPassphraseModal,
CreateProjectPassphraseModal,
DeleteBucketModal,
CreateProjectPromptModal,
@ -206,5 +209,12 @@ export default class AllModals extends Vue {
public get isCreateProjectPassphraseModal(): boolean {
return this.$store.state.appStateModule.appState.isCreateProjectPassphraseModalShown;
}
/**
* Indicates if manage project passphrase modal is shown.
*/
public get isManageProjectPassphraseModal(): boolean {
return this.$store.state.appStateModule.appState.isManageProjectPassphraseModalShown;
}
}
</script>

View File

@ -0,0 +1,91 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="clear-step">
<h1 class="clear-step__title">Clear my passphrase</h1>
<p class="clear-step__info">
By choosing to clear your passphrase for this session, your data will become locked while you can use the
rest of the dashboard.
</p>
<div class="clear-step__buttons">
<VButton
label="Cancel"
width="100%"
height="48px"
:is-white="true"
:on-press="onCancel"
/>
<VButton
label="Clear my passphrase"
width="100%"
height="48px"
:on-press="onClear"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { useStore } from '@/utils/hooks';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { OBJECTS_MUTATIONS } from '@/store/modules/objects';
import VButton from '@/components/common/VButton.vue';
const props = withDefaults(defineProps<{
onCancel?: () => void,
}>(), {
onCancel: () => () => {},
});
const store = useStore();
/**
* Clears passphrase and edge credentials.
*/
function onClear(): void {
store.commit(OBJECTS_MUTATIONS.CLEAR);
store.commit(APP_STATE_MUTATIONS.TOGGLE_MANAGE_PROJECT_PASSPHRASE_MODAL_SHOWN);
}
</script>
<style scoped lang="scss">
.clear-step {
display: flex;
flex-direction: column;
align-items: center;
font-family: 'font_regular', sans-serif;
max-width: 433px;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 32px;
line-height: 39px;
color: #1b2533;
margin: 14px 0;
}
&__info {
font-size: 14px;
line-height: 19px;
color: #354049;
margin-bottom: 24px;
}
&__buttons {
display: flex;
align-items: center;
justify-content: center;
column-gap: 33px;
margin-top: 20px;
width: 100%;
@media screen and (max-width: 530px) {
column-gap: unset;
flex-direction: column-reverse;
row-gap: 15px;
}
}
}
</style>

View File

@ -0,0 +1,90 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="create-step">
<h1 class="create-step__title">Create a new passphrase</h1>
<p class="create-step__info">
Creating a new passphrase allows you to upload data separately from the data uploaded with the current
encryption passphrase.
</p>
<div class="create-step__buttons">
<VButton
label="Cancel"
width="100%"
height="48px"
:is-white="true"
:on-press="onCancel"
/>
<VButton
label="Next"
width="100%"
height="48px"
:on-press="onNext"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { useStore } from '@/utils/hooks';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import VButton from '@/components/common/VButton.vue';
const props = withDefaults(defineProps<{
onCancel?: () => void,
}>(), {
onCancel: () => () => {},
});
const store = useStore();
/**
* Starts create new passphrase flow.
*/
function onNext(): void {
store.commit(APP_STATE_MUTATIONS.TOGGLE_MANAGE_PROJECT_PASSPHRASE_MODAL_SHOWN);
store.commit(APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_PASSPHRASE_MODAL_SHOWN);
}
</script>
<style scoped lang="scss">
.create-step {
display: flex;
flex-direction: column;
align-items: center;
font-family: 'font_regular', sans-serif;
max-width: 433px;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 32px;
line-height: 39px;
color: #1b2533;
margin: 14px 0;
}
&__info {
font-size: 14px;
line-height: 19px;
color: #354049;
margin-bottom: 24px;
}
&__buttons {
display: flex;
align-items: center;
justify-content: center;
column-gap: 33px;
margin-top: 20px;
width: 100%;
@media screen and (max-width: 530px) {
column-gap: unset;
flex-direction: column-reverse;
row-gap: 15px;
}
}
}
</style>

View File

@ -0,0 +1,137 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="manage-options">
<h1 class="manage-options__title">Manage passphrases</h1>
<p class="manage-options__info">
Here are all the options for your encryption passphrases.
</p>
<div class="manage-options__option" @click="setCreate">
<PlusIcon class="manage-options__option__icon" />
<div class="manage-options__option__info">
<h2 class="manage-options__option__info__title">Create a new passphrase</h2>
<p class="manage-options__option__info__msg">Allows you to upload data with a different passphrase.</p>
</div>
<ChevronIcon class="manage-options__option__arrow" />
</div>
<div class="manage-options__option middle" @click="setSwitch">
<SwitchIcon class="manage-options__option__icon" />
<div class="manage-options__option__info">
<h2 class="manage-options__option__info__title">Switch active passphrase</h2>
<p class="manage-options__option__info__msg">View and upload data using another passphrase. </p>
</div>
<ChevronIcon class="manage-options__option__arrow" />
</div>
<div class="manage-options__option" @click="setClear">
<ClearIcon class="manage-options__option__icon" />
<div class="manage-options__option__info">
<h2 class="manage-options__option__info__title">Clear saved passphrase</h2>
<p class="manage-options__option__info__msg">Lock your data and clear passphrase from this session.</p>
</div>
<ChevronIcon class="manage-options__option__arrow" />
</div>
</div>
</template>
<script setup lang="ts">
import PlusIcon from '@/../static/images/projectPassphrase/plus.svg';
import SwitchIcon from '@/../static/images/projectPassphrase/switch.svg';
import ClearIcon from '@/../static/images/projectPassphrase/clear.svg';
import ChevronIcon from '@/../static/images/projectPassphrase/chevron.svg';
const props = withDefaults(defineProps<{
setCreate?: () => void,
setSwitch?: () => void,
setClear?: () => void,
}>(), {
setCreate: () => () => {},
setSwitch: () => () => {},
setClear: () => () => {},
});
</script>
<style scoped lang="scss">
.manage-options {
display: flex;
flex-direction: column;
align-items: center;
font-family: 'font_regular', sans-serif;
max-width: 433px;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 32px;
line-height: 39px;
color: #1b2533;
margin: 14px 0;
}
&__info {
font-size: 14px;
line-height: 19px;
color: #354049;
margin-bottom: 24px;
}
&__option {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 20px;
width: calc(100% - 40px);
background: #fafafb;
border: 1px solid #d8dee3;
border-radius: 10px;
cursor: pointer;
&__icon {
min-width: 32px;
min-height: 32px;
}
&__info {
margin: 0 8px 0 16px;
width: 100%;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 14px;
line-height: 20px;
color: #000;
margin-bottom: 8px;
text-align: left;
}
&__msg {
font-size: 12px;
line-height: 18px;
color: #000;
text-align: left;
}
}
&__arrow {
min-height: 20px;
min-width: 20px;
}
&:hover {
background-color: #fff;
border-color: #0149ff;
h2 {
color: #0d6efd;
}
:deep(path) {
fill: #0149ff;
}
}
}
}
.middle {
margin: 18px 0;
}
</style>

View File

@ -0,0 +1,102 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<VModal :on-close="closeModal">
<template #content>
<div class="modal">
<LockIcon />
<ManageOptionsStep
v-if="activeStep === ManageProjectPassphraseStep.ManageOptions"
:set-create="setCreate"
:set-switch="setSwitch"
:set-clear="setClear"
/>
<CreateStep
v-if="activeStep === ManageProjectPassphraseStep.Create"
:on-cancel="setManageOptions"
/>
<SwitchStep
v-if="activeStep === ManageProjectPassphraseStep.Switch"
:on-cancel="setManageOptions"
/>
<ClearStep
v-if="activeStep === ManageProjectPassphraseStep.Clear"
:on-cancel="setManageOptions"
/>
</div>
</template>
</VModal>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useNotify, useStore } from '@/utils/hooks';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import VModal from '@/components/common/VModal.vue';
import ManageOptionsStep from '@/components/modals/manageProjectPassphrase/ManageOptionsStep.vue';
import CreateStep from '@/components/modals/manageProjectPassphrase/CreateStep.vue';
import SwitchStep from '@/components/modals/manageProjectPassphrase/SwitchStep.vue';
import ClearStep from '@/components/modals/manageProjectPassphrase/ClearStep.vue';
import LockIcon from '@/../static/images/projectPassphrase/lock.svg';
enum ManageProjectPassphraseStep {
ManageOptions = 'ManageOptions',
Create = 'Create',
Switch = 'Switch',
Clear = 'Clear',
}
const store = useStore();
const notify = useNotify();
const activeStep = ref<ManageProjectPassphraseStep>(ManageProjectPassphraseStep.ManageOptions);
/**
* Sets flow to create step.
*/
function setCreate(): void {
activeStep.value = ManageProjectPassphraseStep.Create;
}
/**
* Sets flow to switch step.
*/
function setSwitch(): void {
activeStep.value = ManageProjectPassphraseStep.Switch;
}
/**
* Sets flow to clear step.
*/
function setClear(): void {
activeStep.value = ManageProjectPassphraseStep.Clear;
}
/**
* Sets flow to manage options step.
*/
function setManageOptions(): void {
activeStep.value = ManageProjectPassphraseStep.ManageOptions;
}
/**
* Closes modal.
*/
function closeModal(): void {
store.commit(APP_STATE_MUTATIONS.TOGGLE_MANAGE_PROJECT_PASSPHRASE_MODAL_SHOWN);
}
</script>
<style scoped lang="scss">
.modal {
padding: 40px 60px 68px;
@media screen and (max-width: 615px) {
padding: 30px 20px;
}
}
</style>

View File

@ -0,0 +1,242 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="switch-step">
<h1 class="switch-step__title">Switch passphrase</h1>
<p class="switch-step__info">
Switch passphrases to view existing data that is uploaded with a different passphrase, or upload new data.
Please note that you wont see the previous data once you switch passphrases.
</p>
<VInput
label="Encryption Passphrase"
:is-password="true"
width="100%"
height="56px"
placeholder="Enter Encryption Passphrase"
:error="enterError"
@setData="setPassphrase"
/>
<div class="switch-step__buttons">
<VButton
label="Cancel"
width="100%"
height="48px"
:is-white="true"
:on-press="onCancel"
/>
<VButton
label="Switch Passphrase"
width="100%"
height="48px"
:on-press="onSwitch"
:is-disabled="isLoading"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useStore } from '@/utils/hooks';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { AccessGrant, EdgeCredentials } from '@/types/accessGrants';
import { OBJECTS_ACTIONS, OBJECTS_MUTATIONS } from '@/store/modules/objects';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { MetaUtils } from '@/utils/meta';
import VButton from '@/components/common/VButton.vue';
import VInput from '@/components/common/VInput.vue';
const FILE_BROWSER_AG_NAME = 'Web file browser API key';
const props = withDefaults(defineProps<{
onCancel?: () => void,
}>(), {
onCancel: () => () => {},
});
const notify = useNotify();
const store = useStore();
const passphrase = ref<string>('');
const enterError = ref<string>('');
const isLoading = ref<boolean>(false);
const worker = ref<Worker | null>(null);
/**
* Returns web file browser api key from vuex state.
*/
const apiKey = computed((): string => {
return store.state.objectsModule.apiKey;
});
/**
* Lifecycle hook after initial render.
* Sets local worker.
*/
onMounted(() => {
setWorker();
});
/**
* Sets passphrase input value to local variable.
* Resets error is present.
* @param value
*/
function setPassphrase(value: string): void {
if (enterError.value) {
enterError.value = '';
}
passphrase.value = value;
}
/**
* Sets local worker with worker instantiated in store.
*/
function setWorker(): void {
worker.value = store.state.accessGrantsModule.accessGrantsWebWorker;
if (worker.value) {
worker.value.onerror = (error: ErrorEvent) => {
notify.error(error.message);
};
}
}
/**
* Generates s3 credentials from provided passphrase and stores it in vuex state to be reused.
*/
async function setAccess(): Promise<void> {
if (!worker.value) {
notify.error('Worker is not defined');
return;
}
if (!apiKey.value) {
await store.dispatch(ACCESS_GRANTS_ACTIONS.DELETE_BY_NAME_AND_PROJECT_ID, FILE_BROWSER_AG_NAME);
const cleanAPIKey: AccessGrant = await store.dispatch(ACCESS_GRANTS_ACTIONS.CREATE, FILE_BROWSER_AG_NAME);
await store.dispatch(OBJECTS_ACTIONS.SET_API_KEY, cleanAPIKey.secret);
}
const now = new Date();
const inThreeDays = new Date(now.setDate(now.getDate() + 3));
await worker.value.postMessage({
'type': 'SetPermission',
'isDownload': true,
'isUpload': true,
'isList': true,
'isDelete': true,
'notAfter': inThreeDays.toISOString(),
'buckets': [],
'apiKey': apiKey.value,
});
const grantEvent: MessageEvent = await new Promise(resolve => {
if (worker.value) {
worker.value.onmessage = resolve;
}
});
if (grantEvent.data.error) {
throw new Error(grantEvent.data.error);
}
const salt = await store.dispatch(PROJECTS_ACTIONS.GET_SALT, store.getters.selectedProject.id);
const satelliteNodeURL: string = MetaUtils.getMetaContent('satellite-nodeurl');
worker.value.postMessage({
'type': 'GenerateAccess',
'apiKey': grantEvent.data.value,
'passphrase': passphrase.value,
'salt': salt,
'satelliteNodeURL': satelliteNodeURL,
});
const accessGrantEvent: MessageEvent = await new Promise(resolve => {
if (worker.value) {
worker.value.onmessage = resolve;
}
});
if (accessGrantEvent.data.error) {
throw new Error(accessGrantEvent.data.error);
}
const accessGrant = accessGrantEvent.data.value;
const gatewayCredentials: EdgeCredentials = await store.dispatch(ACCESS_GRANTS_ACTIONS.GET_GATEWAY_CREDENTIALS, { accessGrant });
await store.dispatch(OBJECTS_ACTIONS.SET_GATEWAY_CREDENTIALS, gatewayCredentials);
await store.dispatch(OBJECTS_ACTIONS.SET_S3_CLIENT);
store.commit(OBJECTS_MUTATIONS.SET_PROMPT_FOR_PASSPHRASE, false);
}
/**
* Sets new passphrase and generates new edge credentials.
*/
async function onSwitch(): Promise<void> {
if (isLoading.value) return;
if (!passphrase.value) {
enterError.value = 'Passphrase can\'t be empty';
return;
}
isLoading.value = true;
try {
await setAccess();
store.dispatch(OBJECTS_ACTIONS.SET_PASSPHRASE, passphrase.value);
notify.success('Passphrase was switched successfully');
store.commit(APP_STATE_MUTATIONS.TOGGLE_MANAGE_PROJECT_PASSPHRASE_MODAL_SHOWN);
} catch (e) {
await notify.error(e.message);
} finally {
isLoading.value = false;
}
}
</script>
<style scoped lang="scss">
.switch-step {
display: flex;
flex-direction: column;
align-items: center;
font-family: 'font_regular', sans-serif;
max-width: 433px;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 32px;
line-height: 39px;
color: #1b2533;
margin: 14px 0;
}
&__info {
font-size: 14px;
line-height: 19px;
color: #354049;
margin-bottom: 24px;
}
&__buttons {
display: flex;
align-items: center;
justify-content: center;
column-gap: 33px;
margin-top: 20px;
width: 100%;
@media screen and (max-width: 530px) {
column-gap: unset;
flex-direction: column-reverse;
row-gap: 15px;
}
}
}
</style>

View File

@ -47,6 +47,10 @@
<p class="project-selection__dropdown__items__choice__unselected">{{ project.name }}</p>
</div>
</div>
<div v-if="isNewEncryptionPassphraseFlowEnabled" tabindex="0" class="project-selection__dropdown__link-container" @click.stop="onManagePassphraseClick" @keyup.enter="onManagePassphraseClick">
<PassphraseIcon />
<p class="project-selection__dropdown__link-container__label">Manage Passphrase</p>
</div>
<div class="project-selection__dropdown__link-container" @click.stop="onProjectsLinkClick">
<ManageIcon />
<p class="project-selection__dropdown__link-container__label">Manage Projects</p>
@ -195,6 +199,7 @@ import CreateProjectIcon from '@/../static/images/navigation/createProject.svg';
import InfoIcon from '@/../static/images/navigation/info.svg';
import LogoutIcon from '@/../static/images/navigation/logout.svg';
import ManageIcon from '@/../static/images/navigation/manage.svg';
import PassphraseIcon from '@/../static/images/navigation/passphrase.svg';
import MenuIcon from '@/../static/images/navigation/menu.svg';
import ProjectIcon from '@/../static/images/navigation/project.svg';
import DashboardIcon from '@/../static/images/navigation/projectDashboard.svg';
@ -225,6 +230,7 @@ import UsersIcon from '@/../static/images/navigation/users.svg';
CheckmarkIcon,
ProjectIcon,
ManageIcon,
PassphraseIcon,
CreateProjectIcon,
VLoader,
CrossIcon,
@ -337,10 +343,21 @@ export default class MobileNavigation extends Vue {
}
/**
* Indicates if current route is onboarding tour.
* Toggles manage passphrase modal shown.
*/
public get isOnboardingTour(): boolean {
return this.$route.path.includes(RouteConfig.OnboardingTour.path);
public onManagePassphraseClick(): void {
if (!this.isNewEncryptionPassphraseFlowEnabled) {
return;
}
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_MANAGE_PROJECT_PASSPHRASE_MODAL_SHOWN);
}
/**
* Indicates if new encryption passphrase flow is enabled.
*/
public get isNewEncryptionPassphraseFlowEnabled(): boolean {
return this.$store.state.appStateModule.isNewEncryptionPassphraseFlowEnabled;
}
/**

View File

@ -46,27 +46,6 @@
<ResourcesLinks />
</GuidesDropdown>
</div>
<div v-if="isNewEncryptionPassphraseFlowEnabled" class="container-wrapper">
<div
class="navigation-area__container__wrap__item-container"
:class="{ active: isSwitchEncryptionPassphraseShown }"
@click.stop="toggleEncryptionPassphraseShown"
>
<div class="navigation-area__container__wrap__item-container__left">
<p class="navigation-area__container__wrap__item-container__left__label">switch passphrase</p>
</div>
</div>
</div>
<div v-if="isNewEncryptionPassphraseFlowEnabled" class="container-wrapper">
<div
class="navigation-area__container__wrap__item-container"
@click.stop="debugShowAccess"
>
<div class="navigation-area__container__wrap__item-container__left">
<p class="navigation-area__container__wrap__item-container__left__label">debug show access</p>
</div>
</div>
</div>
<div ref="quickStartContainer" class="container-wrapper">
<div
class="navigation-area__container__wrap__item-container"
@ -248,25 +227,6 @@ export default class NavigationArea extends Vue {
this.$store.dispatch(APP_STATE_ACTIONS.TOGGLE_RESOURCES_DROPDOWN);
}
/**
* Toggles switch passphrase modal.
*/
public toggleEncryptionPassphraseShown(): void {
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_PASSPHRASE_MODAL_SHOWN);
}
public debugShowAccess(): void {
const passphrase = this.$store.state.objectsModule.passphrase;
const apiKey= this.$store.state.objectsModule.apiKey;
const gwCreds = this.$store.state.objectsModule.gatewayCredentials;
console.log('passphrase', passphrase);
console.log('api key', apiKey);
console.log('gw creds', gwCreds);
this.$notify.success('passphrase: ' + passphrase);
}
/**
* Toggles quick start dropdown visibility.
*/
@ -290,20 +250,6 @@ export default class NavigationArea extends Vue {
return this.$store.state.appStateModule.appState.isResourcesDropdownShown;
}
/**
* Indicates if new encryption passphrase flow is enabled.
*/
public get isNewEncryptionPassphraseFlowEnabled(): boolean {
return this.$store.state.appStateModule.isNewEncryptionPassphraseFlowEnabled;
}
/**
* Indicates if set passphrase modal shown.
*/
public get isSwitchEncryptionPassphraseShown(): boolean {
return this.$store.state.appStateModule.appState.isSetEncryptionPassphraseModalShown;
}
/**
* Indicates if quick start dropdown shown.
*/

View File

@ -39,6 +39,10 @@
<p class="project-selection__dropdown__items__choice__unselected">{{ project.name }}</p>
</div>
</div>
<div v-if="isNewEncryptionPassphraseFlowEnabled" tabindex="0" class="project-selection__dropdown__link-container" @click.stop="onManagePassphraseClick" @keyup.enter="onManagePassphraseClick">
<PassphraseIcon />
<p class="project-selection__dropdown__link-container__label">Manage Passphrase</p>
</div>
<div tabindex="0" class="project-selection__dropdown__link-container" @click.stop="onProjectsLinkClick" @keyup.enter="onProjectsLinkClick">
<ManageIcon />
<p class="project-selection__dropdown__link-container__label">Manage Projects</p>
@ -66,13 +70,14 @@ import { BUCKET_ACTIONS } from '@/store/modules/buckets';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { Project } from '@/types/projects';
import { User } from '@/types/users';
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
import { OBJECTS_MUTATIONS } from '@/store/modules/objects';
import VLoader from '@/components/common/VLoader.vue';
import ProjectIcon from '@/../static/images/navigation/project.svg';
import ArrowImage from '@/../static/images/navigation/arrowExpandRight.svg';
import CheckmarkIcon from '@/../static/images/navigation/checkmark.svg';
import PassphraseIcon from '@/../static/images/navigation/passphrase.svg';
import ManageIcon from '@/../static/images/navigation/manage.svg';
import CreateProjectIcon from '@/../static/images/navigation/createProject.svg';
@ -82,6 +87,7 @@ import CreateProjectIcon from '@/../static/images/navigation/createProject.svg';
ArrowImage,
CheckmarkIcon,
ProjectIcon,
PassphraseIcon,
ManageIcon,
CreateProjectIcon,
VLoader,
@ -145,8 +151,11 @@ export default class ProjectSelection extends Vue {
await this.$store.dispatch(PM_ACTIONS.SET_SEARCH_QUERY, '');
this.closeDropdown();
if (this.isNewEncryptionPassphraseFlowEnabled || this.isBucketsView) {
this.$store.commit(OBJECTS_MUTATIONS.CLEAR);
}
if (this.isBucketsView) {
await this.$store.dispatch(OBJECTS_ACTIONS.CLEAR);
await this.$router.push(RouteConfig.Buckets.path).catch(() => {return; });
return;
@ -252,6 +261,19 @@ export default class ProjectSelection extends Vue {
this.closeDropdown();
}
/**
* Toggles manage passphrase modal shown.
*/
public onManagePassphraseClick(): void {
if (!this.isNewEncryptionPassphraseFlowEnabled) {
return;
}
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_MANAGE_PROJECT_PASSPHRASE_MODAL_SHOWN);
this.closeDropdown();
}
/**
* Route to create project page.
*/
@ -273,6 +295,13 @@ export default class ProjectSelection extends Vue {
this.closeDropdown();
}
/**
* Indicates if new encryption passphrase flow is enabled.
*/
public get isNewEncryptionPassphraseFlowEnabled(): boolean {
return this.$store.state.appStateModule.isNewEncryptionPassphraseFlowEnabled;
}
/**
* Indicates if current route is objects view.
*/

View File

@ -22,7 +22,6 @@
import { Component, Vue, Watch } from 'vue-property-decorator';
import { RouteConfig } from '@/router';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
import { LocalData } from '@/utils/localData';
import { BUCKET_ACTIONS } from '@/store/modules/buckets';
@ -43,8 +42,6 @@ import WhitePlusIcon from '@/../static/images/common/plusWhite.svg';
},
})
export default class BucketsView extends Vue {
private readonly FILE_BROWSER_AG_NAME: string = 'Web file browser API key';
public isLoading = true;
public isServerSideEncryptionBannerHidden = true;
@ -82,12 +79,6 @@ export default class BucketsView extends Vue {
return;
}
if (!this.bucketsPage.buckets.length && wasDemoBucketCreated) {
await this.removeTemporaryAccessGrant();
return;
}
if (!this.bucketsPage.buckets.length && !wasDemoBucketCreated) {
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path);
await this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path);
@ -118,18 +109,6 @@ export default class BucketsView extends Vue {
this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path);
}
/**
* Removes temporary created access grant.
*/
public async removeTemporaryAccessGrant(): Promise<void> {
try {
await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.DELETE_BY_NAME_AND_PROJECT_ID, this.FILE_BROWSER_AG_NAME);
await this.$store.dispatch(OBJECTS_ACTIONS.CLEAR);
} catch (error) {
await this.$notify.error(error.message);
}
}
/**
* Hides server-side encryption banner.
*/

View File

@ -86,6 +86,11 @@ export default class UploadFile extends Vue {
return;
}
if (!this.edgeCredentials.secretKey) {
await this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path).catch(() => {return;});
return;
}
await this.$router.push(RouteConfig.Buckets.with(RouteConfig.UploadFile).path).catch(() => {return;});
await this.$store.commit('files/reinit', {
endpoint: this.edgeCredentials.endpoint,

View File

@ -22,12 +22,9 @@
:is-disabled="isLoading"
/>
</div>
<router-link
class="overview-area__skip-button"
:to="projectDashboardPath"
>
<p class="overview-area__skip-button" @click="onSkip">
Skip and go directly to dashboard
</router-link>
</p>
</div>
</template>
@ -39,6 +36,7 @@ import { RouteConfig } from '@/router';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { MetaUtils } from '@/utils/meta';
import { PartneredSatellite } from '@/types/common';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import OverviewContainer from '@/components/onboardingTour/steps/common/OverviewContainer.vue';
@ -78,6 +76,14 @@ export default class OverviewStep extends Vue {
this.titleLabel = 'OSP';
}
/**
* Skips onboarding flow.
*/
public onSkip(): void {
this.$router.push(this.projectDashboardPath);
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_PASSPHRASE_MODAL_SHOWN);
}
/**
* Holds button click logic.
* Redirects to next step (creating access grant).
@ -153,7 +159,7 @@ export default class OverviewStep extends Vue {
margin-top: 58px;
color: #b7c1ca;
cursor: pointer;
text-decoration: underline !important;
text-decoration: underline;
&:hover {
text-decoration: underline;

View File

@ -42,6 +42,7 @@ class ViewsState {
public isDeleteBucketModalShown = false,
public isNewFolderModalShown = false,
public isCreateProjectPassphraseModalShown = false,
public isManageProjectPassphraseModalShown = false,
public isObjectDetailsModalShown = false,
public isAddCouponModalShown = false,
public isNewBillingAddCouponModalShown = false,
@ -147,6 +148,9 @@ export const appStateModule = {
[APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_PASSPHRASE_MODAL_SHOWN](state: State): void {
state.appState.isCreateProjectPassphraseModalShown = !state.appState.isCreateProjectPassphraseModalShown;
},
[APP_STATE_MUTATIONS.TOGGLE_MANAGE_PROJECT_PASSPHRASE_MODAL_SHOWN](state: State): void {
state.appState.isManageProjectPassphraseModalShown = !state.appState.isManageProjectPassphraseModalShown;
},
[APP_STATE_MUTATIONS.TOGGLE_MFA_RECOVERY_MODAL_SHOWN](state: State): void {
state.appState.isMFARecoveryModalShown = !state.appState.isMFARecoveryModalShown;
},

View File

@ -45,6 +45,7 @@ export const APP_STATE_MUTATIONS = {
TOGGLE_NEW_BILLING_ADD_COUPON_MODAL_SHOWN: 'TOGGLE_NEW_BILLING_ADD_COUPON_MODAL_SHOWN',
TOGGLE_NEW_FOLDER_MODAL_SHOWN: 'TOGGLE_NEW_FOLDER_MODAL_SHOWN',
TOGGLE_CREATE_PROJECT_PASSPHRASE_MODAL_SHOWN: 'TOGGLE_CREATE_PROJECT_PASSPHRASE_MODAL_SHOWN',
TOGGLE_MANAGE_PROJECT_PASSPHRASE_MODAL_SHOWN: 'TOGGLE_MANAGE_PROJECT_PASSPHRASE_MODAL_SHOWN',
SHOW_DELETE_PAYMENT_METHOD_POPUP: 'SHOW_DELETE_PAYMENT_METHOD_POPUP',
SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP: 'SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP',
CLOSE_ALL: 'CLOSE_ALL',

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.87383 0.799805C10.7959 0.799805 13.1702 3.14491 13.2174 6.05572L13.2181 6.14409L13.2182 7.28151C13.4792 7.46716 13.69 7.70744 13.8444 7.99622L13.8777 8.06035C14.0505 8.40357 14.1438 8.7761 14.1477 9.68768V12.2891C14.1477 13.3012 14.0423 13.6682 13.8444 14.0382C13.6465 14.4083 13.3562 14.6987 12.9861 14.8965L12.922 14.9299C12.5788 15.1026 12.2063 15.196 11.2947 15.1997L4.51068 15.1998C3.49856 15.1998 3.13155 15.0944 2.76153 14.8965C2.39152 14.6987 2.10113 14.4083 1.90324 14.0382L1.88038 13.9947C1.70092 13.6456 1.6039 13.2766 1.59998 12.3468V9.74536C1.59998 8.73325 1.70536 8.36623 1.90324 7.99622C2.05768 7.70744 2.26847 7.46716 2.52947 7.28151L2.52955 6.14409C2.52955 3.19252 4.92226 0.799805 7.87383 0.799805ZM11.237 8.14918L4.45839 8.14929L4.32492 8.15037C3.74681 8.15762 3.56759 8.19755 3.38146 8.29709C3.24053 8.37246 3.13778 8.47521 3.06241 8.61614L3.04151 8.65706C2.95717 8.83098 2.92147 9.02767 2.91544 9.5642L2.9145 9.7453L2.9146 12.3414L2.91568 12.4749C2.92294 13.053 2.96287 13.2322 3.06241 13.4183C3.13778 13.5593 3.24053 13.662 3.38146 13.7374L3.42238 13.7583C3.5963 13.8426 3.79298 13.8783 4.32956 13.8843L4.51068 13.8853L11.3305 13.885C11.9792 13.8817 12.1712 13.8416 12.3662 13.7374C12.5071 13.662 12.6099 13.5593 12.6853 13.4183L12.7062 13.3774C12.7905 13.2035 12.8262 13.0068 12.8322 12.4702L12.8332 12.2891L12.8329 9.65184C12.8296 9.00318 12.7895 8.81111 12.6853 8.61614C12.6099 8.47521 12.5071 8.37246 12.3662 8.29709L12.3253 8.2762C12.1514 8.19185 11.9547 8.15616 11.4181 8.15012L11.237 8.14918ZM7.99334 9.58321C8.58733 9.58321 9.06886 10.0647 9.06886 10.6587C9.06886 11.0052 8.90505 11.3134 8.65067 11.5101L8.6506 11.9135C8.6506 12.2765 8.35633 12.5708 7.99334 12.5708C7.63034 12.5708 7.33608 12.2765 7.33608 11.9135L7.33612 11.5101C7.08167 11.3134 6.91782 11.0052 6.91782 10.6587C6.91782 10.0647 7.39934 9.58321 7.99334 9.58321ZM7.87383 2.11433C5.67051 2.11433 3.88019 3.88261 3.84461 6.07746L3.84407 6.14409L3.84399 6.85556C4.0167 6.84273 4.21675 6.83574 4.453 6.83478L11.237 6.83466C11.4986 6.83466 11.7172 6.8417 11.9038 6.85556L11.9036 6.14409C11.9036 3.91852 10.0994 2.11433 7.87383 2.11433Z" fill="#56606D"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.5335 9.46647C14.821 9.75394 14.828 10.2157 14.5546 10.5116L14.5335 10.5335L6.28806 18.779C5.9934 19.0737 5.51566 19.0737 5.221 18.779C4.93352 18.4915 4.92651 18.0298 5.19996 17.7338L5.221 17.7119L12.933 10.0001L5.221 2.28806C4.93352 2.00058 4.92651 1.53885 5.19996 1.24288L5.221 1.221C5.50847 0.933522 5.9702 0.92651 6.26617 1.19996L6.28806 1.221L14.5335 9.46647Z" fill="#0149FF"/>
</svg>

After

Width:  |  Height:  |  Size: 502 B

View File

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.7479 1.6001C21.592 1.6001 26.3407 6.29031 26.4351 12.1119L26.4365 12.2887L26.4366 14.5635C26.9587 14.9348 27.3802 15.4154 27.6891 15.9929L27.7557 16.1212C28.1013 16.8076 28.2879 17.5527 28.2956 19.3758V24.5787C28.2956 26.6029 28.0849 27.337 27.6891 28.077C27.2933 28.817 26.7125 29.3978 25.9725 29.7936L25.8442 29.8602C25.1578 30.2058 24.4127 30.3924 22.5896 30.3999L9.0216 30.4001C6.99737 30.4001 6.26334 30.1893 5.52331 29.7936C4.78328 29.3978 4.2025 28.817 3.80673 28.077L3.76101 27.9898C3.40208 27.2916 3.20805 26.5537 3.2002 24.6941V19.4912C3.2002 17.467 3.41096 16.7329 3.80673 15.9929C4.11561 15.4154 4.53717 14.9348 5.05918 14.5635L5.05933 12.2887C5.05933 6.38554 9.84477 1.6001 15.7479 1.6001ZM22.4743 16.2989L8.91702 16.2991L8.65008 16.3012C7.49386 16.3157 7.13541 16.3956 6.76316 16.5947C6.48129 16.7454 6.2758 16.9509 6.12506 17.2328L6.08327 17.3146C5.91458 17.6625 5.84319 18.0558 5.83112 19.1289L5.82924 19.4911L5.82945 24.6833L5.83161 24.9502C5.84612 26.1064 5.92598 26.4649 6.12506 26.8371C6.2758 27.119 6.48129 27.3245 6.76316 27.4752L6.845 27.517C7.19284 27.6857 7.58621 27.7571 8.65937 27.7692L9.0216 27.7711L22.6613 27.7706C23.9586 27.7639 24.3427 27.6838 24.7327 27.4752C25.0145 27.3245 25.22 27.119 25.3708 26.8371L25.4126 26.7553C25.5813 26.4075 25.6526 26.0141 25.6647 24.9409L25.6666 24.5787L25.6661 19.3042C25.6594 18.0068 25.5793 17.6227 25.3708 17.2328C25.22 16.9509 25.0145 16.7454 24.7327 16.5947L24.6508 16.5529C24.303 16.3842 23.9096 16.3128 22.8365 16.3007L22.4743 16.2989ZM15.9869 19.1669C17.1749 19.1669 18.138 20.13 18.138 21.3179C18.138 22.0108 17.8104 22.6272 17.3016 23.0206L17.3014 23.8275C17.3014 24.5535 16.7129 25.142 15.9869 25.142C15.2609 25.142 14.6724 24.5535 14.6724 23.8275L14.6725 23.0208C14.1636 22.6274 13.8359 22.0109 13.8359 21.3179C13.8359 20.13 14.7989 19.1669 15.9869 19.1669ZM15.7479 4.22914C11.3413 4.22914 7.76063 7.76571 7.68946 12.1554L7.68838 12.2887L7.68823 13.7116C8.03365 13.6859 8.43374 13.672 8.90624 13.67L22.4742 13.6698C22.9975 13.6698 23.4346 13.6839 23.8078 13.7116L23.8074 12.2887C23.8074 7.83752 20.1991 4.22914 15.7479 4.22914Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.0001 1.6001C23.953 1.6001 30.4001 8.0472 30.4001 16.0001C30.4001 23.953 23.953 30.4001 16.0001 30.4001C8.0472 30.4001 1.6001 23.953 1.6001 16.0001C1.6001 8.0472 8.0472 1.6001 16.0001 1.6001ZM16.0001 4.2401C9.50523 4.2401 4.2401 9.50523 4.2401 16.0001C4.2401 22.495 9.50523 27.7601 16.0001 27.7601C22.495 27.7601 27.7601 22.495 27.7601 16.0001C27.7601 9.50523 22.495 4.2401 16.0001 4.2401ZM17.1994 11.0383L17.1999 11.0596L17.1999 14.7569H20.7883C21.5247 14.7569 22.1285 15.3409 22.153 16.0769C22.1764 16.7821 21.6238 17.3727 20.9186 17.3962L20.8974 17.3967L17.1999 17.3969L17.2001 21.0737C17.2001 21.8027 16.6091 22.3937 15.8801 22.3937C15.1689 22.3937 14.589 21.8312 14.5611 21.1268L14.5601 21.0737L14.5599 17.3969H10.8833C10.1543 17.3969 9.5633 16.8059 9.5633 16.0769C9.5633 15.3657 10.1258 14.7858 10.8302 14.7579L10.8833 14.7569H14.5599L14.5601 11.1687C14.5601 10.4323 15.1441 9.82851 15.8801 9.80402C16.5853 9.78055 17.1759 10.3332 17.1994 11.0383Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.66396 15.1905L7.62508 15.1531L1.9927 9.52074C1.482 9.01003 1.46954 8.18976 1.95533 7.66396L1.9927 7.62508L7.62508 1.9927C8.14855 1.46923 8.99726 1.46923 9.52073 1.9927C10.0314 2.5034 10.0439 3.32367 9.5581 3.84948L9.52073 3.88835L6.17661 7.23271L29.0246 7.23248C29.7649 7.23248 30.365 7.83261 30.365 8.57291C30.365 9.29515 29.7938 9.88398 29.0785 9.91227L29.0246 9.91334L6.17661 9.91357L9.52073 13.2575C10.0314 13.7682 10.0439 14.5884 9.5581 15.1142L9.52073 15.1531C9.01003 15.6638 8.18976 15.6763 7.66396 15.1905ZM22.4795 29.5769C21.9688 29.0662 21.9563 28.246 22.4421 27.7202L22.4795 27.6813L25.8239 24.3373L2.97561 24.3372C2.23531 24.3372 1.63518 23.737 1.63518 22.9967C1.63518 22.2745 2.20639 21.6857 2.92169 21.6574L2.97561 21.6563L25.8239 21.6564L22.4795 18.3122C21.9688 17.8015 21.9563 16.9812 22.4421 16.4554L22.4795 16.4165C22.9902 15.9058 23.8104 15.8934 24.3362 16.3791L24.3751 16.4165L30.0075 22.0489C30.5182 22.5596 30.5307 23.3799 30.0449 23.9057L30.0075 23.9446L24.3751 29.5769C23.8516 30.1004 23.0029 30.1004 22.4795 29.5769Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -8,8 +8,8 @@ exports[`OverviewStep.vue renders correctly 1`] = `
<overviewcontainer-stub isweb="true" title="Start with web browser" info="Start uploading files in the browser and instantly see how your data gets distributed over the Storj network around the world." buttonlabel="Continue in web ->" onclick="[Function]"></overviewcontainer-stub>
<overviewcontainer-stub title="Start with Uplink CLI" info="The Uplink CLI is a command-line interface tool which allows you to upload and download files from the network, manage permissions and share files." buttonlabel="Continue in cli ->" onclick="[Function]"></overviewcontainer-stub>
</div>
<router-link-stub to="/project-dashboard" tag="a" ariacurrentvalue="page" event="click" class="overview-area__skip-button">
<p class="overview-area__skip-button">
Skip and go directly to dashboard
</router-link-stub>
</p>
</div>
`;