forked from jsnbuchanan/crowd-funder-for-time-pwa
Merge branch 'master' into profile_include_location
This commit is contained in:
@@ -1,33 +1,154 @@
|
|||||||
|
/** * @file RegistrationNotice.vue * @description Reusable component for
|
||||||
|
displaying user registration status and related actions. * Shows registration
|
||||||
|
notice when user is not registered, with options to show identifier info * or
|
||||||
|
access advanced options. * * @author Jose Olarte III * @version 1.0.0 * @created
|
||||||
|
2025-08-21T17:25:28-08:00 */
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="!isRegistered && show"
|
id="noticeSomeoneMustRegisterYou"
|
||||||
id="noticeBeforeAnnounce"
|
class="bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 my-4"
|
||||||
class="bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 mt-4"
|
|
||||||
role="alert"
|
|
||||||
aria-live="polite"
|
|
||||||
>
|
>
|
||||||
<p class="mb-4">
|
<p class="mb-4">{{ message }}</p>
|
||||||
Before you can publicly announce a new project or time commitment, a
|
<div class="grid grid-cols-1 gap-2 sm:flex sm:justify-center">
|
||||||
friend needs to register you.
|
|
||||||
</p>
|
|
||||||
<button
|
<button
|
||||||
class="inline-block text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md"
|
class="inline-block text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md"
|
||||||
@click="shareInfo"
|
@click="showNameThenIdDialog"
|
||||||
>
|
>
|
||||||
Share Your Info
|
Show them {{ passkeysEnabled ? "default" : "your" }} identifier info
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="inline-block text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md"
|
||||||
|
@click="openAdvancedOptions"
|
||||||
|
>
|
||||||
|
See advanced options
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<UserNameDialog ref="userNameDialog" />
|
||||||
|
<ChoiceButtonDialog ref="choiceButtonDialog" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue, Prop, Emit } from "vue-facing-decorator";
|
import { Component, Vue, Prop } from "vue-facing-decorator";
|
||||||
|
import { Router } from "vue-router";
|
||||||
|
import { Capacitor } from "@capacitor/core";
|
||||||
|
import UserNameDialog from "./UserNameDialog.vue";
|
||||||
|
import ChoiceButtonDialog from "./ChoiceButtonDialog.vue";
|
||||||
|
|
||||||
@Component({ name: "RegistrationNotice" })
|
/**
|
||||||
|
* RegistrationNotice Component
|
||||||
|
*
|
||||||
|
* Displays registration status notice and provides actions for unregistered users.
|
||||||
|
* Handles all registration-related flows internally without requiring parent component intervention.
|
||||||
|
*
|
||||||
|
* Template Usage:
|
||||||
|
* ```vue
|
||||||
|
* <RegistrationNotice
|
||||||
|
* v-if="!isUserRegistered"
|
||||||
|
* :passkeys-enabled="PASSKEYS_ENABLED"
|
||||||
|
* :given-name="givenName"
|
||||||
|
* message="Custom registration message here"
|
||||||
|
* />
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Component Dependencies:
|
||||||
|
* - UserNameDialog: Dialog for entering user name
|
||||||
|
* - ChoiceButtonDialog: Dialog for sharing method selection
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
name: "RegistrationNotice",
|
||||||
|
components: {
|
||||||
|
UserNameDialog,
|
||||||
|
ChoiceButtonDialog,
|
||||||
|
},
|
||||||
|
})
|
||||||
export default class RegistrationNotice extends Vue {
|
export default class RegistrationNotice extends Vue {
|
||||||
@Prop({ required: true }) isRegistered!: boolean;
|
$router!: Router;
|
||||||
@Prop({ required: true }) show!: boolean;
|
|
||||||
|
|
||||||
@Emit("share-info")
|
/**
|
||||||
shareInfo() {}
|
* Whether passkeys are enabled in the application
|
||||||
|
*/
|
||||||
|
@Prop({ required: true })
|
||||||
|
passkeysEnabled!: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User's given name for dialog pre-population
|
||||||
|
*/
|
||||||
|
@Prop({ required: true })
|
||||||
|
givenName!: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom message to display in the registration notice
|
||||||
|
* Defaults to "To share, someone must register you."
|
||||||
|
*/
|
||||||
|
@Prop({ default: "To share, someone must register you." })
|
||||||
|
message!: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows name input dialog if needed
|
||||||
|
* Handles the full flow internally without requiring parent component intervention
|
||||||
|
*/
|
||||||
|
showNameThenIdDialog() {
|
||||||
|
this.openUserNameDialog(() => {
|
||||||
|
this.promptForShareMethod();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens advanced options page
|
||||||
|
* Navigates directly to the start page
|
||||||
|
*/
|
||||||
|
openAdvancedOptions() {
|
||||||
|
this.$router.push({ name: "start" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows dialog for sharing method selection
|
||||||
|
* Provides options for different sharing scenarios
|
||||||
|
*/
|
||||||
|
promptForShareMethod() {
|
||||||
|
(this.$refs.choiceButtonDialog as ChoiceButtonDialog).open({
|
||||||
|
title: "How can you share your info?",
|
||||||
|
text: "",
|
||||||
|
option1Text: "We are nearby with cameras",
|
||||||
|
option2Text: "Someone created a meeting room",
|
||||||
|
option3Text: "We will share some other way",
|
||||||
|
onOption1: () => {
|
||||||
|
this.handleQRCodeClick();
|
||||||
|
},
|
||||||
|
onOption2: () => {
|
||||||
|
this.$router.push({ name: "onboard-meeting-list" });
|
||||||
|
},
|
||||||
|
onOption3: () => {
|
||||||
|
this.$router.push({ name: "share-my-contact-info" });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles QR code sharing based on platform
|
||||||
|
* Navigates to appropriate QR code page
|
||||||
|
*/
|
||||||
|
private handleQRCodeClick() {
|
||||||
|
if (Capacitor.isNativePlatform()) {
|
||||||
|
this.$router.push({ name: "contact-qr-scan-full" });
|
||||||
|
} else {
|
||||||
|
this.$router.push({ name: "contact-qr" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the user name dialog if needed
|
||||||
|
*
|
||||||
|
* @param callback Function to call after name is entered
|
||||||
|
*/
|
||||||
|
openUserNameDialog(callback: () => void) {
|
||||||
|
if (!this.givenName) {
|
||||||
|
(this.$refs.userNameDialog as UserNameDialog).open(callback);
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -55,9 +55,10 @@
|
|||||||
|
|
||||||
<!-- Registration notice -->
|
<!-- Registration notice -->
|
||||||
<RegistrationNotice
|
<RegistrationNotice
|
||||||
:is-registered="isRegistered"
|
v-if="!isRegistered"
|
||||||
:show="showRegistrationNotice"
|
:passkeys-enabled="PASSKEYS_ENABLED"
|
||||||
@share-info="onShareInfo"
|
:given-name="givenName"
|
||||||
|
message="Before you can publicly announce a new project or time commitment, a friend needs to register you."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Notifications -->
|
<!-- Notifications -->
|
||||||
@@ -781,6 +782,7 @@ import {
|
|||||||
DEFAULT_PUSH_SERVER,
|
DEFAULT_PUSH_SERVER,
|
||||||
IMAGE_TYPE_PROFILE,
|
IMAGE_TYPE_PROFILE,
|
||||||
NotificationIface,
|
NotificationIface,
|
||||||
|
PASSKEYS_ENABLED,
|
||||||
} from "../constants/app";
|
} from "../constants/app";
|
||||||
import { Contact } from "../db/tables/contacts";
|
import { Contact } from "../db/tables/contacts";
|
||||||
import {
|
import {
|
||||||
@@ -851,6 +853,7 @@ export default class AccountViewView extends Vue {
|
|||||||
readonly DEFAULT_PUSH_SERVER: string = DEFAULT_PUSH_SERVER;
|
readonly DEFAULT_PUSH_SERVER: string = DEFAULT_PUSH_SERVER;
|
||||||
readonly DEFAULT_IMAGE_API_SERVER: string = DEFAULT_IMAGE_API_SERVER;
|
readonly DEFAULT_IMAGE_API_SERVER: string = DEFAULT_IMAGE_API_SERVER;
|
||||||
readonly DEFAULT_PARTNER_API_SERVER: string = DEFAULT_PARTNER_API_SERVER;
|
readonly DEFAULT_PARTNER_API_SERVER: string = DEFAULT_PARTNER_API_SERVER;
|
||||||
|
readonly PASSKEYS_ENABLED: boolean = PASSKEYS_ENABLED;
|
||||||
|
|
||||||
// Identity and settings properties
|
// Identity and settings properties
|
||||||
activeDid: string = "";
|
activeDid: string = "";
|
||||||
@@ -1780,20 +1783,6 @@ export default class AccountViewView extends Vue {
|
|||||||
this.doCopyTwoSecRedo(did, () => (this.showDidCopy = !this.showDidCopy));
|
this.doCopyTwoSecRedo(did, () => (this.showDidCopy = !this.showDidCopy));
|
||||||
}
|
}
|
||||||
|
|
||||||
get showRegistrationNotice(): boolean {
|
|
||||||
// Show the notice if not registered and any other conditions you want
|
|
||||||
return !this.isRegistered;
|
|
||||||
}
|
|
||||||
|
|
||||||
onShareInfo() {
|
|
||||||
// Navigate to QR code sharing page - mobile uses full scan, web uses basic
|
|
||||||
if (Capacitor.isNativePlatform()) {
|
|
||||||
this.$router.push({ name: "contact-qr-scan-full" });
|
|
||||||
} else {
|
|
||||||
this.$router.push({ name: "contact-qr" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onRecheckLimits() {
|
onRecheckLimits() {
|
||||||
this.checkLimits();
|
this.checkLimits();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,33 +86,14 @@ Raymer * @version 1.0.0 */
|
|||||||
Identity creation is now handled by router navigation guard.
|
Identity creation is now handled by router navigation guard.
|
||||||
-->
|
-->
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<div
|
<RegistrationNotice
|
||||||
v-if="!isUserRegistered"
|
v-if="!isUserRegistered"
|
||||||
id="noticeSomeoneMustRegisterYou"
|
:passkeys-enabled="PASSKEYS_ENABLED"
|
||||||
class="bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4"
|
:given-name="givenName"
|
||||||
>
|
message="To share, someone must register you."
|
||||||
To share, someone must register you.
|
/>
|
||||||
<div class="block text-center">
|
|
||||||
<button
|
|
||||||
class="text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"
|
|
||||||
@click="showNameThenIdDialog()"
|
|
||||||
>
|
|
||||||
Show them {{ PASSKEYS_ENABLED ? "default" : "your" }} identifier
|
|
||||||
info
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<UserNameDialog ref="userNameDialog" />
|
|
||||||
<div class="flex justify-end w-full">
|
|
||||||
<router-link
|
|
||||||
:to="{ name: 'start' }"
|
|
||||||
class="block text-right text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"
|
|
||||||
>
|
|
||||||
See advanced options
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else id="sectionRecordSomethingGiven">
|
<div v-if="isUserRegistered" id="sectionRecordSomethingGiven">
|
||||||
<!-- Record Quick-Action -->
|
<!-- Record Quick-Action -->
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<div class="flex gap-2 items-center mb-2">
|
<div class="flex gap-2 items-center mb-2">
|
||||||
@@ -252,8 +233,6 @@ Raymer * @version 1.0.0 */
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<ChoiceButtonDialog ref="choiceButtonDialog" />
|
|
||||||
|
|
||||||
<ImageViewer v-model:is-open="isImageViewerOpen" :image-url="selectedImage" />
|
<ImageViewer v-model:is-open="isImageViewerOpen" :image-url="selectedImage" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -261,7 +240,6 @@ Raymer * @version 1.0.0 */
|
|||||||
import { UAParser } from "ua-parser-js";
|
import { UAParser } from "ua-parser-js";
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
import { Router } from "vue-router";
|
import { Router } from "vue-router";
|
||||||
import { Capacitor } from "@capacitor/core";
|
|
||||||
|
|
||||||
//import App from "../App.vue";
|
//import App from "../App.vue";
|
||||||
import EntityIcon from "../components/EntityIcon.vue";
|
import EntityIcon from "../components/EntityIcon.vue";
|
||||||
@@ -272,10 +250,9 @@ import InfiniteScroll from "../components/InfiniteScroll.vue";
|
|||||||
import OnboardingDialog from "../components/OnboardingDialog.vue";
|
import OnboardingDialog from "../components/OnboardingDialog.vue";
|
||||||
import QuickNav from "../components/QuickNav.vue";
|
import QuickNav from "../components/QuickNav.vue";
|
||||||
import TopMessage from "../components/TopMessage.vue";
|
import TopMessage from "../components/TopMessage.vue";
|
||||||
import UserNameDialog from "../components/UserNameDialog.vue";
|
|
||||||
import ChoiceButtonDialog from "../components/ChoiceButtonDialog.vue";
|
|
||||||
import ImageViewer from "../components/ImageViewer.vue";
|
import ImageViewer from "../components/ImageViewer.vue";
|
||||||
import ActivityListItem from "../components/ActivityListItem.vue";
|
import ActivityListItem from "../components/ActivityListItem.vue";
|
||||||
|
import RegistrationNotice from "../components/RegistrationNotice.vue";
|
||||||
import {
|
import {
|
||||||
AppString,
|
AppString,
|
||||||
NotificationIface,
|
NotificationIface,
|
||||||
@@ -383,12 +360,11 @@ interface FeedError {
|
|||||||
GiftedPrompts,
|
GiftedPrompts,
|
||||||
InfiniteScroll,
|
InfiniteScroll,
|
||||||
OnboardingDialog,
|
OnboardingDialog,
|
||||||
ChoiceButtonDialog,
|
|
||||||
QuickNav,
|
QuickNav,
|
||||||
TopMessage,
|
TopMessage,
|
||||||
UserNameDialog,
|
|
||||||
ImageViewer,
|
ImageViewer,
|
||||||
ActivityListItem,
|
ActivityListItem,
|
||||||
|
RegistrationNotice,
|
||||||
},
|
},
|
||||||
mixins: [PlatformServiceMixin],
|
mixins: [PlatformServiceMixin],
|
||||||
})
|
})
|
||||||
@@ -1644,67 +1620,6 @@ export default class HomeView extends Vue {
|
|||||||
return known ? "text-slate-500" : "text-slate-100";
|
return known ? "text-slate-500" : "text-slate-100";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows name input dialog if needed
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @callGraph
|
|
||||||
* Called by: Template
|
|
||||||
* Calls:
|
|
||||||
* - UserNameDialog.open()
|
|
||||||
* - promptForShareMethod()
|
|
||||||
*
|
|
||||||
* @chain
|
|
||||||
* Template -> showNameThenIdDialog() -> promptForShareMethod()
|
|
||||||
*
|
|
||||||
* @requires
|
|
||||||
* - this.$refs.userNameDialog
|
|
||||||
* - this.givenName
|
|
||||||
*/
|
|
||||||
showNameThenIdDialog() {
|
|
||||||
if (!this.givenName) {
|
|
||||||
(this.$refs.userNameDialog as UserNameDialog).open(() => {
|
|
||||||
this.promptForShareMethod();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.promptForShareMethod();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows dialog for sharing method selection
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
* @callGraph
|
|
||||||
* Called by: showNameThenIdDialog()
|
|
||||||
* Calls: ChoiceButtonDialog.open()
|
|
||||||
*
|
|
||||||
* @chain
|
|
||||||
* Template -> showNameThenIdDialog() -> promptForShareMethod()
|
|
||||||
*
|
|
||||||
* @requires
|
|
||||||
* - this.$refs.choiceButtonDialog
|
|
||||||
* - this.$router
|
|
||||||
*/
|
|
||||||
promptForShareMethod() {
|
|
||||||
(this.$refs.choiceButtonDialog as ChoiceButtonDialog).open({
|
|
||||||
title: "How can you share your info?",
|
|
||||||
text: "",
|
|
||||||
option1Text: "We are in a meeting together",
|
|
||||||
option2Text: "We are nearby with cameras",
|
|
||||||
option3Text: "We will share some other way",
|
|
||||||
onOption1: () => {
|
|
||||||
this.$router.push({ name: "onboard-meeting-list" });
|
|
||||||
},
|
|
||||||
onOption2: () => {
|
|
||||||
this.handleQRCodeClick();
|
|
||||||
},
|
|
||||||
onOption3: () => {
|
|
||||||
this.$router.push({ name: "share-my-contact-info" });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens image viewer dialog
|
* Opens image viewer dialog
|
||||||
*
|
*
|
||||||
@@ -1717,14 +1632,6 @@ export default class HomeView extends Vue {
|
|||||||
this.isImageViewerOpen = true;
|
this.isImageViewerOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleQRCodeClick() {
|
|
||||||
if (Capacitor.isNativePlatform()) {
|
|
||||||
this.$router.push({ name: "contact-qr-scan-full" });
|
|
||||||
} else {
|
|
||||||
this.$router.push({ name: "contact-qr" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
openPersonDialog(
|
openPersonDialog(
|
||||||
giver?: GiverReceiverInputInfo | "Unnamed",
|
giver?: GiverReceiverInputInfo | "Unnamed",
|
||||||
prompt?: string,
|
prompt?: string,
|
||||||
|
|||||||
@@ -216,15 +216,12 @@
|
|||||||
<font-awesome icon="plus" :class="plusIconClasses" />
|
<font-awesome icon="plus" :class="plusIconClasses" />
|
||||||
button. You'll never know until you try.
|
button. You'll never know until you try.
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<RegistrationNotice
|
||||||
<button
|
v-else
|
||||||
:class="onboardingButtonClasses"
|
:passkeys-enabled="PASSKEYS_ENABLED"
|
||||||
@click="showNameThenIdDialog()"
|
:given-name="givenName"
|
||||||
>
|
message="To announce a project, get someone to onboard you."
|
||||||
Get someone to onboard you.
|
/>
|
||||||
</button>
|
|
||||||
<UserNameDialog ref="userNameDialog" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<ul id="listProjects" class="border-t border-slate-300">
|
<ul id="listProjects" class="border-t border-slate-300">
|
||||||
<li
|
<li
|
||||||
@@ -266,14 +263,14 @@ import { Component, Vue } from "vue-facing-decorator";
|
|||||||
import { Router } from "vue-router";
|
import { Router } from "vue-router";
|
||||||
// Capacitor import removed - using QRNavigationService instead
|
// Capacitor import removed - using QRNavigationService instead
|
||||||
|
|
||||||
import { NotificationIface } from "../constants/app";
|
import { NotificationIface, PASSKEYS_ENABLED } from "../constants/app";
|
||||||
import EntityIcon from "../components/EntityIcon.vue";
|
import EntityIcon from "../components/EntityIcon.vue";
|
||||||
import InfiniteScroll from "../components/InfiniteScroll.vue";
|
import InfiniteScroll from "../components/InfiniteScroll.vue";
|
||||||
import QuickNav from "../components/QuickNav.vue";
|
import QuickNav from "../components/QuickNav.vue";
|
||||||
import OnboardingDialog from "../components/OnboardingDialog.vue";
|
import OnboardingDialog from "../components/OnboardingDialog.vue";
|
||||||
import ProjectIcon from "../components/ProjectIcon.vue";
|
import ProjectIcon from "../components/ProjectIcon.vue";
|
||||||
import TopMessage from "../components/TopMessage.vue";
|
import TopMessage from "../components/TopMessage.vue";
|
||||||
import UserNameDialog from "../components/UserNameDialog.vue";
|
import RegistrationNotice from "../components/RegistrationNotice.vue";
|
||||||
import { Contact } from "../db/tables/contacts";
|
import { Contact } from "../db/tables/contacts";
|
||||||
import { didInfo, getHeaders, getPlanFromCache } from "../libs/endorserServer";
|
import { didInfo, getHeaders, getPlanFromCache } from "../libs/endorserServer";
|
||||||
import { OfferSummaryRecord, PlanData } from "../interfaces/records";
|
import { OfferSummaryRecord, PlanData } from "../interfaces/records";
|
||||||
@@ -281,14 +278,13 @@ import { OnboardPage, iconForUnitCode } from "../libs/util";
|
|||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
import { QRNavigationService } from "@/services/QRNavigationService";
|
|
||||||
import {
|
import {
|
||||||
NOTIFY_NO_ACCOUNT_ERROR,
|
NOTIFY_NO_ACCOUNT_ERROR,
|
||||||
NOTIFY_PROJECT_LOAD_ERROR,
|
NOTIFY_PROJECT_LOAD_ERROR,
|
||||||
NOTIFY_PROJECT_INIT_ERROR,
|
NOTIFY_PROJECT_INIT_ERROR,
|
||||||
NOTIFY_OFFERS_LOAD_ERROR,
|
NOTIFY_OFFERS_LOAD_ERROR,
|
||||||
NOTIFY_OFFERS_FETCH_ERROR,
|
NOTIFY_OFFERS_FETCH_ERROR,
|
||||||
NOTIFY_CAMERA_SHARE_METHOD,
|
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -318,7 +314,7 @@ import {
|
|||||||
OnboardingDialog,
|
OnboardingDialog,
|
||||||
ProjectIcon,
|
ProjectIcon,
|
||||||
TopMessage,
|
TopMessage,
|
||||||
UserNameDialog,
|
RegistrationNotice,
|
||||||
},
|
},
|
||||||
mixins: [PlatformServiceMixin],
|
mixins: [PlatformServiceMixin],
|
||||||
})
|
})
|
||||||
@@ -336,6 +332,7 @@ export default class ProjectsView extends Vue {
|
|||||||
givenName = "";
|
givenName = "";
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
isRegistered = false;
|
isRegistered = false;
|
||||||
|
readonly PASSKEYS_ENABLED: boolean = PASSKEYS_ENABLED;
|
||||||
|
|
||||||
// Data collections
|
// Data collections
|
||||||
offers: OfferSummaryRecord[] = [];
|
offers: OfferSummaryRecord[] = [];
|
||||||
@@ -624,39 +621,6 @@ export default class ProjectsView extends Vue {
|
|||||||
* Ensures user has provided their name before proceeding with contact sharing.
|
* Ensures user has provided their name before proceeding with contact sharing.
|
||||||
* Uses UserNameDialog component if name is not set.
|
* Uses UserNameDialog component if name is not set.
|
||||||
*/
|
*/
|
||||||
showNameThenIdDialog() {
|
|
||||||
if (!this.givenName) {
|
|
||||||
(this.$refs.userNameDialog as UserNameDialog).open(() => {
|
|
||||||
this.promptForShareMethod();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.promptForShareMethod();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prompts user to choose contact sharing method
|
|
||||||
*
|
|
||||||
* Presents modal dialog asking if users are nearby with cameras.
|
|
||||||
* Routes to appropriate sharing method based on user's choice:
|
|
||||||
* - QR code sharing for nearby users with cameras
|
|
||||||
* - Alternative sharing methods for remote users
|
|
||||||
*/
|
|
||||||
promptForShareMethod() {
|
|
||||||
this.$notify(
|
|
||||||
{
|
|
||||||
group: "modal",
|
|
||||||
type: "confirm",
|
|
||||||
title: NOTIFY_CAMERA_SHARE_METHOD.title,
|
|
||||||
text: NOTIFY_CAMERA_SHARE_METHOD.text,
|
|
||||||
onYes: () => this.handleQRCodeClick(),
|
|
||||||
onNo: () => this.$router.push({ name: "share-my-contact-info" }),
|
|
||||||
yesText: NOTIFY_CAMERA_SHARE_METHOD.yesText,
|
|
||||||
noText: NOTIFY_CAMERA_SHARE_METHOD.noText,
|
|
||||||
},
|
|
||||||
TIMEOUTS.MODAL,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computed properties for template logic streamlining
|
* Computed properties for template logic streamlining
|
||||||
@@ -722,14 +686,6 @@ export default class ProjectsView extends Vue {
|
|||||||
return "bg-green-600 text-white px-1.5 py-1 rounded-full";
|
return "bg-green-600 text-white px-1.5 py-1 rounded-full";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* CSS class names for onboarding button
|
|
||||||
* @returns String with CSS classes for the onboarding button
|
|
||||||
*/
|
|
||||||
get onboardingButtonClasses() {
|
|
||||||
return "text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS class names for project tab styling
|
* CSS class names for project tab styling
|
||||||
* @returns Object with CSS classes based on current tab selection
|
* @returns Object with CSS classes based on current tab selection
|
||||||
@@ -754,20 +710,6 @@ export default class ProjectsView extends Vue {
|
|||||||
* Utility methods
|
* Utility methods
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles QR code sharing functionality with platform detection
|
|
||||||
*
|
|
||||||
* Routes to appropriate QR code interface based on current platform:
|
|
||||||
* - Full QR scanner for native mobile platforms
|
|
||||||
* - Web-based QR interface for browser environments
|
|
||||||
*/
|
|
||||||
private handleQRCodeClick() {
|
|
||||||
const qrNavigationService = QRNavigationService.getInstance();
|
|
||||||
const route = qrNavigationService.getQRScannerRoute();
|
|
||||||
|
|
||||||
this.$router.push(route);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Legacy method compatibility
|
* Legacy method compatibility
|
||||||
* @deprecated Use computedOfferTabClassNames for backward compatibility
|
* @deprecated Use computedOfferTabClassNames for backward compatibility
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
* Key Selectors:
|
* Key Selectors:
|
||||||
* - Activity list: 'ul#listLatestActivity li'
|
* - Activity list: 'ul#listLatestActivity li'
|
||||||
* - Discover list: 'ul#listDiscoverResults li'
|
* - Discover list: 'ul#listDiscoverResults li'
|
||||||
* - Account notices: '#noticeBeforeShare', '#noticeBeforeAnnounce'
|
* - Account notices: '#noticeBeforeShare', '#noticeSomeoneMustRegisterYou'
|
||||||
* - Identity details: '#sectionIdentityDetails code.truncate'
|
* - Identity details: '#sectionIdentityDetails code.truncate'
|
||||||
*
|
*
|
||||||
* State Verification:
|
* State Verification:
|
||||||
@@ -99,7 +99,7 @@ test('Check no-ID messaging in account', async ({ page }) => {
|
|||||||
await page.goto('./account');
|
await page.goto('./account');
|
||||||
|
|
||||||
// Check 'a friend needs to register you' notice
|
// Check 'a friend needs to register you' notice
|
||||||
await expect(page.locator('#noticeBeforeAnnounce')).toBeVisible();
|
await expect(page.locator('#noticeSomeoneMustRegisterYou')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Check ability to share contact', async ({ page }) => {
|
test('Check ability to share contact', async ({ page }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user