web/satellite: use pinia users module instead of vuex users module

Start using pinia users module instead of vuex pinia module.

Change-Id: I2a61aafa4b8f71cccd2a5825e11b315f59767289
This commit is contained in:
Vitalii 2023-04-06 18:38:48 +03:00 committed by Storj Robot
parent 5d860fddbd
commit 9c20c4ecb4
43 changed files with 302 additions and 438 deletions

View File

@ -101,7 +101,6 @@
<script setup lang="ts">
import { computed, onMounted } from 'vue';
import { USER_ACTIONS } from '@/store/modules/users';
import { User } from '@/types/users';
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
import { MODALS } from '@/utils/constants/appStatePopUps';
@ -109,9 +108,11 @@ import { useNotify, useStore } from '@/utils/hooks';
import { useLoading } from '@/composables/useLoading';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { Duration } from '@/utils/time';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const { isLoading, withLoading } = useLoading();
@ -120,14 +121,14 @@ const { isLoading, withLoading } = useLoading();
* Returns user info from store.
*/
const user = computed((): User => {
return store.getters.user;
return usersStore.state.user;
});
/**
* Returns duration from store.
*/
const userDuration = computed((): Duration | null => {
return store.state.usersModule.settings.sessionDuration;
return usersStore.state.settings.sessionDuration;
});
/**
@ -178,7 +179,7 @@ function toggleEditProfileModal(): void {
async function enableMFA(): Promise<void> {
await withLoading(async () => {
try {
await store.dispatch(USER_ACTIONS.GENERATE_USER_MFA_SECRET);
await usersStore.generateUserMFASecret();
toggleEnableMFAModal();
} catch (error) {
await notify.error(error.message, AnalyticsErrorEventSource.ACCOUNT_SETTINGS_AREA);
@ -192,7 +193,7 @@ async function enableMFA(): Promise<void> {
async function generateNewMFARecoveryCodes(): Promise<void> {
await withLoading(async () => {
try {
await store.dispatch(USER_ACTIONS.GENERATE_USER_MFA_RECOVERY_CODES);
await usersStore.generateUserMFARecoveryCodes();
toggleMFACodesModal();
} catch (error) {
await notify.error(error.message, AnalyticsErrorEventSource.ACCOUNT_SETTINGS_AREA);
@ -204,7 +205,7 @@ async function generateNewMFARecoveryCodes(): Promise<void> {
* Lifecycle hook after initial render where user info is fetching.
*/
onMounted(() => {
store.dispatch(USER_ACTIONS.GET);
usersStore.getUser();
});
</script>

View File

@ -85,13 +85,13 @@
<script setup lang="ts">
import { computed, onMounted } from 'vue';
import { USER_ACTIONS } from '@/store/modules/users';
import { User } from '@/types/users';
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
import { MODALS } from '@/utils/constants/appStatePopUps';
import { useNotify, useStore } from '@/utils/hooks';
import { useLoading } from '@/composables/useLoading';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
@ -99,6 +99,7 @@ import ChangePasswordIcon from '@/../static/images/account/profile/changePasswor
import EmailIcon from '@/../static/images/account/profile/email.svg';
import EditIcon from '@/../static/images/common/edit.svg';
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const { isLoading, withLoading } = useLoading();
@ -107,14 +108,14 @@ const { isLoading, withLoading } = useLoading();
* Returns user info from store.
*/
const user = computed((): User => {
return store.getters.user;
return usersStore.state.user;
});
/**
* Returns first letter of user name.
*/
const avatarLetter = computed((): string => {
return store.getters.userName.slice(0, 1).toUpperCase();
return usersStore.userName.slice(0, 1).toUpperCase();
});
/**
@ -158,7 +159,7 @@ function toggleEditProfileModal(): void {
async function enableMFA(): Promise<void> {
await withLoading(async () => {
try {
await store.dispatch(USER_ACTIONS.GENERATE_USER_MFA_SECRET);
await usersStore.generateUserMFASecret();
toggleEnableMFAModal();
} catch (error) {
await notify.error(error.message, AnalyticsErrorEventSource.ACCOUNT_SETTINGS_AREA);
@ -172,7 +173,7 @@ async function enableMFA(): Promise<void> {
async function generateNewMFARecoveryCodes(): Promise<void> {
await withLoading(async () => {
try {
await store.dispatch(USER_ACTIONS.GENERATE_USER_MFA_RECOVERY_CODES);
await usersStore.generateUserMFARecoveryCodes();
toggleMFACodesModal();
} catch (error) {
await notify.error(error.message, AnalyticsErrorEventSource.ACCOUNT_SETTINGS_AREA);
@ -184,7 +185,7 @@ async function generateNewMFARecoveryCodes(): Promise<void> {
* Lifecycle hook after initial render where user info is fetching.
*/
onMounted(() => {
store.dispatch(USER_ACTIONS.GET);
usersStore.getUser();
});
</script>

View File

@ -185,13 +185,13 @@ import {
Wallet,
NativePaymentHistoryItem,
} from '@/types/payments';
import { USER_ACTIONS } from '@/store/modules/users';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { RouteConfig } from '@/router';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsHttpApi } from '@/api/analytics';
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
import VLoader from '@/components/common/VLoader.vue';
@ -231,6 +231,7 @@ const {
GET_NATIVE_PAYMENTS_HISTORY,
} = PAYMENTS_ACTIONS;
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const nativeRouter = useRouter();
@ -275,7 +276,7 @@ const wallet = computed((): Wallet => {
* Indicates if user has own project.
*/
const userHasOwnProject = computed((): boolean => {
return store.getters.projectsCount > 0;
return store.getters.projectsCount(usersStore.state.user.id) > 0;
});
const creditCards = computed((): CreditCard[] => {
@ -362,7 +363,7 @@ async function addCard(token: string): Promise<void> {
await store.dispatch(ADD_CREDIT_CARD, token);
// We fetch User one more time to update their Paid Tier status.
await store.dispatch(USER_ACTIONS.GET);
await usersStore.getUser();
} catch (error) {
await notify.error(error.message, AnalyticsErrorEventSource.BILLING_PAYMENT_METHODS_TAB);

View File

@ -18,9 +18,9 @@
<script setup lang="ts">
import { computed } from 'vue';
import { useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
const store = useStore();
const usersStore = useUsersStore();
const props = withDefaults(defineProps<{
openGenerateModal: () => void,
@ -32,7 +32,7 @@ const props = withDefaults(defineProps<{
* Returns the quantity of MFA recovery codes.
*/
const numCodes = computed((): number => {
return store.getters.user.mfaRecoveryCodeCount;
return usersStore.state.user.mfaRecoveryCodeCount;
});
</script>

View File

@ -32,9 +32,11 @@ import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
import { useNotify, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import VLoader from '@/components/common/VLoader.vue';
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
@ -44,14 +46,14 @@ const isDataFetching = ref<boolean>(true);
* Returns user's projects count.
*/
const projectsCount = computed((): number => {
return store.getters.projectsCount;
return store.getters.projectsCount(usersStore.state.user.id);
});
/**
* Returns project limit from store.
*/
const projectLimit = computed((): number => {
const projectLimit: number = store.getters.user.projectLimit;
const projectLimit: number = usersStore.state.user.projectLimit;
if (projectLimit < projectsCount.value) return projectsCount.value;
return projectLimit;

View File

@ -134,7 +134,6 @@ import { useNotify, useRoute, useStore } from '@/utils/hooks';
import { RouteConfig } from '@/router';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { USER_ACTIONS } from '@/store/modules/users';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsHttpApi } from '@/api/analytics';
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
@ -142,6 +141,7 @@ import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { ProjectUsagePriceModel } from '@/types/payments';
import { decimalShift, formatPrice, CENTS_MB_TO_DOLLARS_TB_SHIFT } from '@/utils/strings';
import { useUsersStore } from '@/store/modules/usersStore';
import VModal from '@/components/common/VModal.vue';
import VLoader from '@/components/common/VLoader.vue';
@ -156,8 +156,9 @@ interface StripeForm {
onSubmit(): Promise<void>;
}
const notify = useNotify();
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const route = useRoute();
const analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
@ -212,7 +213,7 @@ async function addCardToDB(token: string): Promise<void> {
await notify.success('Card successfully added');
// We fetch User one more time to update their Paid Tier status.
await store.dispatch(USER_ACTIONS.GET);
await usersStore.getUser();
if (route.name === RouteConfig.ProjectDashboard.name) {
await store.dispatch(PROJECTS_ACTIONS.GET_LIMITS, store.getters.selectedProject.id);
@ -285,7 +286,7 @@ const bandwidthPrice = computed((): string => {
*/
onBeforeMount(() => {
try {
const partner = store.getters.user.partner;
const partner = usersStore.state.user.partner;
const config = require('@/components/account/billing/billingConfig.json');
if (partner !== '' && config[partner] && config[partner].extraBandwidthPriceInfo) {
extraBandwidthPriceInfo.value = config[partner].extraBandwidthPriceInfo;

View File

@ -91,6 +91,7 @@ import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/ana
import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
import VModal from '@/components/common/VModal.vue';
@ -100,6 +101,7 @@ import AddFieldIcon from '@/../static/images/team/addField.svg';
import AddMemberNotificationIcon from '@/../static/images/team/addMemberNotification.svg';
import DeleteFieldIcon from '@/../static/images/team/deleteField.svg';
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
@ -198,7 +200,7 @@ async function onAddUsersClick(): Promise<void> {
return;
}
if (emailArray.includes(store.state.usersModule.user.email)) {
if (emailArray.includes(usersStore.state.user.email)) {
await notify.error(`Error during adding project members. You can't add yourself to the project`, AnalyticsErrorEventSource.ADD_PROJECT_MEMBER_MODAL);
isLoading.value = false;

View File

@ -72,12 +72,14 @@ import { OBJECTS_MUTATIONS } from '@/store/modules/objects';
import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import VLoader from '@/components/common/VLoader.vue';
import VInput from '@/components/common/VInput.vue';
import VModal from '@/components/common/VModal.vue';
import VButton from '@/components/common/VButton.vue';
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const router = useRouter();
@ -117,7 +119,7 @@ async function onCreateProjectClick(): Promise<void> {
const project = new ProjectFields(
projectName.value,
description.value,
store.getters.user.id,
usersStore.state.user.id,
);
try {

View File

@ -43,12 +43,12 @@
<script setup lang="ts">
import { ref } from 'vue';
import { USER_ACTIONS } from '@/store/modules/users';
import { DisableMFARequest } from '@/types/users';
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import ConfirmMFAInput from '@/components/account/mfa/ConfirmMFAInput.vue';
import VButton from '@/components/common/VButton.vue';
@ -58,6 +58,7 @@ interface ClearInput {
clearInput(): void;
}
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
@ -101,8 +102,8 @@ async function disable(): Promise<void> {
isLoading.value = true;
try {
await store.dispatch(USER_ACTIONS.DISABLE_USER_MFA, request.value);
await store.dispatch(USER_ACTIONS.GET);
await usersStore.disableUserMFA(request.value);
await usersStore.getUser();
await notify.success('MFA was disabled successfully');

View File

@ -41,31 +41,32 @@
<script setup lang="ts">
import { computed, reactive, ref } from 'vue';
import { USER_ACTIONS } from '@/store/modules/users';
import { UpdatedUser } from '@/types/users';
import { AnalyticsHttpApi } from '@/api/analytics';
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import VModal from '@/components/common/VModal.vue';
import VButton from '@/components/common/VButton.vue';
import VInput from '@/components/common/VInput.vue';
const userStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
const userInfo = reactive<UpdatedUser>(new UpdatedUser(store.getters.user.fullName, store.getters.user.shortName));
const userInfo = reactive<UpdatedUser>(new UpdatedUser(userStore.state.user.fullName, userStore.state.user.shortName));
const fullNameError = ref<string>('');
/**
* Returns first letter of user name.
*/
const avatarLetter = computed((): string => {
return store.getters.userName.slice(0, 1).toUpperCase();
return userStore.userName.slice(0, 1).toUpperCase();
});
/**
@ -87,7 +88,7 @@ async function onUpdateClick(): Promise<void> {
}
try {
await store.dispatch(USER_ACTIONS.UPDATE, userInfo);
await userStore.updateUser(userInfo);
} catch (error) {
await notify.error(error.message, AnalyticsErrorEventSource.EDIT_PROFILE_MODAL);

View File

@ -57,9 +57,8 @@ import { computed, onMounted, ref } from 'vue';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useStore } from '@/utils/hooks';
import { Duration } from '@/utils/time';
import { USER_ACTIONS } from '@/store/modules/users';
import { SetUserSettingsData } from '@/types/users';
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
import VModal from '@/components/common/VModal.vue';
@ -67,6 +66,7 @@ import TimeoutSelector from '@/components/modals/editSessionTimeout/TimeoutSelec
import Icon from '@/../static/images/session/inactivityTimer.svg';
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
@ -85,7 +85,7 @@ onMounted(() => {
* Returns duration from store.
*/
const userDuration = computed((): Duration | null => {
return store.state.usersModule.settings.sessionDuration;
return usersStore.state.settings.sessionDuration;
});
/**
@ -112,9 +112,7 @@ function durationChange(duration: Duration) {
async function save() {
isLoading.value = true;
try {
await store.dispatch(USER_ACTIONS.UPDATE_SETTINGS, {
sessionDuration: sessionDuration.value?.nanoseconds ?? 0,
} as SetUserSettingsData);
await usersStore.updateSettings({ sessionDuration: sessionDuration.value?.nanoseconds ?? 0 });
notify.success(`Session timeout changed successfully. Your session timeout is ${sessionDuration.value?.shortString}.`);
onClose();
} catch (error) {

View File

@ -80,17 +80,18 @@
import QRCode from 'qrcode';
import { computed, onMounted, ref } from 'vue';
import { USER_ACTIONS } from '@/store/modules/users';
import { AnalyticsHttpApi } from '@/api/analytics';
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import ConfirmMFAInput from '@/components/account/mfa/ConfirmMFAInput.vue';
import VButton from '@/components/common/VButton.vue';
import VModal from '@/components/common/VModal.vue';
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
@ -115,17 +116,17 @@ const satellite = computed((): string => {
* Returns pre-generated MFA secret from store.
*/
const userMFASecret = computed((): string => {
return store.state.usersModule.userMFASecret;
return usersStore.state.userMFASecret;
});
/**
* Returns user MFA recovery codes from store.
*/
const userMFARecoveryCodes = computed((): string[] => {
return store.state.usersModule.userMFARecoveryCodes;
return usersStore.state.userMFARecoveryCodes;
});
const qrLink = `otpauth://totp/${encodeURIComponent(store.getters.user.email)}?secret=${userMFASecret.value}&issuer=${encodeURIComponent(`STORJ ${satellite.value}`)}&algorithm=SHA1&digits=6&period=30`;
const qrLink = `otpauth://totp/${encodeURIComponent(usersStore.state.user.email)}?secret=${userMFASecret.value}&issuer=${encodeURIComponent(`STORJ ${satellite.value}`)}&algorithm=SHA1&digits=6&period=30`;
/**
* Toggles view to Enable MFA state.
@ -147,7 +148,7 @@ function closeModal(): void {
*/
async function showCodes(): Promise<void> {
try {
await store.dispatch(USER_ACTIONS.GENERATE_USER_MFA_RECOVERY_CODES);
await usersStore.generateUserMFARecoveryCodes();
isEnable.value = false;
isCodes.value = true;
} catch (error) {
@ -172,8 +173,8 @@ async function enable(): Promise<void> {
isLoading.value = true;
try {
await store.dispatch(USER_ACTIONS.ENABLE_USER_MFA, confirmPasscode.value);
await store.dispatch(USER_ACTIONS.GET);
await usersStore.enableUserMFA(confirmPasscode.value);
await usersStore.getUser();
await showCodes();
analytics.eventTriggered(AnalyticsEvent.MFA_ENABLED);

View File

@ -35,17 +35,19 @@ import { computed } from 'vue';
import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
import VModal from '@/components/common/VModal.vue';
const usersStore = useUsersStore();
const store = useStore();
/**
* Returns MFA recovery codes from store.
*/
const userMFARecoveryCodes = computed((): string[] => {
return store.state.usersModule.userMFARecoveryCodes;
return usersStore.state.userMFARecoveryCodes;
});
/**

View File

@ -75,7 +75,6 @@
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { RouteConfig } from '@/router';
@ -89,6 +88,7 @@ import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { PM_ACTIONS } from '@/utils/constants/actionNames';
import { useUsersStore } from '@/store/modules/usersStore';
import VLoader from '@/components/common/VLoader.vue';
import VInput from '@/components/common/VInput.vue';
@ -97,14 +97,15 @@ import VButton from '@/components/common/VButton.vue';
import BlueBoxIcon from '@/../static/images/common/blueBox.svg';
const usersStore = useUsersStore();
const store = useStore();
const router = useRouter();
const notify = useNotify();
const store = useStore();
const description = ref('');
const createdProjectId = ref('');
const hasDescription = ref(false);
const isLoading = ref(false);
const projectName = ref('');
const nameError = ref('');
@ -139,7 +140,7 @@ async function onCreateProjectClick(): Promise<void> {
const project = new ProjectFields(
projectName.value,
description.value,
store.getters.user.id,
usersStore.state.user.id,
);
try {
@ -171,7 +172,7 @@ async function onCreateProjectClick(): Promise<void> {
store.commit(OBJECTS_MUTATIONS.CLEAR);
if (store.getters.shouldOnboard && store.state.appStateModule.isAllProjectsDashboard) {
if (usersStore.shouldOnboard && store.state.appStateModule.isAllProjectsDashboard) {
analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
await router.push(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
return;

View File

@ -38,7 +38,7 @@
width="100%"
font-size="13px"
icon="lock"
:is-green="plan.type == 'partner'"
:is-green="plan.type === 'partner'"
:is-disabled="isLoading"
:on-press="onActivateClick"
/>
@ -83,9 +83,9 @@ import { computed, ref, watch } from 'vue';
import { RouteConfig } from '@/router';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { USER_ACTIONS } from '@/store/modules/users';
import { PricingPlanInfo, PricingPlanType } from '@/types/common';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import StripeCardInput from '@/components/account/billing/paymentMethods/StripeCardInput.vue';
import VButton from '@/components/common/VButton.vue';
@ -99,6 +99,7 @@ interface StripeForm {
onSubmit(): Promise<void>;
}
const usersStore = useUsersStore();
const store = useStore();
const router = useRouter();
const notify = useNotify();
@ -174,7 +175,7 @@ async function onCardAdded(token: string): Promise<void> {
isSuccess.value = true;
// Fetch user to update paid tier status
await store.dispatch(USER_ACTIONS.GET);
await usersStore.getUser();
// Fetch cards to hide paid tier banner
await store.dispatch(PAYMENTS_ACTIONS.GET_CREDIT_CARDS);
} catch (error) {

View File

@ -55,7 +55,6 @@ import { RouteConfig } from '@/router';
import { AuthHttpApi } from '@/api/auth';
import { APP_STATE_ACTIONS, NOTIFICATION_ACTIONS, PM_ACTIONS } from '@/utils/constants/actionNames';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { USER_ACTIONS } from '@/store/modules/users';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { BUCKET_ACTIONS } from '@/store/modules/buckets';
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
@ -66,6 +65,7 @@ import { APP_STATE_DROPDOWNS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useABTestingStore } from '@/store/modules/abTestingStore';
import { useUsersStore } from '@/store/modules/usersStore';
import BillingIcon from '@/../static/images/navigation/billing.svg';
import InfoIcon from '@/../static/images/navigation/info.svg';
@ -80,7 +80,9 @@ import TierBadgePro from '@/../static/images/navigation/tierBadgePro.svg';
const store = useStore();
const router = useRouter();
const notify = useNotify();
const usersStore = useUsersStore();
const abTestingStore = useABTestingStore();
const auth: AuthHttpApi = new AuthHttpApi();
const analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
@ -113,7 +115,7 @@ const satellite = computed((): string => {
* Returns user entity from store.
*/
const user = computed((): User => {
return store.getters.user;
return usersStore.state.user;
});
/**
@ -147,7 +149,7 @@ async function onLogout(): Promise<void> {
await Promise.all([
store.dispatch(PM_ACTIONS.CLEAR),
store.dispatch(PROJECTS_ACTIONS.CLEAR),
store.dispatch(USER_ACTIONS.CLEAR),
usersStore.clear(),
store.dispatch(ACCESS_GRANTS_ACTIONS.STOP_ACCESS_GRANTS_WEB_WORKER),
store.dispatch(ACCESS_GRANTS_ACTIONS.CLEAR),
store.dispatch(NOTIFICATION_ACTIONS.CLEAR),

View File

@ -169,7 +169,6 @@ import { BUCKET_ACTIONS } from '@/store/modules/buckets';
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { USER_ACTIONS } from '@/store/modules/users';
import { NavigationLink } from '@/types/navigation';
import { Project } from '@/types/projects';
import { User } from '@/types/users';
@ -181,6 +180,7 @@ import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useABTestingStore } from '@/store/modules/abTestingStore';
import { useUsersStore } from '@/store/modules/usersStore';
import ResourcesLinks from '@/components/navigation/ResourcesLinks.vue';
import QuickStartLinks from '@/components/navigation/QuickStartLinks.vue';
@ -218,6 +218,7 @@ const navigation: NavigationLink[] = [
RouteConfig.Users.withIcon(UsersIcon),
];
const usersStore = useUsersStore();
const store = useStore();
const router = useRouter();
const notify = useNotify();
@ -263,7 +264,7 @@ const selectedProject = computed((): Project => {
/**
* Returns satellite name from store.
*/
const satellite = computed((): boolean => {
const satellite = computed((): string => {
return store.state.appStateModule.satelliteName;
});
@ -271,7 +272,7 @@ const satellite = computed((): boolean => {
* Returns user entity from store.
*/
const user = computed((): User => {
return store.getters.user;
return usersStore.state.user;
});
/**
@ -411,8 +412,8 @@ function onCreateLinkClick(): void {
if (router.currentRoute.name !== RouteConfig.CreateProject.name) {
analytics.eventTriggered(AnalyticsEvent.CREATE_NEW_CLICKED);
const user: User = store.getters.user;
const ownProjectsCount: number = store.getters.projectsCount;
const user: User = usersStore.state.user;
const ownProjectsCount: number = store.getters.projectsCount(user.id);
if (!user.paidTier && user.projectLimit === ownProjectsCount) {
store.commit(APP_STATE_MUTATIONS.UPDATE_ACTIVE_MODAL, MODALS.createProjectPrompt);
@ -459,7 +460,7 @@ async function onLogout(): Promise<void> {
await Promise.all([
store.dispatch(PM_ACTIONS.CLEAR),
store.dispatch(PROJECTS_ACTIONS.CLEAR),
store.dispatch(USER_ACTIONS.CLEAR),
usersStore.clear(),
store.dispatch(ACCESS_GRANTS_ACTIONS.STOP_ACCESS_GRANTS_WEB_WORKER),
store.dispatch(ACCESS_GRANTS_ACTIONS.CLEAR),
store.dispatch(NOTIFICATION_ACTIONS.CLEAR),

View File

@ -76,6 +76,7 @@ import { OBJECTS_MUTATIONS } from '@/store/modules/objects';
import { APP_STATE_DROPDOWNS, MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import VLoader from '@/components/common/VLoader.vue';
@ -86,10 +87,11 @@ import PassphraseIcon from '@/../static/images/navigation/passphrase.svg';
import ManageIcon from '@/../static/images/navigation/manage.svg';
import CreateProjectIcon from '@/../static/images/navigation/createProject.svg';
const nativeRouter = useRouter();
const router = reactive(nativeRouter);
const userStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const nativeRouter = useRouter();
const router = reactive(nativeRouter);
const FIRST_PAGE = 1;
const analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
@ -279,8 +281,8 @@ function onCreateLinkClick(): void {
if (router.currentRoute.name !== RouteConfig.CreateProject.name) {
analytics.eventTriggered(AnalyticsEvent.CREATE_NEW_CLICKED);
const user: User = store.getters.user;
const ownProjectsCount: number = store.getters.projectsCount;
const user: User = userStore.state.user;
const ownProjectsCount: number = store.getters.projectsCount(user.id);
if (!user.paidTier && user.projectLimit === ownProjectsCount) {
store.commit(APP_STATE_MUTATIONS.UPDATE_ACTIVE_MODAL, MODALS.createProjectPrompt);

View File

@ -52,6 +52,7 @@ import { AccessType } from '@/types/createAccessGrant';
import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useRouter, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import NewProjectIcon from '@/../static/images/navigation/newProject.svg';
import CreateAGIcon from '@/../static/images/navigation/createAccessGrant.svg';
@ -59,6 +60,7 @@ import S3Icon from '@/../static/images/navigation/s3.svg';
import UploadInCLIIcon from '@/../static/images/navigation/uploadInCLI.svg';
import UploadInWebIcon from '@/../static/images/navigation/uploadInWeb.svg';
const usersStore = useUsersStore();
const store = useStore();
const nativeRouter = useRouter();
const router = reactive(nativeRouter);
@ -127,8 +129,8 @@ function navigateToNewProject(): void {
if (router.currentRoute.name !== RouteConfig.CreateProject.name) {
analytics.eventTriggered(AnalyticsEvent.NEW_PROJECT_CLICKED);
const user: User = store.getters.user;
const ownProjectsCount: number = store.getters.projectsCount;
const user: User = usersStore.state.user;
const ownProjectsCount: number = store.getters.projectsCount(user.id);
if (!user.paidTier && user.projectLimit === ownProjectsCount) {
store.commit(APP_STATE_MUTATIONS.UPDATE_ACTIVE_MODAL, MODALS.createProjectPrompt);

View File

@ -27,9 +27,11 @@ import { computed } from 'vue';
import { MetaUtils } from '@/utils/meta';
import { useStore } from '@/utils/hooks';
import { LocalData } from '@/utils/localData';
import { useUsersStore } from '@/store/modules/usersStore';
import VBanner from '@/components/common/VBanner.vue';
const usersStore = useUsersStore();
const store = useStore();
const props = defineProps<{
@ -58,21 +60,21 @@ const projectLimitsIncreaseRequestURL = computed((): string => {
* Returns whether user is in paid tier.
*/
const isPaidTier = computed((): boolean => {
return store.state.usersModule.user.paidTier;
return usersStore.state.user.paidTier;
});
/**
* Returns user's projects count.
*/
const projectsCount = computed((): number => {
return store.getters.projectsCount;
return store.getters.projectsCount(usersStore.state.user.id);
});
/**
* Returns project limit from store.
*/
const projectLimit = computed((): number => {
const projectLimit: number = store.getters.user.projectLimit;
const projectLimit: number = usersStore.state.user.projectLimit;
if (projectLimit < projectsCount.value) return projectsCount.value;
return projectLimit;

View File

@ -36,12 +36,12 @@ import { MetaUtils } from '@/utils/meta';
import { PartneredSatellite } from '@/types/common';
import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { USER_ACTIONS } from '@/store/modules/users';
import { UserSettings } from '@/types/users';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import OverviewContainer from '@/components/onboardingTour/steps/common/OverviewContainer.vue';
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const router = useRouter();
@ -87,9 +87,7 @@ async function onUploadInBrowserClick(): Promise<void> {
async function endOnboarding(): Promise<void> {
try {
await store.dispatch(USER_ACTIONS.UPDATE_SETTINGS, {
onboardingEnd: true,
} as Partial<UserSettings>);
await usersStore.updateSettings({ onboardingEnd: true });
} catch (error) {
notify.error(error.message, AnalyticsErrorEventSource.ONBOARDING_OVERVIEW_STEP);
}
@ -101,10 +99,8 @@ async function endOnboarding(): Promise<void> {
*/
onMounted(async (): Promise<void> => {
try {
if (!store.state.usersModule.settings.onboardingStart) {
await store.dispatch(USER_ACTIONS.UPDATE_SETTINGS, {
onboardingStart: true,
} as Partial<UserSettings>);
if (!usersStore.state.settings.onboardingStart) {
await usersStore.updateSettings({ onboardingStart: true });
}
} catch (error) {
notify.error(error.message, AnalyticsErrorEventSource.ONBOARDING_OVERVIEW_STEP);

View File

@ -23,16 +23,17 @@ import { onBeforeMount, ref } from 'vue';
import { RouteConfig } from '@/router';
import { PricingPlanInfo, PricingPlanType } from '@/types/common';
import { User, UserSettings } from '@/types/users';
import { User } from '@/types/users';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { MetaUtils } from '@/utils/meta';
import { PaymentsHttpApi } from '@/api/payments';
import { USER_ACTIONS } from '@/store/modules/users';
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
import { useUsersStore } from '@/store/modules/usersStore';
import PricingPlanContainer from '@/components/onboardingTour/steps/pricingPlanFlow/PricingPlanContainer.vue';
import VLoader from '@/components/common/VLoader.vue';
const usersStore = useUsersStore();
const store = useStore();
const router = useRouter();
const notify = useNotify();
@ -71,7 +72,7 @@ const plans = ref<PricingPlanInfo[]>([
* Loads pricing plan config.
*/
onBeforeMount(async () => {
const user: User = store.getters.user;
const user: User = usersStore.state.user;
let nextPath = RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path;
if (store.state.appStateModule.isAllProjectsDashboard) {
nextPath = RouteConfig.AllProjectsDashboard.path;
@ -112,11 +113,9 @@ onBeforeMount(async () => {
plan.type = PricingPlanType.PARTNER;
plans.value.unshift(plan);
if (!store.state.usersModule.settings.onboardingStart) {
if (!usersStore.state.settings.onboardingStart) {
try {
await store.dispatch(USER_ACTIONS.UPDATE_SETTINGS, {
onboardingStart: true,
} as Partial<UserSettings>);
await usersStore.updateSettings({ onboardingStart: true });
} catch (error) {
notify.error(error.message, AnalyticsErrorEventSource.PRICING_PLAN_STEP);
}

View File

@ -32,15 +32,15 @@
<script setup lang="ts">
import { RouteConfig } from '@/router';
import { AnalyticsHttpApi } from '@/api/analytics';
import { USER_ACTIONS } from '@/store/modules/users';
import { UserSettings } from '@/types/users';
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
import Icon from '@/../static/images/onboardingTour/successStep.svg';
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const router = useRouter();
@ -66,9 +66,7 @@ async function onFinishClick(): Promise<void> {
async function endOnboarding(): Promise<void> {
try {
await store.dispatch(USER_ACTIONS.UPDATE_SETTINGS, {
onboardingEnd: true,
} as Partial<UserSettings>);
await usersStore.updateSettings({ onboardingEnd: true });
} catch (error) {
notify.error(error.message, AnalyticsErrorEventSource.ONBOARDING_OVERVIEW_STEP);
}

View File

@ -69,11 +69,13 @@ import { LocalData } from '@/utils/localData';
import { AnalyticsHttpApi } from '@/api/analytics';
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import VLoader from '@/components/common/VLoader.vue';
import VButton from '@/components/common/VButton.vue';
import VInput from '@/components/common/VInput.vue';
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const router = useRouter();
@ -121,7 +123,7 @@ async function onCreateProjectClick(): Promise<void> {
const project = new ProjectFields(
projectName.value,
description.value,
store.getters.user.id,
usersStore.state.user.id,
);
try {

View File

@ -213,9 +213,11 @@ import { MetaUtils } from '@/utils/meta';
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { AnalyticsHttpApi } from '@/api/analytics';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const router = useRouter();
@ -584,7 +586,7 @@ function toggleDescriptionEditing(): void {
function toggleStorageLimitEditing(): void {
const storageLimitUnit = new Size(currentLimits.value.storageLimit, 2).label;
if (store.state.usersModule.user.paidTier) {
if (usersStore.state.user.paidTier) {
isStorageLimitEditing.value = !isStorageLimitEditing.value;
if (activeStorageMeasurement.value === Dimensions.TB && storageLimitUnit !== Dimensions.TB) {
@ -604,7 +606,7 @@ function toggleStorageLimitEditing(): void {
function toggleBandwidthLimitEditing(): void {
const bandwidthLimitUnit = new Size(currentLimits.value.bandwidthLimit, 2).label;
if (store.state.usersModule.user.paidTier) {
if (usersStore.state.user.paidTier) {
isBandwidthLimitEditing.value = !isBandwidthLimitEditing.value;
if (activeBandwidthMeasurement.value === Dimensions.TB && bandwidthLimitUnit !== Dimensions.TB) {
@ -633,7 +635,7 @@ function onBackClick(): void {
onMounted(async (): Promise<void> => {
if (!store.getters.selectedProject.id) return;
if (store.state.usersModule.user.paidTier) {
if (usersStore.state.user.paidTier) {
isPaidTier.value = true;
}

View File

@ -13,12 +13,12 @@
import { computed } from 'vue';
import { Project } from '@/types/projects';
import { useStore } from '@/utils/hooks';
import { User } from '@/types/users';
import { useUsersStore } from '@/store/modules/usersStore';
import BoxIcon from '@/../static/images/allDashboard/box.svg';
const store = useStore();
const usersStore = useUsersStore();
const props = defineProps<{
project?: Project,
@ -28,7 +28,7 @@ const props = defineProps<{
* Returns user entity from store.
*/
const user = computed((): User => {
return store.getters.user;
return usersStore.state.user;
});
/**

View File

@ -170,6 +170,7 @@ import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames
import { APP_STATE_DROPDOWNS, MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import VLoader from '@/components/common/VLoader.vue';
import InfoContainer from '@/components/project/dashboard/InfoContainer.vue';
@ -186,6 +187,7 @@ import ProjectOwnershipTag from '@/components/project/ProjectOwnershipTag.vue';
import NewProjectIcon from '@/../static/images/project/newProject.svg';
import InfoIcon from '@/../static/images/project/infoIcon.svg';
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const router = useRouter();
@ -224,7 +226,7 @@ const status = computed((): string => {
* Returns pro account status from store.
*/
const isProAccount = computed((): boolean => {
return store.getters.user.paidTier;
return usersStore.state.user.paidTier;
});
/**

View File

@ -62,6 +62,7 @@ import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/ana
import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import ProjectsListItem from '@/components/projectsList/ProjectsListItem.vue';
import VTable from '@/components/common/VTable.vue';
@ -75,6 +76,7 @@ const {
const FIRST_PAGE = 1;
const analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const router = useRouter();
@ -109,8 +111,8 @@ async function onPageClick(page: number): Promise<void> {
function onCreateClick(): void {
analytics.eventTriggered(AnalyticsEvent.NEW_PROJECT_CLICKED);
const user: User = store.getters.user;
const ownProjectsCount: number = store.getters.projectsCount;
const user: User = usersStore.state.user;
const ownProjectsCount: number = store.getters.projectsCount(user.id);
if (!user.paidTier && user.projectLimit === ownProjectsCount) {
store.commit(APP_STATE_MUTATIONS.UPDATE_ACTIVE_MODAL, MODALS.createProjectPrompt);

View File

@ -20,7 +20,6 @@ import { makeObjectsModule, OBJECTS_ACTIONS, ObjectsState } from '@/store/module
import { makePaymentsModule, PaymentsState } from '@/store/modules/payments';
import { makeProjectMembersModule, ProjectMembersState } from '@/store/modules/projectMembers';
import { makeProjectsModule, PROJECTS_MUTATIONS, ProjectsState } from '@/store/modules/projects';
import { makeUsersModule, UsersState } from '@/store/modules/users';
import { FilesState, makeFilesModule } from '@/store/modules/files';
import { NavigationLink } from '@/types/navigation';
import { MODALS } from '@/utils/constants/appStatePopUps';
@ -52,7 +51,6 @@ export interface ModulesState {
appStateModule: AppState;
projectMembersModule: ProjectMembersState;
paymentsModule: PaymentsState;
usersModule: UsersState;
projectsModule: ProjectsState;
objectsModule: ObjectsState;
bucketUsageModule: BucketsState;
@ -67,7 +65,6 @@ export const store = new Vuex.Store<ModulesState>({
appStateModule: makeAppStateModule(configApi),
projectMembersModule: makeProjectMembersModule(projectMembersApi),
paymentsModule: makePaymentsModule(paymentsApi),
usersModule: makeUsersModule(authApi),
projectsModule: makeProjectsModule(projectsApi),
bucketUsageModule: makeBucketsModule(bucketsApi),
objectsModule: makeObjectsModule(),

View File

@ -237,13 +237,13 @@ export function makeProjectsModule(api: ProjectsApi): StoreModule<ProjectsState,
return project;
},
[CREATE_DEFAULT_PROJECT]: async function ({ rootGetters, dispatch }: ProjectsContext): Promise<void> {
[CREATE_DEFAULT_PROJECT]: async function ({ rootGetters, dispatch }: ProjectsContext, userID: string): Promise<void> {
const UNTITLED_PROJECT_NAME = 'My First Project';
const UNTITLED_PROJECT_DESCRIPTION = '___';
const project = new ProjectFields(
UNTITLED_PROJECT_NAME,
UNTITLED_PROJECT_DESCRIPTION,
rootGetters.user.id,
userID,
);
const createdProject = await dispatch(CREATE, project);
@ -358,11 +358,11 @@ export function makeProjectsModule(api: ProjectsApi): StoreModule<ProjectsState,
});
},
selectedProject: (state: ProjectsState): Project => state.selectedProject,
projectsCount: (state: ProjectsState, rootGetters: ProjectsContext['rootGetters']): number => {
projectsCount: (state: ProjectsState) => (userID: string): number => {
let projectsCount = 0;
state.projects.forEach((project: Project) => {
if (project.ownerId === rootGetters.user.id) {
if (project.ownerId === userID) {
projectsCount++;
}
});

View File

@ -1,179 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
import {
DisableMFARequest,
SetUserSettingsData,
UpdatedUser,
User,
UsersApi,
UserSettings,
} from '@/types/users';
import { MetaUtils } from '@/utils/meta';
import { StoreModule } from '@/types/store';
import { Duration } from '@/utils/time';
export const USER_ACTIONS = {
LOGIN: 'loginUser',
UPDATE: 'updateUser',
GET: 'getUser',
ENABLE_USER_MFA: 'enableUserMFA',
DISABLE_USER_MFA: 'disableUserMFA',
GENERATE_USER_MFA_SECRET: 'generateUserMFASecret',
GENERATE_USER_MFA_RECOVERY_CODES: 'generateUserMFARecoveryCodes',
CLEAR: 'clearUser',
GET_FROZEN_STATUS: 'getFrozenStatus',
GET_SETTINGS: 'getSettings',
UPDATE_SETTINGS: 'updateSettings',
};
export const USER_MUTATIONS = {
SET_USER: 'setUser',
SET_USER_MFA_SECRET: 'setUserMFASecret',
SET_USER_MFA_RECOVERY_CODES: 'setUserMFARecoveryCodes',
UPDATE_USER: 'updateUser',
CLEAR: 'clearUser',
SET_FROZEN_STATUS: 'setFrozenStatus',
SET_SETTINGS: 'setSettings',
};
export class UsersState {
public user: User = new User();
public settings: UserSettings = new UserSettings();
public userMFASecret = '';
public userMFARecoveryCodes: string[] = [];
}
const {
GET,
UPDATE,
ENABLE_USER_MFA,
DISABLE_USER_MFA,
GENERATE_USER_MFA_SECRET,
GENERATE_USER_MFA_RECOVERY_CODES,
GET_FROZEN_STATUS,
GET_SETTINGS,
UPDATE_SETTINGS,
} = USER_ACTIONS;
const {
SET_USER,
UPDATE_USER,
SET_USER_MFA_SECRET,
SET_USER_MFA_RECOVERY_CODES,
CLEAR,
SET_FROZEN_STATUS,
SET_SETTINGS,
} = USER_MUTATIONS;
interface UsersContext {
state: UsersState
commit: (string, ...unknown) => void
}
/**
* creates users module with all dependencies
*
* @param api - users api
*/
export function makeUsersModule(api: UsersApi): StoreModule<UsersState, UsersContext> {
return {
state: new UsersState(),
mutations: {
[SET_USER](state: UsersState, user: User): void {
state.user = user;
if (user.projectLimit === 0) {
const limitFromConfig = MetaUtils.getMetaContent('default-project-limit');
state.user.projectLimit = parseInt(limitFromConfig);
return;
}
state.user.projectLimit = user.projectLimit;
},
[SET_FROZEN_STATUS](state: UsersState, status: boolean): void {
state.user.isFrozen = status;
},
[SET_SETTINGS](state: UsersState, settings: UserSettings): void {
state.settings = settings;
},
[CLEAR](state: UsersState): void {
state.user = new User();
state.user.projectLimit = 1;
state.userMFASecret = '';
state.userMFARecoveryCodes = [];
},
[UPDATE_USER](state: UsersState, user: UpdatedUser): void {
state.user.fullName = user.fullName;
state.user.shortName = user.shortName;
},
[SET_USER_MFA_SECRET](state: UsersState, secret: string): void {
state.userMFASecret = secret;
},
[SET_USER_MFA_RECOVERY_CODES](state: UsersState, codes: string[]): void {
state.userMFARecoveryCodes = codes;
state.user.mfaRecoveryCodeCount = codes.length;
},
},
actions: {
[UPDATE]: async function ({ commit }: UsersContext, userInfo: UpdatedUser): Promise<void> {
await api.update(userInfo);
commit(UPDATE_USER, userInfo);
},
[GET]: async function ({ commit }: UsersContext): Promise<User> {
const user = await api.get();
commit(SET_USER, user);
return user;
},
[GET_FROZEN_STATUS]: async function ({ commit }: UsersContext): Promise<void> {
const frozenStatus = await api.getFrozenStatus();
commit(SET_FROZEN_STATUS, frozenStatus);
},
[GET_SETTINGS]: async function ({ commit }: UsersContext): Promise<UserSettings> {
const settings = await api.getUserSettings();
commit(SET_SETTINGS, settings);
return settings;
},
[UPDATE_SETTINGS]: async function ({ commit, state }: UsersContext, update: SetUserSettingsData): Promise<void> {
const settings = await api.updateSettings(update);
commit(SET_SETTINGS, settings);
},
[DISABLE_USER_MFA]: async function (_, request: DisableMFARequest): Promise<void> {
await api.disableUserMFA(request.passcode, request.recoveryCode);
},
[ENABLE_USER_MFA]: async function (_, passcode: string): Promise<void> {
await api.enableUserMFA(passcode);
},
[GENERATE_USER_MFA_SECRET]: async function ({ commit }: UsersContext): Promise<void> {
const secret = await api.generateUserMFASecret();
commit(SET_USER_MFA_SECRET, secret);
},
[GENERATE_USER_MFA_RECOVERY_CODES]: async function ({ commit }: UsersContext): Promise<void> {
const codes = await api.generateUserMFARecoveryCodes();
commit(SET_USER_MFA_RECOVERY_CODES, codes);
},
[CLEAR]: function({ commit }: UsersContext) {
commit(CLEAR);
},
},
getters: {
user: (state: UsersState): User => state.user,
userName: (state: UsersState): string => state.user.getFullName(),
shouldOnboard: (state: UsersState): boolean => !state.settings.onboardingStart || (state.settings.onboardingStart && !state.settings.onboardingEnd),
},
};
}

View File

@ -4,12 +4,20 @@
import { defineStore } from 'pinia';
import { computed, reactive } from 'vue';
import { DisableMFARequest, UpdatedUser, User, UsersApi } from '@/types/users';
import {
DisableMFARequest,
SetUserSettingsData,
UpdatedUser,
User,
UsersApi,
UserSettings,
} from '@/types/users';
import { MetaUtils } from '@/utils/meta';
import { AuthHttpApi } from '@/api/auth';
export class UsersState {
public user: User = new User();
public settings: UserSettings = new UserSettings();
public userMFASecret = '';
public userMFARecoveryCodes: string[] = [];
}
@ -21,32 +29,31 @@ export const useUsersStore = defineStore('users', () => {
return state.user.getFullName();
});
const shouldOnboard = computed(() => {
return !state.settings.onboardingStart || (state.settings.onboardingStart && !state.settings.onboardingEnd);
});
const api: UsersApi = new AuthHttpApi();
async function updateUserInfo(userInfo: UpdatedUser): Promise<void> {
async function updateUser(userInfo: UpdatedUser): Promise<void> {
await api.update(userInfo);
state.user.fullName = userInfo.fullName;
state.user.shortName = userInfo.shortName;
}
async function fetchUserInfo(): Promise<void> {
async function getUser(): Promise<void> {
const user = await api.get();
state.user = user;
if (user.projectLimit === 0) {
const limitFromConfig = MetaUtils.getMetaContent('default-project-limit');
state.user.projectLimit = parseInt(limitFromConfig);
return;
user.projectLimit = parseInt(limitFromConfig);
}
state.user.projectLimit = user.projectLimit;
setUser(user);
}
async function fetchFrozenStatus(): Promise<void> {
async function getFrozenStatus(): Promise<void> {
state.user.isFrozen = await api.getFrozenStatus();
}
@ -69,21 +76,47 @@ export const useUsersStore = defineStore('users', () => {
state.user.mfaRecoveryCodeCount = codes.length;
}
function clearUserInfo() {
async function getSettings(): Promise<UserSettings> {
const settings = await api.getUserSettings();
state.settings = settings;
return settings;
}
async function updateSettings(update: SetUserSettingsData): Promise<void> {
state.settings = await api.updateSettings(update);
}
function setUser(user: User): void {
state.user = user;
}
// Does nothing. It is called on login screen, and we just subscribe to this action in dashboard wrappers.
function login(): void {}
function clear() {
state.user = new User();
state.user.projectLimit = 1;
state.settings = new UserSettings();
state.userMFASecret = '';
state.userMFARecoveryCodes = [];
}
return {
usersState: state,
state,
userName,
updateUserInfo,
fetchUserInfo,
shouldOnboard,
updateUser,
getUser,
disableUserMFA,
enableUserMFA,
generateUserMFASecret,
generateUserMFARecoveryCodes,
clearUserInfo,
fetchFrozenStatus,
clear,
login,
getFrozenStatus,
setUser,
updateSettings,
getSettings,
};
});

View File

@ -94,7 +94,6 @@ import { Validator } from '@/utils/validation';
import { RouteConfig } from '@/router';
import { BUCKET_ACTIONS } from '@/store/modules/buckets';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { USER_ACTIONS } from '@/store/modules/users';
import { Project } from '@/types/projects';
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
@ -103,6 +102,7 @@ import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { OAuthClient, OAuthClientsAPI } from '@/api/oauthClients';
import { AnalyticsHttpApi } from '@/api/analytics';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { useUsersStore } from '@/store/modules/usersStore';
import VInput from '@/components/common/VInput.vue';
@ -117,6 +117,7 @@ const validPerms = {
'delete': true,
};
const usersStore = useUsersStore();
const store = useStore();
const notify = useNotify();
const router = useRouter();
@ -184,10 +185,10 @@ function formatObjectPermissions(scope: string): string {
async function ensureLogin(): Promise<void> {
try {
await store.dispatch(USER_ACTIONS.GET);
await usersStore.getUser();
} catch (error) {
if (!(error instanceof ErrorUnauthorized)) {
await store.dispatch(APP_STATE_ACTIONS.CHANGE_STATE, FetchState.ERROR);
await store.dispatch(APP_STATE_ACTIONS.CHANGE_FETCH_STATE, FetchState.ERROR);
await notify.error(error.message, null);
}

View File

@ -106,7 +106,6 @@ import { RouteConfig } from '@/router';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { USER_ACTIONS } from '@/store/modules/users';
import { CouponType } from '@/types/coupons';
import { Project } from '@/types/projects';
import { APP_STATE_ACTIONS, NOTIFICATION_ACTIONS, PM_ACTIONS } from '@/utils/constants/actionNames';
@ -123,6 +122,7 @@ import { useNotify, useRouter, useStore } from '@/utils/hooks';
import { MODALS } from '@/utils/constants/appStatePopUps';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useABTestingStore } from '@/store/modules/abTestingStore';
import { useUsersStore } from '@/store/modules/usersStore';
import NavigationArea from '@/components/navigation/NavigationArea.vue';
import InactivityModal from '@/components/modals/InactivityModal.vue';
@ -141,6 +141,7 @@ const {
GET_CREDIT_CARDS,
} = PAYMENTS_ACTIONS;
const usersStore = useUsersStore();
const store = useStore();
// TODO: will be swapped with useRouter from new version of router. remove after vue-router version upgrade.
const nativeRouter = useRouter();
@ -175,7 +176,7 @@ const dashboardContent = ref<HTMLElement | null>(null);
* Indicates if account was frozen due to billing issues.
*/
const isAccountFrozen = computed((): boolean => {
return store.state.usersModule.user.isFrozen;
return usersStore.state.user.isFrozen;
});
/**
@ -204,7 +205,7 @@ const limitState = computed((): LimitedState => {
hundredModalLimitType: '',
};
if (store.state.usersModule.user.paidTier || isAccountFrozen.value) {
if (usersStore.state.user.paidTier || isAccountFrozen.value) {
return result;
}
@ -269,22 +270,22 @@ const isAllProjectsDashboard = computed((): boolean => {
const isProjectLimitBannerShown = computed((): boolean => {
return !LocalData.getProjectLimitBannerHidden()
&& isProjectListPage.value
&& (hasReachedProjectLimit.value || !store.state.usersModule.user.paidTier);
&& (hasReachedProjectLimit.value || !usersStore.state.user.paidTier);
});
/**
* Returns whether the user has reached project limits.
*/
const hasReachedProjectLimit = computed((): boolean => {
const projectLimit: number = store.getters.user.projectLimit;
const projectsCount: number = store.getters.projectsCount;
const projectLimit: number = usersStore.state.user.projectLimit;
const projectsCount: number = store.getters.projectsCount(usersStore.state.user.id);
return projectsCount === projectLimit;
});
/* whether the paid tier banner should be shown */
const isPaidTierBannerShown = computed((): boolean => {
return !store.state.usersModule.user.paidTier
return !usersStore.state.user.paidTier
&& !isOnboardingTour.value
&& joinedWhileAgo.value
&& isDashboardPage.value;
@ -292,7 +293,7 @@ const isPaidTierBannerShown = computed((): boolean => {
/* whether the user joined more than 7 days ago */
const joinedWhileAgo = computed((): boolean => {
const createdAt = store.state.usersModule.user.createdAt as Date | null;
const createdAt = usersStore.state.user.createdAt as Date | null;
if (!createdAt) return true; // true so we can show the banner regardless
const millisPerDay = 24 * 60 * 60 * 1000;
return ((Date.now() - createdAt.getTime()) / millisPerDay) > 7;
@ -330,7 +331,7 @@ const isLoading = computed((): boolean => {
* Indicates whether the MFA recovery code warning bar should be shown.
*/
const showMFARecoveryCodeBar = computed((): boolean => {
const user: User = store.getters.user;
const user: User = usersStore.state.user;
return user.isMFAEnabled && user.mfaRecoveryCodeCount < recoveryCodeWarningThreshold;
});
@ -484,7 +485,7 @@ async function handleInactive(): Promise<void> {
await Promise.all([
store.dispatch(PM_ACTIONS.CLEAR),
store.dispatch(PROJECTS_ACTIONS.CLEAR),
store.dispatch(USER_ACTIONS.CLEAR),
usersStore.clear(),
store.dispatch(ACCESS_GRANTS_ACTIONS.STOP_ACCESS_GRANTS_WEB_WORKER),
store.dispatch(ACCESS_GRANTS_ACTIONS.CLEAR),
store.dispatch(NOTIFICATION_ACTIONS.CLEAR),
@ -531,7 +532,7 @@ function toggleMFARecoveryModal(): void {
*/
async function generateNewMFARecoveryCodes(): Promise<void> {
try {
await store.dispatch(USER_ACTIONS.GENERATE_USER_MFA_RECOVERY_CODES);
await usersStore.generateUserMFARecoveryCodes();
toggleMFARecoveryModal();
} catch (error) {
await notify.error(error.message, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
@ -579,19 +580,19 @@ async function onSessionActivity(): Promise<void> {
* Pre fetches user`s and project information.
*/
onMounted(async () => {
store.subscribeAction((action) => {
if (action.type === USER_ACTIONS.CLEAR) clearSessionTimers();
usersStore.$onAction((action) => {
if (action.name === 'clear') clearSessionTimers();
});
try {
await store.dispatch(USER_ACTIONS.GET);
await store.dispatch(USER_ACTIONS.GET_FROZEN_STATUS);
await usersStore.getUser();
await usersStore.getFrozenStatus();
await abTestingStore.fetchValues();
await store.dispatch(USER_ACTIONS.GET_SETTINGS);
await usersStore.getSettings();
setupSessionTimers();
} catch (error) {
store.subscribeAction((action) => {
if (action.type === USER_ACTIONS.LOGIN) setupSessionTimers();
usersStore.$onAction((action) => {
if (action.name === 'login') setupSessionTimers();
});
if (!(error instanceof ErrorUnauthorized)) {
@ -641,11 +642,11 @@ onMounted(async () => {
if (!store.state.appStateModule.isAllProjectsDashboard) {
try {
if (!projects.length) {
await store.dispatch(PROJECTS_ACTIONS.CREATE_DEFAULT_PROJECT);
await store.dispatch(PROJECTS_ACTIONS.CREATE_DEFAULT_PROJECT, usersStore.state.user.id);
} else {
selectProject(projects);
}
if (store.getters.shouldOnboard) {
if (usersStore.shouldOnboard) {
const onboardingPath = RouteConfig.OnboardingTour.with(RouteConfig.FirstOnboardingStep).path;
await analytics.pageVisit(onboardingPath);

View File

@ -173,11 +173,11 @@ import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
import { ErrorBadRequest } from '@/api/errors/ErrorBadRequest';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsHttpApi } from '@/api/analytics';
import { USER_ACTIONS } from '@/store/modules/users';
import { TokenInfo } from '@/types/users';
import { LocalData } from '@/utils/localData';
import { useNotify, useRoute, useRouter, useStore } from '@/utils/hooks';
import { PartneredSatellite } from '@/types/common';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
import VInput from '@/components/common/VInput.vue';
@ -225,9 +225,11 @@ const forgotPasswordPath: string = RouteConfig.ForgotPassword.path;
const registerPath: string = RouteConfig.Register.path;
const auth = new AuthHttpApi();
const notify = useNotify();
const analytics = new AnalyticsHttpApi();
const usersStore = useUsersStore();
const router = useRouter();
const notify = useNotify();
const store = useStore();
const route = useRoute();
@ -442,7 +444,7 @@ async function login(): Promise<void> {
return;
}
await store.dispatch(USER_ACTIONS.LOGIN);
usersStore.login();
await store.dispatch(APP_STATE_ACTIONS.CHANGE_FETCH_STATE, FetchState.LOADING);
isLoading.value = false;

View File

@ -112,7 +112,6 @@ import {
import { AnalyticsHttpApi } from '@/api/analytics';
import { useNotify, useRoute, useRouter, useStore } from '@/utils/hooks';
import { RouteConfig } from '@/router';
import { USER_ACTIONS } from '@/store/modules/users';
import {
APP_STATE_ACTIONS,
NOTIFICATION_ACTIONS,
@ -131,6 +130,7 @@ import { CouponType } from '@/types/coupons';
import { AuthHttpApi } from '@/api/auth';
import Heading from '@/views/all-dashboard/components/Heading.vue';
import { useABTestingStore } from '@/store/modules/abTestingStore';
import { useUsersStore } from '@/store/modules/usersStore';
import InactivityModal from '@/components/modals/InactivityModal.vue';
import BetaSatBar from '@/components/infoBars/BetaSatBar.vue';
@ -151,6 +151,7 @@ const {
const router = useRouter();
const store = useStore();
const notify = useNotify();
const usersStore = useUsersStore();
const abTestingStore = useABTestingStore();
const analytics = new AnalyticsHttpApi();
const auth: AuthHttpApi = new AuthHttpApi();
@ -178,7 +179,7 @@ const dashboardContent = ref<HTMLElement | null>(null);
* Indicates if account was frozen due to billing issues.
*/
const isAccountFrozen = computed((): boolean => {
return store.state.usersModule.user.isFrozen;
return usersStore.state.user.isFrozen;
});
/**
@ -207,7 +208,7 @@ const limitState = computed((): LimitedState => {
hundredModalLimitType: '',
};
if (store.state.usersModule.user.paidTier || isAccountFrozen.value) {
if (usersStore.state.user.paidTier || isAccountFrozen.value) {
return result;
}
@ -280,7 +281,7 @@ const isLoading = computed((): boolean => {
* Indicates whether the MFA recovery code warning bar should be shown.
*/
const showMFARecoveryCodeBar = computed((): boolean => {
const user: User = store.getters.user;
const user: User = usersStore.state.user;
return user.isMFAEnabled && user.mfaRecoveryCodeCount < recoveryCodeWarningThreshold;
});
@ -288,29 +289,29 @@ const showMFARecoveryCodeBar = computed((): boolean => {
const isProjectLimitBannerShown = computed((): boolean => {
return !LocalData.getProjectLimitBannerHidden()
&& !isBillingPage.value
&& (hasReachedProjectLimit.value || !store.state.usersModule.user.paidTier);
&& (hasReachedProjectLimit.value || !usersStore.state.user.paidTier);
});
/**
* Returns whether the user has reached project limits.
*/
const hasReachedProjectLimit = computed((): boolean => {
const projectLimit: number = store.getters.user.projectLimit;
const projectsCount: number = store.getters.projectsCount;
const projectLimit: number = usersStore.state.user.projectLimit;
const projectsCount: number = store.getters.projectsCount(usersStore.state.user.id);
return projectsCount === projectLimit;
});
/* whether the paid tier banner should be shown */
const isPaidTierBannerShown = computed((): boolean => {
return !store.state.usersModule.user.paidTier
return !usersStore.state.user.paidTier
&& !isBillingPage.value
&& joinedWhileAgo.value;
});
/* whether the user joined more than 7 days ago */
const joinedWhileAgo = computed((): boolean => {
const createdAt = store.state.usersModule.user.createdAt as Date | null;
const createdAt = usersStore.state.user.createdAt as Date | null;
if (!createdAt) return true; // true so we can show the banner regardless
const millisPerDay = 24 * 60 * 60 * 1000;
return ((Date.now() - createdAt.getTime()) / millisPerDay) > 7;
@ -341,7 +342,7 @@ async function handleInactive(): Promise<void> {
await Promise.all([
store.dispatch(PM_ACTIONS.CLEAR),
store.dispatch(PROJECTS_ACTIONS.CLEAR),
store.dispatch(USER_ACTIONS.CLEAR),
usersStore.clear(),
store.dispatch(ACCESS_GRANTS_ACTIONS.STOP_ACCESS_GRANTS_WEB_WORKER),
store.dispatch(ACCESS_GRANTS_ACTIONS.CLEAR),
store.dispatch(NOTIFICATION_ACTIONS.CLEAR),
@ -373,7 +374,7 @@ async function handleInactive(): Promise<void> {
*/
async function generateNewMFARecoveryCodes(): Promise<void> {
try {
await store.dispatch(USER_ACTIONS.GENERATE_USER_MFA_RECOVERY_CODES);
await usersStore.generateUserMFARecoveryCodes();
toggleMFARecoveryModal();
} catch (error) {
await notify.error(error.message, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
@ -520,19 +521,19 @@ async function onSessionActivity(): Promise<void> {
* Pre fetches user`s and project information.
*/
onMounted(async () => {
store.subscribeAction((action) => {
if (action.type === USER_ACTIONS.CLEAR) clearSessionTimers();
usersStore.$onAction((action) => {
if (action.name === 'clear') clearSessionTimers();
});
try {
await store.dispatch(USER_ACTIONS.GET);
await store.dispatch(USER_ACTIONS.GET_FROZEN_STATUS);
await usersStore.getUser();
await usersStore.getFrozenStatus();
await abTestingStore.fetchValues();
await store.dispatch(USER_ACTIONS.GET_SETTINGS);
await usersStore.getSettings();
setupSessionTimers();
} catch (error) {
store.subscribeAction((action) => {
if (action.type === USER_ACTIONS.LOGIN) setupSessionTimers();
usersStore.$onAction((action) => {
if (action.name === 'login') setupSessionTimers();
});
if (!(error instanceof ErrorUnauthorized)) {
@ -579,7 +580,7 @@ onMounted(async () => {
await store.dispatch(APP_STATE_ACTIONS.CHANGE_FETCH_STATE, FetchState.LOADED);
if (store.getters.shouldOnboard && !store.state.appStateModule.viewsState.hasShownPricingPlan) {
if (usersStore.shouldOnboard && !store.state.appStateModule.viewsState.hasShownPricingPlan) {
store.commit(APP_STATE_MUTATIONS.SET_HAS_SHOWN_PRICING_PLAN, true);
// if the user is not legible for a pricing plan, they'll automatically be
// navigated back to all projects dashboard.

View File

@ -38,11 +38,13 @@ import { AnalyticsHttpApi } from '@/api/analytics';
import { RouteConfig } from '@/router';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { MODALS } from '@/utils/constants/appStatePopUps';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
import BoxIcon from '@/../static/images/allDashboard/box.svg';
const usersStore = useUsersStore();
const store = useStore();
const analytics = new AnalyticsHttpApi();
@ -52,8 +54,8 @@ const analytics = new AnalyticsHttpApi();
function onCreateProjectClicked(): void {
analytics.eventTriggered(AnalyticsEvent.CREATE_NEW_CLICKED);
const user: User = store.getters.user;
const ownProjectsCount: number = store.getters.projectsCount;
const user: User = usersStore.state.user;
const ownProjectsCount: number = store.getters.projectsCount(user.id);
if (!user.paidTier && user.projectLimit === ownProjectsCount) {
store.commit(APP_STATE_MUTATIONS.UPDATE_ACTIVE_MODAL, MODALS.createProjectPrompt);

View File

@ -108,13 +108,13 @@ import {
PM_ACTIONS,
} from '@/utils/constants/actionNames';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { USER_ACTIONS } from '@/store/modules/users';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { BUCKET_ACTIONS } from '@/store/modules/buckets';
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { AuthHttpApi } from '@/api/auth';
import { useABTestingStore } from '@/store/modules/abTestingStore';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
@ -134,10 +134,12 @@ import MenuIcon from '@/../static/images/navigation/menu.svg';
const router = useRouter();
const store = useStore();
const notify = useNotify();
const usersStore = useUsersStore();
const abTestingStore = useABTestingStore();
const analytics = new AnalyticsHttpApi();
const auth = new AuthHttpApi();
const notify = useNotify();
const link = 'https://docs.storj.io/';
@ -155,7 +157,7 @@ const satellite = computed((): string => {
* Returns user from store.
*/
const user = computed((): User => {
return store.getters.user;
return usersStore.state.user;
});
/**
@ -227,7 +229,7 @@ async function onLogout(): Promise<void> {
await Promise.all([
store.dispatch(PM_ACTIONS.CLEAR),
store.dispatch(PROJECTS_ACTIONS.CLEAR),
store.dispatch(USER_ACTIONS.CLEAR),
usersStore.clear(),
store.dispatch(ACCESS_GRANTS_ACTIONS.STOP_ACCESS_GRANTS_WEB_WORKER),
store.dispatch(ACCESS_GRANTS_ACTIONS.CLEAR),
store.dispatch(NOTIFICATION_ACTIONS.CLEAR),

View File

@ -59,7 +59,6 @@ import {
PM_ACTIONS,
} from '@/utils/constants/actionNames';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { USER_ACTIONS } from '@/store/modules/users';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { BUCKET_ACTIONS } from '@/store/modules/buckets';
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
@ -72,6 +71,7 @@ import { AnalyticsHttpApi } from '@/api/analytics';
import { AuthHttpApi } from '@/api/auth';
import { APP_STATE_DROPDOWNS } from '@/utils/constants/appStatePopUps';
import { useABTestingStore } from '@/store/modules/abTestingStore';
import { useUsersStore } from '@/store/modules/usersStore';
import AccountIcon from '@/../static/images/navigation/account.svg';
import ArrowDownIcon from '@/../static/images/common/dropIcon.svg';
@ -87,6 +87,7 @@ const notify = useNotify();
const analytics = new AnalyticsHttpApi();
const auth = new AuthHttpApi();
const usersStore = useUsersStore();
const abTestingStore = useABTestingStore();
const isHoveredOver = ref(false);
@ -153,7 +154,7 @@ async function onLogout(): Promise<void> {
await Promise.all([
store.dispatch(PM_ACTIONS.CLEAR),
store.dispatch(PROJECTS_ACTIONS.CLEAR),
store.dispatch(USER_ACTIONS.CLEAR),
usersStore.clear(),
store.dispatch(ACCESS_GRANTS_ACTIONS.STOP_ACCESS_GRANTS_WEB_WORKER),
store.dispatch(ACCESS_GRANTS_ACTIONS.CLEAR),
store.dispatch(NOTIFICATION_ACTIONS.CLEAR),

View File

@ -40,14 +40,17 @@ import { MODALS } from '@/utils/constants/appStatePopUps';
import { AnalyticsHttpApi } from '@/api/analytics';
import EmptyProjectItem from '@/views/all-dashboard/components/EmptyProjectItem.vue';
import ProjectItem from '@/views/all-dashboard/components/ProjectItem.vue';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
import RocketIcon from '@/../static/images/common/rocket.svg';
const usersStore = useUsersStore();
const store = useStore();
const router = useRouter();
const route = useRoute();
const store = useStore();
const analytics = new AnalyticsHttpApi();
/**
@ -63,8 +66,8 @@ const projects = computed((): Project[] => {
function onCreateProjectClicked(): void {
analytics.eventTriggered(AnalyticsEvent.CREATE_NEW_CLICKED);
const user: User = store.getters.user;
const ownProjectsCount: number = store.getters.projectsCount;
const user: User = usersStore.state.user;
const ownProjectsCount: number = store.getters.projectsCount(user.id);
if (!user.paidTier && user.projectLimit === ownProjectsCount) {
store.commit(APP_STATE_MUTATIONS.UPDATE_ACTIVE_MODAL, MODALS.createProjectPrompt);

View File

@ -46,10 +46,10 @@
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { computed } from 'vue';
import { Project } from '@/types/projects';
import { useNotify, useRoute, useRouter, useStore } from '@/utils/hooks';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
import {
AnalyticsEvent,
} from '@/utils/constants/analyticsEventNames';
@ -62,6 +62,7 @@ import { OBJECTS_MUTATIONS } from '@/store/modules/objects';
import { RouteConfig } from '@/router';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { MODALS } from '@/utils/constants/appStatePopUps';
import { useUsersStore } from '@/store/modules/usersStore';
import VButton from '@/components/common/VButton.vue';
import ProjectOwnershipTag from '@/components/project/ProjectOwnershipTag.vue';
@ -70,11 +71,12 @@ import GearIcon from '@/../static/images/common/gearIcon.svg';
import UsersIcon from '@/../static/images/navigation/users.svg';
import MenuIcon from '@/../static/images/allDashboard/menu.svg';
const router = useRouter();
const route = useRoute();
const usersStore = useUsersStore();
const store = useStore();
const analytics = new AnalyticsHttpApi();
const notify = useNotify();
const router = useRouter();
const analytics = new AnalyticsHttpApi();
const props = withDefaults(defineProps<{
project?: Project,
@ -93,7 +95,7 @@ const isDropdownOpen = computed((): boolean => {
* Returns user entity from store.
*/
const user = computed((): User => {
return store.getters.user;
return usersStore.state.user;
});
/**
@ -116,7 +118,7 @@ function closeDropDown() {
*/
async function onOpenClicked(): Promise<void> {
await selectProject();
if (store.getters.shouldOnboard) {
if (usersStore.shouldOnboard) {
analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
await router.push(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
return;

View File

@ -20,7 +20,6 @@ import { makeNotificationsModule } from '@/store/modules/notifications';
import { makePaymentsModule } from '@/store/modules/payments';
import { makeProjectMembersModule } from '@/store/modules/projectMembers';
import { makeProjectsModule } from '@/store/modules/projects';
import { makeUsersModule } from '@/store/modules/users';
import { User } from '@/types/users';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import { FetchState } from '@/utils/constants/fetchStateEnum';
@ -38,7 +37,6 @@ usersApi.setMockUser(new User('1', '2', '3', '4', '5', '6', 1));
projectsApi.setMockProjects([]);
const appStateModule = makeAppStateModule(new FrontendConfigApiMock());
const usersModule = makeUsersModule(usersApi);
const projectsModule = makeProjectsModule(projectsApi);
const accessGrantsModule = makeAccessGrantsModule(new AccessGrantsMock());
const teamMembersModule = makeProjectMembersModule(new ProjectMembersApiMock());
@ -51,7 +49,6 @@ const store = new Vuex.Store({
notificationsModule,
bucketsModule,
accessGrantsModule,
usersModule,
projectsModule,
appStateModule,
teamMembersModule,

View File

@ -1,30 +1,23 @@
// Copyright (C) 2019 Storj Labs, Inc.
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
import Vuex from 'vuex';
import { createLocalVue } from '@vue/test-utils';
import { createPinia, setActivePinia } from 'pinia';
import { AuthHttpApi } from '@/api/auth';
import { makeUsersModule, USER_ACTIONS, USER_MUTATIONS } from '@/store/modules/users';
import { UpdatedUser, User } from '@/types/users';
import { useUsersStore } from '@/store/modules/usersStore';
const Vue = createLocalVue();
const authApi = new AuthHttpApi();
const usersModule = makeUsersModule(authApi);
const { UPDATE, GET, CLEAR } = USER_ACTIONS;
Vue.use(Vuex);
const store = new Vuex.Store<typeof usersModule.state>(usersModule);
describe('mutations', () => {
describe('actions', () => {
beforeEach(() => {
createLocalVue().use(Vuex);
setActivePinia(createPinia());
jest.resetAllMocks();
});
it('Set user', () => {
it('set user', () => {
const store = useUsersStore();
const user = new User('1', 'fullName', 'shortName', 'user@example.com');
store.commit(USER_MUTATIONS.SET_USER, user);
store.setUser(user);
expect(store.state.user.id).toBe(user.id);
expect(store.state.user.email).toBe(user.email);
@ -33,48 +26,41 @@ describe('mutations', () => {
});
it('clear user', () => {
store.commit(USER_MUTATIONS.CLEAR);
const store = useUsersStore();
const user = new User('1', 'fullName', 'shortName', 'user@example.com');
store.setUser(user);
expect(store.state.user.id).toBe(user.id);
store.clear();
expect(store.state.user.id).toBe('');
expect(store.state.user.email).toBe('');
expect(store.state.user.fullName).toBe('');
expect(store.state.user.shortName).toBe('');
});
it('Update user', () => {
it('update user', async () => {
const store = useUsersStore();
const user = new UpdatedUser('fullName', 'shortName');
store.commit(USER_MUTATIONS.UPDATE_USER, user);
jest.spyOn(AuthHttpApi.prototype, 'update').mockImplementation(() => Promise.resolve());
await store.updateUser(user);
expect(store.state.user.fullName).toBe(user.fullName);
expect(store.state.user.shortName).toBe(user.shortName);
});
});
describe('actions', () => {
beforeEach(() => {
jest.resetAllMocks();
});
it('success update account', async () => {
jest.spyOn(authApi, 'update').mockReturnValue(
Promise.resolve(),
);
const user = new UpdatedUser('fullName1', 'shortName2');
await store.dispatch(UPDATE, user);
expect(store.state.user.fullName).toBe('fullName1');
expect(store.state.user.shortName).toBe('shortName2');
});
it('update throws an error when api call fails', async () => {
jest.spyOn(authApi, 'update').mockImplementation(() => { throw new Error(); });
const store = useUsersStore();
const newUser = new UpdatedUser('', '');
const oldUser = store.getters.user;
const oldUser = store.state.user;
jest.spyOn(AuthHttpApi.prototype, 'update').mockImplementation(() => { throw new Error(); });
try {
await store.dispatch(UPDATE, newUser);
await store.updateUser(newUser);
expect(true).toBe(false);
} catch (error) {
expect(store.state.user.fullName).toBe(oldUser.fullName);
@ -82,23 +68,15 @@ describe('actions', () => {
}
});
it('clears state', async () => {
await store.dispatch(CLEAR);
expect(store.state.user.fullName).toBe('');
expect(store.state.user.shortName).toBe('');
expect(store.state.user.email).toBe('');
expect(store.state.user.id).toBe('');
});
it('success get user', async () => {
const store = useUsersStore();
const user = new User('2', 'newFullName', 'newShortName', 'user2@example.com');
jest.spyOn(authApi, 'get').mockReturnValue(
jest.spyOn(AuthHttpApi.prototype, 'get').mockReturnValue(
Promise.resolve(user),
);
await store.dispatch(GET);
await store.getUser();
expect(store.state.user.id).toBe(user.id);
expect(store.state.user.shortName).toBe(user.shortName);
@ -107,11 +85,13 @@ describe('actions', () => {
});
it('get throws an error when api call fails', async () => {
const user = store.getters.user;
jest.spyOn(authApi, 'get').mockImplementation(() => { throw new Error(); });
const store = useUsersStore();
const user = store.state.user;
jest.spyOn(AuthHttpApi.prototype, 'get').mockImplementation(() => { throw new Error(); });
try {
await store.dispatch(GET);
await store.getUser();
expect(true).toBe(false);
} catch (error) {
expect(store.state.user.fullName).toBe(user.fullName);
@ -121,17 +101,13 @@ describe('actions', () => {
});
describe('getters', () => {
it('user model', function () {
const retrievedUser = store.getters.user;
expect(retrievedUser.id).toBe(store.state.user.id);
expect(retrievedUser.fullName).toBe(store.state.user.fullName);
expect(retrievedUser.shortName).toBe(store.state.user.shortName);
expect(retrievedUser.email).toBe(store.state.user.email);
beforeEach(() => {
setActivePinia(createPinia());
});
it('user name', function () {
const retrievedUserName = store.getters.userName;
const store = useUsersStore();
const retrievedUserName = store.userName;
expect(retrievedUserName).toBe(store.state.user.getFullName());
});