diff --git a/_data/serverState.ts b/_data/serverState.ts index 240ef96af..ff59725fa 100644 --- a/_data/serverState.ts +++ b/_data/serverState.ts @@ -51,7 +51,7 @@ const serverState = { "locale": "en_US", "name": "fuji", // "pluginInstalled": "dynamix.unraid.net.staging.plg", - "pluginInstalled": true, + "pluginInstalled": false, "registered": true, "regGen": 0, "regGuid": "0781-5583-8355-81071A2B0211", diff --git a/components/Brand/Button.vue b/components/Brand/Button.vue index cb8648c4c..b3d9bde1f 100644 --- a/components/Brand/Button.vue +++ b/components/Brand/Button.vue @@ -1,21 +1,21 @@ + + diff --git a/components/UserProfile/DropdownContent.vue b/components/UserProfile/DropdownContent.vue index 374037021..b183d5d9e 100644 --- a/components/UserProfile/DropdownContent.vue +++ b/components/UserProfile/DropdownContent.vue @@ -3,7 +3,6 @@ import { storeToRefs } from 'pinia'; import { ArrowRightOnRectangleIcon, ArrowTopRightOnSquareIcon, BarsArrowDownIcon, CogIcon, InformationCircleIcon, UserIcon } from '@heroicons/vue/24/solid'; import { ACCOUNT, CONNECT_DASHBOARD, PLUGIN_SETTINGS } from '~/helpers/urls'; -import { useDropdownStore } from '~/store/dropdown'; import { usePromoStore } from '~/store/promo'; import { useServerStore } from '~/store/server'; import type { UserProfileLink } from '~/types/userProfile'; @@ -12,7 +11,6 @@ import type { ServerStateDataAction } from '~/types/server'; const myServersEnv = ref('Staging'); const devEnv = ref('development'); -const dropdownStore = useDropdownStore(); const promoStore = usePromoStore(); const { keyActions, pluginInstalled, registered, stateData } = storeToRefs(useServerStore()); @@ -59,7 +57,6 @@ const links = computed(():UserProfileLink[] => { { click: () => { promoStore.promoShow(); - dropdownStore.dropdownHide(); }, icon: InformationCircleIcon, text: 'Enhance your Unraid experience with Connect', diff --git a/components/UserProfile/Promo.vue b/components/UserProfile/Promo.vue index 353fa0aab..7b8ad73b4 100644 --- a/components/UserProfile/Promo.vue +++ b/components/UserProfile/Promo.vue @@ -62,32 +62,32 @@ const installButtonClasses = 'text-white text-14px text-center w-full flex flex- diff --git a/components/UserProfile/PromoFeature.vue b/components/UserProfile/PromoFeature.vue index 188040b27..23b19f9a9 100644 --- a/components/UserProfile/PromoFeature.vue +++ b/components/UserProfile/PromoFeature.vue @@ -20,14 +20,14 @@ defineProps(); -
-

+
+

{{ title }}

-

+

\ No newline at end of file diff --git a/store/account.ts b/store/account.ts index 15033e8ee..33729081d 100644 --- a/store/account.ts +++ b/store/account.ts @@ -18,6 +18,8 @@ export const useAccountStore = defineStore('account', () => { const accountAction = ref(); const accountActionStatus = ref<'failed' | 'ready' | 'success' | 'updating'>('ready'); + const username = ref(''); + // Actions const recover = () => { console.debug('[accountStore.recover]'); @@ -70,17 +72,20 @@ export const useAccountStore = defineStore('account', () => { */ const updatePluginConfig = async (action: ExternalSignIn | ExternalSignOut) => { console.debug('[accountStore.updatePluginConfig]', action); + // save any existing username before updating + if (serverStore.username) username.value = serverStore.username; + accountAction.value = action; accountActionStatus.value = 'updating'; const userPayload = { - ...(action.user + ...(accountAction.value.user ? { - apikey: action.apiKey, + apikey: accountAction.value.apiKey, // avatar: '', - email: action.user?.email, + email: accountAction.value.user?.email, regWizTime: `${Date.now()}_${serverStore.guid}`, // set when signing in the first time and never unset for the sake of displaying Sign In/Up in the UPC without needing to validate guid every time - username: action.user?.preferred_username, + username: accountAction.value.user?.preferred_username, } : { accesstoken: '', @@ -126,20 +131,20 @@ export const useAccountStore = defineStore('account', () => { case 'updating': return { text: accountAction.value?.type === 'signIn' - ? 'Signing in...' - : 'Signing out...', + ? `Signing in ${accountAction.value.user?.preferred_username}...` + : `Signing out ${username.value}...`, }; case 'success': return { text: accountAction.value?.type === 'signIn' - ? 'Signed in successfully' - : 'Signed out successfully', + ? `Signed ${accountAction.value.user?.preferred_username} In Successfully` + : `Signed Out ${username.value} Successfully`, }; case 'failed': return { text: accountAction.value?.type === 'signIn' - ? 'Sign in failed' - : 'Sign out failed', + ? 'Sign In Failed' + : 'Sign Out Failed', }; } }); diff --git a/store/callback.ts b/store/callback.ts index 875fd765e..718053084 100644 --- a/store/callback.ts +++ b/store/callback.ts @@ -1,9 +1,32 @@ import AES from 'crypto-js/aes'; import Utf8 from 'crypto-js/enc-utf8'; -import { ref } from 'vue'; import { defineStore, createPinia, setActivePinia } from 'pinia'; -export interface ServerAccountCallbackServerData { +/** + * @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components + * @see https://github.com/vuejs/pinia/discussions/1085 + */ +setActivePinia(createPinia()); + +export type SignIn = 'signIn'; +export type SignOut = 'signOut'; +export type OemSignOut = 'oemSignOut'; +export type Troubleshoot = 'troubleshoot'; +export type Recover = 'recover'; +export type Replace = 'replace'; +export type TrialExtend = 'trialExtend'; +export type TrialStart = 'trialStart'; +export type Purchase = 'purchase'; +export type Redeem = 'redeem'; +export type Upgrade = 'upgrade'; + +export type AccountAction = SignIn | SignOut | OemSignOut | Troubleshoot; +export type AccountKeyAction = Recover | Replace | TrialExtend | TrialStart; +export type PurchaseAction = Purchase | Redeem | Upgrade; + +export type ServerAction = AccountAction | AccountKeyAction | PurchaseAction; + +export interface ServerData { description?: string; deviceCount?: number; expireTime?: number; @@ -20,26 +43,9 @@ export interface ServerAccountCallbackServerData { wanFQDN?: string; } -export type SignIn = 'signIn'; -export type SignOut = 'signOut'; -export type Troubleshoot = 'troubleshoot'; -export type Recover = 'recover'; -export type Replace = 'replace'; -export type TrialExtend = 'trialExtend'; -export type TrialStart = 'trialStart'; -export type Purchase = 'purchase'; -export type Redeem = 'redeem'; -export type Upgrade = 'upgrade'; - -export type AccountAction = SignIn | SignOut | Troubleshoot; -export type AccountKeyAction = Recover | Replace | TrialExtend | TrialStart; -export type PurchaseAction = Purchase | Redeem | Upgrade; - -export type ServerStateDataActionType = AccountAction | AccountKeyAction | PurchaseAction; - export interface ServerPayload { - server: ServerAccountCallbackServerData; - type: ServerStateDataActionType; + server: ServerData; + type: ServerAction; } export interface ExternalSignIn { @@ -48,7 +54,7 @@ export interface ExternalSignIn { user: UserInfo; } export interface ExternalSignOut { - type: SignOut; + type: SignOut | OemSignOut; } export interface ExternalKeyActions { type: PurchaseAction | AccountKeyAction; @@ -92,12 +98,6 @@ interface CallbackActionsStore { sendType: 'fromUpc' | 'forUpc'; } -/** - * @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components - * @see https://github.com/vuejs/pinia/discussions/1085 - */ -setActivePinia(createPinia()); - export const useCallbackStoreGeneric = ( useCallbackActions: () => CallbackActionsStore, ) => @@ -105,10 +105,7 @@ export const useCallbackStoreGeneric = ( const callbackActions = useCallbackActions(); const encryptionKey = 'Uyv2o8e*FiQe8VeLekTqyX6Z*8XonB'; const sendType = 'fromUpc'; - // state - const encryptedMessage = ref(null); - // actions const send = (url: string, payload: SendPayloads) => { console.debug('[callback.send]'); const stringifiedData = JSON.stringify({ @@ -118,11 +115,11 @@ export const useCallbackStoreGeneric = ( sender: window.location.href, type: sendType, }); - encryptedMessage.value = AES.encrypt(stringifiedData, encryptionKey).toString(); + const encryptedMessage = AES.encrypt(stringifiedData, encryptionKey).toString(); // build and go to url const destinationUrl = new URL(url); - destinationUrl.searchParams.set('data', encodeURI(encryptedMessage.value)); - console.debug('[callback.send]', encryptedMessage.value, destinationUrl); + destinationUrl.searchParams.set('data', encodeURI(encryptedMessage)); + console.debug('[callback.send]', encryptedMessage, destinationUrl); window.location.href = destinationUrl.toString(); return; }; @@ -144,7 +141,6 @@ export const useCallbackStoreGeneric = ( }; return { - // actions send, watcher, }; diff --git a/store/callbackActions.ts b/store/callbackActions.ts index e70d8a32d..f70675c7f 100644 --- a/store/callbackActions.ts +++ b/store/callbackActions.ts @@ -2,7 +2,7 @@ import { defineStore } from 'pinia'; import { useAccountStore } from './account'; import { useInstallKeyStore } from './installKey'; -import { useCallbackStoreGeneric, type ExternalPayload, type ExternalActions, type ExternalKeyActions, type QueryPayloads } from './callback'; +import { useCallbackStoreGeneric, type ExternalPayload, type ExternalKeyActions, type QueryPayloads } from './callback'; // import { useServerStore } from './server'; export const useCallbackActionsStore = defineStore( @@ -11,7 +11,7 @@ export const useCallbackActionsStore = defineStore( const accountStore = useAccountStore(); const installKeyStore = useInstallKeyStore(); // const serverStore = useServerStore(); - type CallbackStatus = 'error' | 'loading' | 'ready' | 'done'; + type CallbackStatus = 'error' | 'loading' | 'ready' | 'success'; const callbackStatus = ref('ready'); const callbackData = ref(); @@ -38,22 +38,21 @@ export const useCallbackActionsStore = defineStore( if (action?.keyUrl) { await installKeyStore.install(action as ExternalKeyActions); } - /** @todo add oemSignOut */ - if (action?.user || action.type === 'signOut') { + if (action?.user || action.type === 'signOut' || action.type === 'oemSignOut') { await accountStore.updatePluginConfig(action); } // all actions have run if (array.length === (index + 1)) { - callbackStatus.value = 'done'; - // if (array.length > 1) { - // // if we have more than 1 action it means there was a key install and an account action so both need to be successful - // const allSuccess = accountStore.accountActionStatus === 'success' && installKeyStore.keyInstallStatus === 'success'; - // callbackStatus.value = allSuccess ? 'success' : 'error'; - // } else { - // // only 1 action needs to be successful - // const oneSuccess = accountStore.accountActionStatus === 'success' || installKeyStore.keyInstallStatus === 'success'; - // callbackStatus.value = oneSuccess ? 'success' : 'error'; - // } + // callbackStatus.value = 'done'; + if (array.length > 1) { + // if we have more than 1 action it means there was a key install and an account action so both need to be successful + const allSuccess = accountStore.accountActionStatus === 'success' && installKeyStore.keyInstallStatus === 'success'; + callbackStatus.value = allSuccess ? 'success' : 'error'; + } else { + // only 1 action needs to be successful + const oneSuccess = accountStore.accountActionStatus === 'success' || installKeyStore.keyInstallStatus === 'success'; + callbackStatus.value = oneSuccess ? 'success' : 'error'; + } } }); }; @@ -83,6 +82,10 @@ export const useCallbackActionsStore = defineStore( } }); + watch(callbackData, () => { + console.debug('[callbackData] watch', callbackData.value); + }); + return { redirectToCallbackType, callbackData, diff --git a/store/installKey.ts b/store/installKey.ts index 815383bc3..34b4c3bb3 100644 --- a/store/installKey.ts +++ b/store/installKey.ts @@ -97,6 +97,7 @@ export const useInstallKeyStore = defineStore('installKey', () => { // State keyInstallStatus, // getters + keyActionType, keyInstallStatusCopy, keyType, keyUrl, diff --git a/store/promo.ts b/store/promo.ts index a099edd3b..35a685d41 100644 --- a/store/promo.ts +++ b/store/promo.ts @@ -1,6 +1,8 @@ import { useToggle } from '@vueuse/core'; import { defineStore, createPinia, setActivePinia } from 'pinia'; +import { useDropdownStore } from '~/store/dropdown'; + /** * @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components * @see https://github.com/vuejs/pinia/discussions/1085 @@ -8,17 +10,33 @@ import { defineStore, createPinia, setActivePinia } from 'pinia'; setActivePinia(createPinia()); export const usePromoStore = defineStore('promo', () => { - const promoVisible = ref(false); + const dropdownStore = useDropdownStore(); + const promoVisible = ref(false); + + const openOnNextLoad = () => sessionStorage.setItem('unraidConnectPromo', 'show'); const promoHide = () => promoVisible.value = false; const promoShow = () => promoVisible.value = true; const promoToggle = useToggle(promoVisible); + watch(promoVisible, (newVal, _oldVal) => { console.debug('[promoVisible]', newVal, _oldVal); + if (newVal) { // close the dropdown when the promo is opened + dropdownStore.dropdownHide(); + } + }); + + onBeforeMount(() => { + if (sessionStorage.getItem('unraidConnectPromo') === 'show') { + sessionStorage.removeItem('unraidConnectPromo'); + promoShow(); + } }); return { promoVisible, + + openOnNextLoad, promoHide, promoShow, promoToggle, diff --git a/types/callback.ts b/types/callback.ts deleted file mode 100644 index 931a7ec1a..000000000 --- a/types/callback.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { CognitoUser, ChallengeName } from 'amazon-cognito-identity-js'; -import type { - ServerAccountCallbackSendPayload, - ServerPurchaseCallbackSendPayload, - ServerStateDataActionType, -} from '~/types/server'; - -/** - * These user interfaces are mimiced from the Auth repo - */ -export interface UserInfo { - 'custom:ips_id'?: string; - email?: string; - email_verifed?: 'true' | 'false'; - preferred_username?: string; - sub?: string; - username?: string; -} -export interface CallbackSendPayload { - server: ServerAccountCallbackSendPayload|ServerPurchaseCallbackSendPayload; - type: ServerStateDataActionType; -} - -export interface CallbackAction { - apiKey?: string; - keyUrl?: string; - type: ServerStateDataActionType; - user?: UserInfo; -} - -export interface CallbackReceivePayload { - actions: CallbackAction[]; - sender: string; -} \ No newline at end of file