have the user accept an invitation (to avoid previews from stealing it)

This commit is contained in:
2026-01-10 18:46:01 -07:00
parent 051af89476
commit 02eb891ee9
4 changed files with 74 additions and 42 deletions

View File

@@ -545,7 +545,6 @@ export default class HomeView extends Vue {
});
await this.loadNewStarredProjectChanges();
await this.checkOnboarding();
logger.debug("[HomeView] mounted() - component lifecycle completed", {
timestamp: new Date().toISOString(),
@@ -594,21 +593,6 @@ export default class HomeView extends Vue {
}
}
/**
* Watch for changes in the current activeDid
* Reload settings when user switches identities
*/
async onActiveDidChanged(newDid: string | null, oldDid: string | null) {
if (newDid !== oldDid) {
// Re-initialize identity with new settings (loads settings internally)
await this.initializeIdentity();
} else {
logger.debug(
"[HomeView Settings Trace] 📍 DID unchanged, skipping re-initialization",
);
}
}
/**
* Initializes user identity
* - Retrieves existing DIDs
@@ -713,13 +697,6 @@ export default class HomeView extends Vue {
);
this.isAnyFeedFilterOn = checkIsAnyFeedFilterOn(settings);
// Check onboarding status
if (!settings.finishedOnboarding) {
(this.$refs.onboardingDialog as OnboardingDialog).open(
OnboardPage.Home,
);
}
// Check registration status if needed
if (!this.isRegistered && this.activeDid) {
try {
@@ -762,6 +739,12 @@ export default class HomeView extends Vue {
);
}
}
if (!settings.finishedOnboarding) {
(this.$refs.onboardingDialog as OnboardingDialog).open(
OnboardPage.Home,
);
}
} catch (err: unknown) {
logger.error("[HomeView Settings Trace] ❌ initializeIdentity() failed", {
error: err instanceof Error ? err.message : String(err),
@@ -963,20 +946,6 @@ export default class HomeView extends Vue {
}
}
/**
* Checks if user needs onboarding using ultra-concise mixin utilities
* Opens onboarding dialog if not completed
*
* @internal
* Called by mounted()
*/
private async checkOnboarding() {
const settings = await this.$accountSettings();
if (!settings.finishedOnboarding) {
(this.$refs.onboardingDialog as OnboardingDialog).open(OnboardPage.Home);
}
}
/**
* Handles errors during initialization
* - Logs error to console and database

View File

@@ -1,12 +1,39 @@
<template>
<QuickNav selected="Invite" />
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
<!-- Confirmation Dialog -->
<div v-if="showConfirmation" class="text-center mt-8">
<div class="bg-white rounded-lg shadow-lg p-6 max-w-md mx-auto">
<h2 class="text-xl font-semibold mb-4">Accept Invitation</h2>
<p class="mb-6 text-gray-700">
You've received an invitation to connect. Would you like to accept it?
</p>
<div class="flex justify-center gap-4">
<button
class="px-6 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition"
@click="confirmInvite"
>
Accept
</button>
<button
class="px-6 py-2 bg-gray-300 text-gray-700 rounded hover:bg-gray-400 transition"
@click="cancelInvite"
>
Cancel
</button>
</div>
</div>
</div>
<!-- Processing Spinner -->
<div
v-if="checkingInvite"
v-else-if="checkingInvite"
class="text-lg text-center font-light relative px-7"
>
<font-awesome icon="spinner" class="fa-spin-pulse" />
</div>
<!-- Error/Manual Entry -->
<div v-else class="text-center mt-4">
<p>That invitation did not work.</p>
<p class="mt-2">
@@ -48,6 +75,7 @@ import { errorStringForLog } from "../libs/endorserServer";
import { generateSaveAndActivateIdentity } from "../libs/util";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers } from "@/utils/notify";
import { logger } from "@/utils/logger";
import {
NOTIFY_INVITE_MISSING,
NOTIFY_INVITE_PROCESSING_ERROR,
@@ -101,6 +129,10 @@ export default class InviteOneAcceptView extends Vue {
checkingInvite = true;
/** User input for manual JWT entry */
inputJwt = "";
/** Show confirmation dialog */
showConfirmation = false;
/** JWT pending user confirmation */
pendingJwtParameter = "";
/**
* Component lifecycle hook that initializes invite processing
@@ -110,7 +142,7 @@ export default class InviteOneAcceptView extends Vue {
* 2. Retrieves account settings
* 3. Ensures active DID exists or generates one
* 4. Extracts JWT from URL path
* 5. Processes invite automatically
* 5. Shows confirmation dialog to user
*
* @throws Will not throw but logs errors
* @emits Notifications on errors
@@ -141,11 +173,33 @@ export default class InviteOneAcceptView extends Vue {
(this.$route.params.jwt as string) ||
(this.$route.query.jwt as string) ||
"";
await this.processInvite(jwt, false);
// Show confirmation dialog if JWT is present
if (jwt) {
this.pendingJwtParameter = jwt;
this.showConfirmation = true;
}
this.checkingInvite = false;
}
/**
* Confirms and processes the pending invite
*/
async confirmInvite() {
this.showConfirmation = false;
await this.processInvite(this.pendingJwtParameter, true);
}
/**
* Cancels the pending invite
*/
cancelInvite() {
this.showConfirmation = false;
this.pendingJwtParameter = "";
this.checkingInvite = false;
// Show error state (manual entry interface)
}
/**
* Processes an invite JWT and/or text containing the invite
*
@@ -191,6 +245,7 @@ export default class InviteOneAcceptView extends Vue {
if (!jwt) {
this.handleMissingJwt(notifyOnFailure);
this.checkingInvite = false;
return;
}

View File

@@ -17,8 +17,10 @@
* - Custom notes
* 3. Verifies the invite appears in the list
* 4. Creates a new user with Ethr DID
* 5. Accepts the invitation as the new user
* 6. Verifies the connection is established
* 5. Navigates to the invitation link
* 6. Confirms the invitation in the acceptance dialog
* 7. Accepts the invitation as the new user
* 8. Verifies the connection is established
*
* Related Files:
* - Frontend invite handling: src/libs/endorserServer.ts
@@ -55,6 +57,10 @@ test('Check User 0 can invite someone', async ({ page }) => {
await switchToUser(page, newDid);
await page.goto(inviteLink as string);
// Wait for and click the Accept button in the confirmation dialog
await expect(page.locator('button:has-text("Accept")')).toBeVisible();
await page.locator('button:has-text("Accept")').click();
// Wait for the ContactNameDialog to appear before trying to fill the Name field
await expect(page.getByPlaceholder('Name', { exact: true })).toBeVisible();
await page.getByPlaceholder('Name', { exact: true }).fill(`My pal User #0`);

View File

@@ -13,6 +13,8 @@ test('New offers for another user', async ({ page }) => {
}
await page.getByTestId('closeOnboardingAndFinish').click();
// Wait for onboarding dialog to fully close and settings to persist
await expect(page.locator('.dialog-overlay')).toBeHidden({ timeout: 5000 });
await expect(page.getByTestId('newDirectOffersActivityNumber')).toBeHidden();
// Become User Zero