Merge branch 'build-improvement' into performance-optimizations-testing
This commit is contained in:
@@ -251,8 +251,7 @@
|
|||||||
import { Component, Prop, Vue, Emit } from "vue-facing-decorator";
|
import { Component, Prop, Vue, Emit } from "vue-facing-decorator";
|
||||||
import { GiveRecordWithContactInfo } from "@/interfaces/give";
|
import { GiveRecordWithContactInfo } from "@/interfaces/give";
|
||||||
import EntityIcon from "./EntityIcon.vue";
|
import EntityIcon from "./EntityIcon.vue";
|
||||||
import { isGiveClaimType, notifyWhyCannotConfirm } from "../libs/util";
|
import { isHiddenDid } from "../libs/endorserServer";
|
||||||
import { containsHiddenDid, isHiddenDid } from "../libs/endorserServer";
|
|
||||||
import ProjectIcon from "./ProjectIcon.vue";
|
import ProjectIcon from "./ProjectIcon.vue";
|
||||||
import { createNotifyHelpers } from "@/utils/notify";
|
import { createNotifyHelpers } from "@/utils/notify";
|
||||||
import {
|
import {
|
||||||
@@ -272,7 +271,6 @@ export default class ActivityListItem extends Vue {
|
|||||||
@Prop() lastViewedClaimId?: string;
|
@Prop() lastViewedClaimId?: string;
|
||||||
@Prop() isRegistered!: boolean;
|
@Prop() isRegistered!: boolean;
|
||||||
@Prop() activeDid!: string;
|
@Prop() activeDid!: string;
|
||||||
@Prop() confirmerIdList?: string[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function prop for handling image caching
|
* Function prop for handling image caching
|
||||||
@@ -331,15 +329,6 @@ export default class ActivityListItem extends Vue {
|
|||||||
return unitCode === "HUR" ? (single ? "hour" : "hours") : unitCode;
|
return unitCode === "HUR" ? (single ? "hour" : "hours") : unitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
get canConfirm(): boolean {
|
|
||||||
if (!this.isRegistered) return false;
|
|
||||||
if (!isGiveClaimType(this.record.fullClaim?.["@type"])) return false;
|
|
||||||
if (this.confirmerIdList?.includes(this.activeDid)) return false;
|
|
||||||
if (this.record.issuerDid === this.activeDid) return false;
|
|
||||||
if (containsHiddenDid(this.record.fullClaim)) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit methods using @Emit decorator
|
// Emit methods using @Emit decorator
|
||||||
@Emit("viewImage")
|
@Emit("viewImage")
|
||||||
emitViewImage(imageUrl: string) {
|
emitViewImage(imageUrl: string) {
|
||||||
@@ -351,26 +340,6 @@ export default class ActivityListItem extends Vue {
|
|||||||
return jwtId;
|
return jwtId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Emit("confirmClaim")
|
|
||||||
emitConfirmClaim() {
|
|
||||||
if (!this.canConfirm) {
|
|
||||||
notifyWhyCannotConfirm(
|
|
||||||
(msg, timeout) => this.notify.info(msg.text ?? "", timeout),
|
|
||||||
this.isRegistered,
|
|
||||||
this.record.fullClaim?.["@type"],
|
|
||||||
this.record,
|
|
||||||
this.activeDid,
|
|
||||||
this.confirmerIdList,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return this.record;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleConfirmClick() {
|
|
||||||
this.emitConfirmClaim();
|
|
||||||
}
|
|
||||||
|
|
||||||
get friendlyDate(): string {
|
get friendlyDate(): string {
|
||||||
const date = new Date(this.record.issuedAt);
|
const date = new Date(this.record.issuedAt);
|
||||||
return date.toLocaleDateString(undefined, {
|
return date.toLocaleDateString(undefined, {
|
||||||
|
|||||||
@@ -261,12 +261,13 @@ export default class EntitySelectionStep extends Vue {
|
|||||||
giverProjectName: this.giver?.name || "",
|
giverProjectName: this.giver?.name || "",
|
||||||
giverProjectImage: this.giver?.image || "",
|
giverProjectImage: this.giver?.image || "",
|
||||||
giverProjectHandleId: this.giver?.handleId || "",
|
giverProjectHandleId: this.giver?.handleId || "",
|
||||||
giverDid: this.giver?.did || "",
|
giverDid: this.giverEntityType === "person" ? this.giver?.did || "" : "",
|
||||||
recipientProjectId: this.toProjectId || "",
|
recipientProjectId: this.toProjectId || "",
|
||||||
recipientProjectName: this.receiver?.name || "",
|
recipientProjectName: this.receiver?.name || "",
|
||||||
recipientProjectImage: this.receiver?.image || "",
|
recipientProjectImage: this.receiver?.image || "",
|
||||||
recipientProjectHandleId: this.receiver?.handleId || "",
|
recipientProjectHandleId: this.receiver?.handleId || "",
|
||||||
recipientDid: this.receiver?.did || "",
|
recipientDid:
|
||||||
|
this.recipientEntityType === "person" ? this.receiver?.did || "" : "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -315,16 +315,15 @@ export default class GiftDetailsStep extends Vue {
|
|||||||
giverName: this.giver?.name,
|
giverName: this.giver?.name,
|
||||||
offerId: this.offerId,
|
offerId: this.offerId,
|
||||||
fulfillsProjectId:
|
fulfillsProjectId:
|
||||||
this.giverEntityType === "person" &&
|
this.recipientEntityType === "project" ? this.toProjectId : undefined,
|
||||||
this.recipientEntityType === "project"
|
|
||||||
? this.toProjectId
|
|
||||||
: undefined,
|
|
||||||
providerProjectId:
|
providerProjectId:
|
||||||
this.giverEntityType === "project" &&
|
this.giverEntityType === "project"
|
||||||
this.recipientEntityType === "person"
|
|
||||||
? this.giver?.handleId
|
? this.giver?.handleId
|
||||||
: this.fromProjectId,
|
: this.fromProjectId,
|
||||||
recipientDid: this.receiver?.did,
|
recipientDid:
|
||||||
|
this.recipientEntityType === "person"
|
||||||
|
? this.receiver?.did
|
||||||
|
: undefined,
|
||||||
recipientName: this.receiver?.name,
|
recipientName: this.receiver?.name,
|
||||||
unitCode: this.localUnitCode,
|
unitCode: this.localUnitCode,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -81,6 +81,12 @@ import GiftDetailsStep from "../components/GiftDetailsStep.vue";
|
|||||||
import { PlanData } from "../interfaces/records";
|
import { PlanData } from "../interfaces/records";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
|
import {
|
||||||
|
NOTIFY_GIFT_ERROR_NEGATIVE_AMOUNT,
|
||||||
|
NOTIFY_GIFT_ERROR_NO_DESCRIPTION,
|
||||||
|
NOTIFY_GIFTED_DETAILS_NO_IDENTIFIER,
|
||||||
|
NOTIFY_GIFTED_DETAILS_RECORDING_GIVE,
|
||||||
|
} from "@/constants/notifications";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
@@ -288,23 +294,24 @@ export default class GiftedDialog extends Vue {
|
|||||||
async confirm() {
|
async confirm() {
|
||||||
if (!this.activeDid) {
|
if (!this.activeDid) {
|
||||||
this.safeNotify.error(
|
this.safeNotify.error(
|
||||||
"You must select an identifier before you can record a give.",
|
NOTIFY_GIFTED_DETAILS_NO_IDENTIFIER.message,
|
||||||
TIMEOUTS.STANDARD,
|
TIMEOUTS.SHORT,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (parseFloat(this.amountInput) < 0) {
|
if (parseFloat(this.amountInput) < 0) {
|
||||||
this.safeNotify.error(
|
this.safeNotify.error(
|
||||||
"You may not send a negative number.",
|
NOTIFY_GIFT_ERROR_NEGATIVE_AMOUNT.message,
|
||||||
TIMEOUTS.SHORT,
|
TIMEOUTS.SHORT,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.description && !parseFloat(this.amountInput)) {
|
if (!this.description && !parseFloat(this.amountInput)) {
|
||||||
this.safeNotify.error(
|
this.safeNotify.error(
|
||||||
`You must enter a description or some number of ${
|
NOTIFY_GIFT_ERROR_NO_DESCRIPTION.message.replace(
|
||||||
this.libsUtil.UNIT_LONG[this.unitCode]
|
"{unit}",
|
||||||
}.`,
|
this.libsUtil.UNIT_SHORT[this.unitCode] || this.unitCode,
|
||||||
|
),
|
||||||
TIMEOUTS.SHORT,
|
TIMEOUTS.SHORT,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@@ -320,7 +327,11 @@ export default class GiftedDialog extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.close();
|
this.close();
|
||||||
this.safeNotify.toast("Recording the give...", undefined, TIMEOUTS.BRIEF);
|
this.safeNotify.toast(
|
||||||
|
NOTIFY_GIFTED_DETAILS_RECORDING_GIVE.message,
|
||||||
|
undefined,
|
||||||
|
TIMEOUTS.BRIEF,
|
||||||
|
);
|
||||||
// this is asynchronous, but we don't need to wait for it to complete
|
// this is asynchronous, but we don't need to wait for it to complete
|
||||||
await this.recordGive(
|
await this.recordGive(
|
||||||
(this.giver?.did as string) || null,
|
(this.giver?.did as string) || null,
|
||||||
@@ -447,10 +458,13 @@ export default class GiftedDialog extends Vue {
|
|||||||
name: contact.name || contact.did,
|
name: contact.name || contact.did,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
this.giver = {
|
// Only set to "Unnamed" if no giver is currently set
|
||||||
did: "",
|
if (!this.giver || !this.giver.did) {
|
||||||
name: "Unnamed",
|
this.giver = {
|
||||||
};
|
did: "",
|
||||||
|
name: "Unnamed",
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.firstStep = false;
|
this.firstStep = false;
|
||||||
}
|
}
|
||||||
@@ -460,6 +474,10 @@ export default class GiftedDialog extends Vue {
|
|||||||
this.firstStep = true;
|
this.firstStep = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
moveToStep2() {
|
||||||
|
this.firstStep = false;
|
||||||
|
}
|
||||||
|
|
||||||
async loadProjects() {
|
async loadProjects() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(this.apiServer + "/api/v2/report/plans", {
|
const response = await fetch(this.apiServer + "/api/v2/report/plans", {
|
||||||
@@ -502,10 +520,13 @@ export default class GiftedDialog extends Vue {
|
|||||||
name: contact.name || contact.did,
|
name: contact.name || contact.did,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
this.receiver = {
|
// Only set to "Unnamed" if no receiver is currently set
|
||||||
did: "",
|
if (!this.receiver || !this.receiver.did) {
|
||||||
name: "Unnamed",
|
this.receiver = {
|
||||||
};
|
did: "",
|
||||||
|
name: "Unnamed",
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.firstStep = false;
|
this.firstStep = false;
|
||||||
}
|
}
|
||||||
@@ -529,16 +550,13 @@ export default class GiftedDialog extends Vue {
|
|||||||
giverName: this.giver?.name,
|
giverName: this.giver?.name,
|
||||||
offerId: this.offerId,
|
offerId: this.offerId,
|
||||||
fulfillsProjectId:
|
fulfillsProjectId:
|
||||||
this.giverEntityType === "person" &&
|
this.recipientEntityType === "project" ? this.toProjectId : undefined,
|
||||||
this.recipientEntityType === "project"
|
|
||||||
? this.toProjectId
|
|
||||||
: undefined,
|
|
||||||
providerProjectId:
|
providerProjectId:
|
||||||
this.giverEntityType === "project" &&
|
this.giverEntityType === "project"
|
||||||
this.recipientEntityType === "person"
|
|
||||||
? this.giver?.handleId
|
? this.giver?.handleId
|
||||||
: this.fromProjectId,
|
: this.fromProjectId,
|
||||||
recipientDid: this.receiver?.did,
|
recipientDid:
|
||||||
|
this.recipientEntityType === "person" ? this.receiver?.did : undefined,
|
||||||
recipientName: this.receiver?.name,
|
recipientName: this.receiver?.name,
|
||||||
unitCode: this.unitCode,
|
unitCode: this.unitCode,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ export default class ContactGiftingView extends Vue {
|
|||||||
let giver: GiverReceiverInputInfo | undefined;
|
let giver: GiverReceiverInputInfo | undefined;
|
||||||
|
|
||||||
if (this.stepType === "giver") {
|
if (this.stepType === "giver") {
|
||||||
// We're selecting a giver, so recipient is either a project or the current user
|
// We're selecting a giver, so preserve the existing recipient from context
|
||||||
if (this.recipientEntityType === "project") {
|
if (this.recipientEntityType === "project") {
|
||||||
recipient = {
|
recipient = {
|
||||||
did: this.recipientProjectHandleId,
|
did: this.recipientProjectHandleId,
|
||||||
@@ -204,7 +204,20 @@ export default class ContactGiftingView extends Vue {
|
|||||||
handleId: this.recipientProjectHandleId,
|
handleId: this.recipientProjectHandleId,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
recipient = { did: this.activeDid, name: "You" };
|
// Preserve the existing recipient from context
|
||||||
|
if (this.recipientDid === this.activeDid) {
|
||||||
|
// Recipient was "You"
|
||||||
|
recipient = { did: this.activeDid, name: "You" };
|
||||||
|
} else if (this.recipientDid) {
|
||||||
|
// Recipient was a regular contact
|
||||||
|
recipient = {
|
||||||
|
did: this.recipientDid,
|
||||||
|
name: this.recipientProjectName || "Someone",
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Fallback to "You" if no recipient was previously selected
|
||||||
|
recipient = { did: this.activeDid, name: "You" };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
giver = undefined; // Will be set to "Unnamed" in GiftedDialog
|
giver = undefined; // Will be set to "Unnamed" in GiftedDialog
|
||||||
} else {
|
} else {
|
||||||
@@ -239,12 +252,8 @@ export default class ContactGiftingView extends Vue {
|
|||||||
this.unitCode,
|
this.unitCode,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Immediately select "Unnamed" and move to Step 2 based on stepType
|
// Move to Step 2 - entities are already set by the open() call
|
||||||
if (this.stepType === "giver") {
|
(this.$refs.giftedDialog as GiftedDialog).moveToStep2();
|
||||||
(this.$refs.giftedDialog as GiftedDialog).selectGiver();
|
|
||||||
} else {
|
|
||||||
(this.$refs.giftedDialog as GiftedDialog).selectRecipient();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Regular case: contact is a GiverReceiverInputInfo
|
// Regular case: contact is a GiverReceiverInputInfo
|
||||||
let giver: GiverReceiverInputInfo;
|
let giver: GiverReceiverInputInfo;
|
||||||
@@ -317,6 +326,9 @@ export default class ContactGiftingView extends Vue {
|
|||||||
this.amountInput,
|
this.amountInput,
|
||||||
this.unitCode,
|
this.unitCode,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Move to Step 2 - entities are already set by the open() call
|
||||||
|
(this.$refs.giftedDialog as GiftedDialog).moveToStep2();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,15 +213,11 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
$router!: Router;
|
$router!: Router;
|
||||||
|
|
||||||
// Notification helper system
|
// Notification helper system
|
||||||
private notify = createNotifyHelpers(this.$notify);
|
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||||
|
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
apiServer = "";
|
apiServer = "";
|
||||||
|
|
||||||
// Axios instance for API calls
|
|
||||||
get axios() {
|
|
||||||
return (this as any).$platformService.axios;
|
|
||||||
}
|
|
||||||
givenName = "";
|
givenName = "";
|
||||||
hideRegisterPromptOnNewContact = false;
|
hideRegisterPromptOnNewContact = false;
|
||||||
isRegistered = false;
|
isRegistered = false;
|
||||||
@@ -288,6 +284,8 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
|
this.notify = createNotifyHelpers(this.$notify);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const settings = await this.$accountSettings();
|
const settings = await this.$accountSettings();
|
||||||
this.activeDid = settings.activeDid || "";
|
this.activeDid = settings.activeDid || "";
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ import {
|
|||||||
displayAmount,
|
displayAmount,
|
||||||
getHeaders,
|
getHeaders,
|
||||||
register,
|
register,
|
||||||
|
setVisibilityUtil,
|
||||||
} from "../libs/endorserServer";
|
} from "../libs/endorserServer";
|
||||||
import * as libsUtil from "../libs/util";
|
import * as libsUtil from "../libs/util";
|
||||||
import EntityIcon from "../components/EntityIcon.vue";
|
import EntityIcon from "../components/EntityIcon.vue";
|
||||||
@@ -324,6 +325,7 @@ export default class DIDView extends Vue {
|
|||||||
apiServer = "";
|
apiServer = "";
|
||||||
claims: Array<GenericCredWrapper<GenericVerifiableCredential>> = [];
|
claims: Array<GenericCredWrapper<GenericVerifiableCredential>> = [];
|
||||||
contactFromDid?: Contact;
|
contactFromDid?: Contact;
|
||||||
|
|
||||||
contactYaml = "";
|
contactYaml = "";
|
||||||
hitEnd = false;
|
hitEnd = false;
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
@@ -722,18 +724,31 @@ export default class DIDView extends Vue {
|
|||||||
visibility: boolean,
|
visibility: boolean,
|
||||||
showSuccessAlert: boolean,
|
showSuccessAlert: boolean,
|
||||||
) {
|
) {
|
||||||
// Update contact visibility using mixin method
|
const result = await setVisibilityUtil(
|
||||||
await this.$updateContact(contact.did, { seesMe: visibility });
|
this.activeDid,
|
||||||
|
this.apiServer,
|
||||||
|
this.axios,
|
||||||
|
contact,
|
||||||
|
visibility,
|
||||||
|
);
|
||||||
|
|
||||||
if (showSuccessAlert) {
|
if (result.success) {
|
||||||
|
if (showSuccessAlert) {
|
||||||
|
const message =
|
||||||
|
(contact.name || "That user") +
|
||||||
|
" can " +
|
||||||
|
(visibility ? "" : "not ") +
|
||||||
|
"see your activity.";
|
||||||
|
this.notify.success(message, TIMEOUTS.SHORT);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.error("Got strange result from setting visibility:", result);
|
||||||
const message =
|
const message =
|
||||||
(contact.name || "That user") +
|
(result.error as string) || "Could not set visibility on the server.";
|
||||||
" can " +
|
this.notify.error(message, TIMEOUTS.LONG);
|
||||||
(visibility ? "" : "not ") +
|
return false;
|
||||||
"see your activity.";
|
|
||||||
this.notify.success(message, TIMEOUTS.SHORT);
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -48,24 +48,12 @@
|
|||||||
placeholder="What was received"
|
placeholder="What was received"
|
||||||
/>
|
/>
|
||||||
<div class="flex mb-4">
|
<div class="flex mb-4">
|
||||||
<button
|
<AmountInput
|
||||||
class="rounded-s border border-e-0 border-slate-400 bg-slate-200 px-4 py-2"
|
:value="parseFloat(amountInput) || 0"
|
||||||
@click="amountInput === '0' ? null : decrement()"
|
:min="0"
|
||||||
>
|
input-id="inputGivenAmount"
|
||||||
<font-awesome icon="chevron-left" />
|
:on-update-value="handleAmountChange"
|
||||||
</button>
|
|
||||||
<input
|
|
||||||
id="inputGivenAmount"
|
|
||||||
v-model="amountInput"
|
|
||||||
type="number"
|
|
||||||
class="flex-1 border border-e-0 border-slate-400 px-2 py-2 text-center w-[1px]"
|
|
||||||
/>
|
/>
|
||||||
<button
|
|
||||||
class="rounded-e border border-slate-400 bg-slate-200 px-4 py-2"
|
|
||||||
@click="increment()"
|
|
||||||
>
|
|
||||||
<font-awesome icon="chevron-right" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<select
|
<select
|
||||||
v-model="unitCode"
|
v-model="unitCode"
|
||||||
@@ -275,6 +263,7 @@ import { RouteLocationNormalizedLoaded, Router } from "vue-router";
|
|||||||
import ImageMethodDialog from "../components/ImageMethodDialog.vue";
|
import ImageMethodDialog from "../components/ImageMethodDialog.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 AmountInput from "../components/AmountInput.vue";
|
||||||
import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "../constants/app";
|
import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "../constants/app";
|
||||||
import { GenericCredWrapper, GiveActionClaim } from "../interfaces";
|
import { GenericCredWrapper, GiveActionClaim } from "../interfaces";
|
||||||
import {
|
import {
|
||||||
@@ -296,9 +285,11 @@ import {
|
|||||||
NOTIFY_GIFTED_DETAILS_DELETE_IMAGE_CONFIRM,
|
NOTIFY_GIFTED_DETAILS_DELETE_IMAGE_CONFIRM,
|
||||||
NOTIFY_GIFTED_DETAILS_DELETE_IMAGE_ERROR,
|
NOTIFY_GIFTED_DETAILS_DELETE_IMAGE_ERROR,
|
||||||
NOTIFY_GIFTED_DETAILS_NO_IDENTIFIER,
|
NOTIFY_GIFTED_DETAILS_NO_IDENTIFIER,
|
||||||
|
NOTIFY_GIFT_ERROR_NEGATIVE_AMOUNT,
|
||||||
NOTIFY_GIFTED_DETAILS_RECORDING_GIVE,
|
NOTIFY_GIFTED_DETAILS_RECORDING_GIVE,
|
||||||
NOTIFY_GIFTED_DETAILS_CREATE_GIVE_ERROR,
|
NOTIFY_GIFTED_DETAILS_CREATE_GIVE_ERROR,
|
||||||
NOTIFY_GIFTED_DETAILS_GIFT_RECORDED,
|
NOTIFY_GIFTED_DETAILS_GIFT_RECORDED,
|
||||||
|
NOTIFY_GIFT_ERROR_NO_DESCRIPTION,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -306,6 +297,7 @@ import {
|
|||||||
ImageMethodDialog,
|
ImageMethodDialog,
|
||||||
QuickNav,
|
QuickNav,
|
||||||
TopMessage,
|
TopMessage,
|
||||||
|
AmountInput,
|
||||||
},
|
},
|
||||||
mixins: [PlatformServiceMixin],
|
mixins: [PlatformServiceMixin],
|
||||||
})
|
})
|
||||||
@@ -528,6 +520,10 @@ export default class GiftedDetails extends Vue {
|
|||||||
)}`;
|
)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleAmountChange(value: number): void {
|
||||||
|
this.amountInput = value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
this.deleteImage(); // not awaiting, so they'll go back immediately
|
this.deleteImage(); // not awaiting, so they'll go back immediately
|
||||||
if (this.destinationPathAfter) {
|
if (this.destinationPathAfter) {
|
||||||
@@ -609,14 +605,17 @@ export default class GiftedDetails extends Vue {
|
|||||||
}
|
}
|
||||||
if (parseFloat(this.amountInput) < 0) {
|
if (parseFloat(this.amountInput) < 0) {
|
||||||
this.notify.error(
|
this.notify.error(
|
||||||
NOTIFY_GIFTED_DETAILS_NO_IDENTIFIER.message,
|
NOTIFY_GIFT_ERROR_NEGATIVE_AMOUNT.message,
|
||||||
TIMEOUTS.SHORT,
|
TIMEOUTS.SHORT,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.description && !parseFloat(this.amountInput)) {
|
if (!this.description && !parseFloat(this.amountInput)) {
|
||||||
this.notify.error(
|
this.notify.error(
|
||||||
NOTIFY_GIFTED_DETAILS_NO_IDENTIFIER.message,
|
NOTIFY_GIFT_ERROR_NO_DESCRIPTION.message.replace(
|
||||||
|
"{unit}",
|
||||||
|
this.libsUtil.UNIT_SHORT[this.unitCode] || this.unitCode,
|
||||||
|
),
|
||||||
TIMEOUTS.SHORT,
|
TIMEOUTS.SHORT,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@@ -625,7 +624,7 @@ export default class GiftedDetails extends Vue {
|
|||||||
this.notify.toast(
|
this.notify.toast(
|
||||||
NOTIFY_GIFTED_DETAILS_RECORDING_GIVE.message,
|
NOTIFY_GIFTED_DETAILS_RECORDING_GIVE.message,
|
||||||
undefined,
|
undefined,
|
||||||
TIMEOUTS.SHORT,
|
TIMEOUTS.BRIEF,
|
||||||
);
|
);
|
||||||
|
|
||||||
// this is asynchronous, but we don't need to wait for it to complete
|
// this is asynchronous, but we don't need to wait for it to complete
|
||||||
|
|||||||
@@ -249,11 +249,9 @@ Raymer * @version 1.0.0 */
|
|||||||
:last-viewed-claim-id="feedLastViewedClaimId"
|
:last-viewed-claim-id="feedLastViewedClaimId"
|
||||||
:is-registered="isUserRegistered"
|
:is-registered="isUserRegistered"
|
||||||
:active-did="activeDid"
|
:active-did="activeDid"
|
||||||
:confirmer-id-list="record.confirmerIdList"
|
|
||||||
:on-image-cache="cacheImageData"
|
:on-image-cache="cacheImageData"
|
||||||
@load-claim="onClickLoadClaim"
|
@load-claim="onClickLoadClaim"
|
||||||
@view-image="openImageViewer"
|
@view-image="openImageViewer"
|
||||||
@confirm-claim="confirmClaim"
|
|
||||||
/>
|
/>
|
||||||
</ul>
|
</ul>
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
@@ -327,16 +325,12 @@ import {
|
|||||||
GiverReceiverInputInfo,
|
GiverReceiverInputInfo,
|
||||||
OnboardPage,
|
OnboardPage,
|
||||||
} from "../libs/util";
|
} from "../libs/util";
|
||||||
import { GiveSummaryRecord, PlanSummaryRecord } from "../interfaces/records";
|
import { GiveSummaryRecord } from "../interfaces/records";
|
||||||
import * as serverUtil from "../libs/endorserServer";
|
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { GiveRecordWithContactInfo } from "../interfaces/give";
|
import { GiveRecordWithContactInfo } from "../interfaces/give";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
import {
|
import { NOTIFY_CONTACT_LOADING_ISSUE } from "@/constants/notifications";
|
||||||
NOTIFY_CONTACT_LOADING_ISSUE,
|
|
||||||
NOTIFY_CONFIRMATION_ERROR,
|
|
||||||
} from "@/constants/notifications";
|
|
||||||
import * as Package from "../../package.json";
|
import * as Package from "../../package.json";
|
||||||
|
|
||||||
// consolidate this with GiveActionClaim in src/interfaces/claims.ts
|
// consolidate this with GiveActionClaim in src/interfaces/claims.ts
|
||||||
@@ -664,42 +658,6 @@ export default class HomeView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads user settings from database using ultra-concise mixin
|
|
||||||
* Used for displaying settings in feed and actions
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
* Called by mounted() and reloadFeedOnChange()
|
|
||||||
*/
|
|
||||||
private async loadSettings() {
|
|
||||||
// Use the current activeDid (set in initializeIdentity) to get user-specific settings
|
|
||||||
const settings = await this.$accountSettings(this.activeDid, {
|
|
||||||
apiServer: "",
|
|
||||||
activeDid: "",
|
|
||||||
filterFeedByVisible: false,
|
|
||||||
filterFeedByNearby: false,
|
|
||||||
isRegistered: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.apiServer = settings.apiServer || "";
|
|
||||||
|
|
||||||
// **CRITICAL**: Ensure correct API server for platform
|
|
||||||
await this.ensureCorrectApiServer();
|
|
||||||
|
|
||||||
this.activeDid = settings.activeDid || "";
|
|
||||||
this.feedLastViewedClaimId = settings.lastViewedClaimId;
|
|
||||||
this.givenName = settings.firstName || "";
|
|
||||||
this.isFeedFilteredByVisible = !!settings.filterFeedByVisible;
|
|
||||||
this.isFeedFilteredByNearby = !!settings.filterFeedByNearby;
|
|
||||||
this.isRegistered = !!settings.isRegistered;
|
|
||||||
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId;
|
|
||||||
this.lastAckedOfferToUserProjectsJwtId =
|
|
||||||
settings.lastAckedOfferToUserProjectsJwtId;
|
|
||||||
this.searchBoxes = settings.searchBoxes || [];
|
|
||||||
this.showShortcutBvc = !!settings.showShortcutBvc;
|
|
||||||
this.isAnyFeedFilterOn = checkIsAnyFeedFilterOn(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads user contacts from database using ultra-concise mixin
|
* Loads user contacts from database using ultra-concise mixin
|
||||||
* Used for displaying contact info in feed and actions
|
* Used for displaying contact info in feed and actions
|
||||||
@@ -714,36 +672,6 @@ export default class HomeView extends Vue {
|
|||||||
.map((c) => c.did);
|
.map((c) => c.did);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies user registration status with endorser service
|
|
||||||
* - Checks if unregistered user can access API
|
|
||||||
* - Updates registration status if successful
|
|
||||||
* - Preserves unregistered state on failure
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
* Called by mounted() and initializeIdentity()
|
|
||||||
*/
|
|
||||||
private async checkRegistrationStatus() {
|
|
||||||
if (!this.isRegistered && this.activeDid) {
|
|
||||||
try {
|
|
||||||
const resp = await fetchEndorserRateLimits(
|
|
||||||
this.apiServer,
|
|
||||||
this.axios,
|
|
||||||
this.activeDid,
|
|
||||||
);
|
|
||||||
if (resp.status === 200) {
|
|
||||||
// Ultra-concise settings update with automatic cache invalidation!
|
|
||||||
await this.$saveMySettings({ isRegistered: true });
|
|
||||||
this.isRegistered = true;
|
|
||||||
// Force Vue to re-render the template
|
|
||||||
await this.$nextTick();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// ignore the error... just keep us unregistered
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes feed data
|
* Initializes feed data
|
||||||
* Triggers updateAllFeed() to populate activity feed
|
* Triggers updateAllFeed() to populate activity feed
|
||||||
@@ -2074,53 +2002,6 @@ export default class HomeView extends Vue {
|
|||||||
this.isImageViewerOpen = true;
|
this.isImageViewerOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles claim confirmation
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* Called by ActivityListItem component
|
|
||||||
* @param record Record to confirm
|
|
||||||
*/
|
|
||||||
async confirmClaim(record: GiveRecordWithContactInfo) {
|
|
||||||
this.notify.confirm(
|
|
||||||
"Do you personally confirm that this is true?",
|
|
||||||
async () => {
|
|
||||||
const goodClaim = serverUtil.removeSchemaContext(
|
|
||||||
serverUtil.removeVisibleToDids(
|
|
||||||
serverUtil.addLastClaimOrHandleAsIdIfMissing(
|
|
||||||
record.fullClaim,
|
|
||||||
record.jwtId,
|
|
||||||
record.handleId,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
const confirmationClaim = {
|
|
||||||
"@context": "https://schema.org",
|
|
||||||
"@type": "AgreeAction",
|
|
||||||
object: goodClaim,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await serverUtil.createAndSubmitClaim(
|
|
||||||
confirmationClaim,
|
|
||||||
this.activeDid,
|
|
||||||
this.apiServer,
|
|
||||||
this.axios,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
this.notify.confirmationSubmitted();
|
|
||||||
|
|
||||||
// Refresh the feed to show updated confirmation status
|
|
||||||
await this.updateAllFeed();
|
|
||||||
} else {
|
|
||||||
logger.error("Error submitting confirmation:", result);
|
|
||||||
this.notify.error(NOTIFY_CONFIRMATION_ERROR.message, TIMEOUTS.LONG);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleQRCodeClick() {
|
private handleQRCodeClick() {
|
||||||
if (Capacitor.isNativePlatform()) {
|
if (Capacitor.isNativePlatform()) {
|
||||||
this.$router.push({ name: "contact-qr-scan-full" });
|
this.$router.push({ name: "contact-qr-scan-full" });
|
||||||
|
|||||||
@@ -115,6 +115,9 @@ import {
|
|||||||
serverMessageForUser,
|
serverMessageForUser,
|
||||||
} from "../libs/endorserServer";
|
} from "../libs/endorserServer";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
|
import { logger } from "@/utils/logger";
|
||||||
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
|
import { NotificationIface } from "@/constants/app";
|
||||||
|
|
||||||
interface Meeting {
|
interface Meeting {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -129,19 +132,11 @@ interface Meeting {
|
|||||||
mixins: [PlatformServiceMixin],
|
mixins: [PlatformServiceMixin],
|
||||||
})
|
})
|
||||||
export default class OnboardMeetingListView extends Vue {
|
export default class OnboardMeetingListView extends Vue {
|
||||||
$notify!: (
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||||
notification: {
|
|
||||||
group: string;
|
|
||||||
type: string;
|
|
||||||
title: string;
|
|
||||||
text: string;
|
|
||||||
onYes?: () => void;
|
|
||||||
yesText?: string;
|
|
||||||
},
|
|
||||||
timeout?: number,
|
|
||||||
) => void;
|
|
||||||
$router!: Router;
|
$router!: Router;
|
||||||
|
|
||||||
|
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||||
|
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
apiServer = "";
|
apiServer = "";
|
||||||
attendingMeeting: Meeting | null = null;
|
attendingMeeting: Meeting | null = null;
|
||||||
@@ -153,30 +148,66 @@ export default class OnboardMeetingListView extends Vue {
|
|||||||
selectedMeeting: Meeting | null = null;
|
selectedMeeting: Meeting | null = null;
|
||||||
showPasswordDialog = false;
|
showPasswordDialog = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vue lifecycle hook - component initialization
|
||||||
|
*
|
||||||
|
* Initializes the component by loading user settings and fetching available
|
||||||
|
* onboarding meetings. This method is called when the component is created
|
||||||
|
* and sets up all necessary data for the meeting list interface.
|
||||||
|
*
|
||||||
|
* Workflow:
|
||||||
|
* 1. Initialize notification system using createNotifyHelpers
|
||||||
|
* 2. Load user account settings (DID, API server, registration status)
|
||||||
|
* 3. Fetch available onboarding meetings from the server
|
||||||
|
*
|
||||||
|
* Dependencies:
|
||||||
|
* - PlatformServiceMixin for settings access ($accountSettings)
|
||||||
|
* - Server API for meeting data (fetchMeetings)
|
||||||
|
*
|
||||||
|
* Error Handling:
|
||||||
|
* - Server errors during meeting fetch are handled in fetchMeetings()
|
||||||
|
*
|
||||||
|
* @author Matthew Raymer
|
||||||
|
*/
|
||||||
async created() {
|
async created() {
|
||||||
const settings = await this.$accountSettings();
|
this.notify = createNotifyHelpers(this.$notify);
|
||||||
|
|
||||||
if (settings?.activeDid) {
|
// Load user account settings
|
||||||
try {
|
const settings = await this.$accountSettings();
|
||||||
// Verify database settings are accessible
|
|
||||||
await this.$query("SELECT * FROM settings WHERE accountDid = ?", [
|
|
||||||
settings.activeDid,
|
|
||||||
]);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error("Error checking database settings:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.activeDid = settings?.activeDid || "";
|
this.activeDid = settings?.activeDid || "";
|
||||||
this.apiServer = settings?.apiServer || "";
|
this.apiServer = settings?.apiServer || "";
|
||||||
this.firstName = settings?.firstName || "";
|
this.firstName = settings?.firstName || "";
|
||||||
this.isRegistered = !!settings?.isRegistered;
|
this.isRegistered = !!settings?.isRegistered;
|
||||||
|
|
||||||
if (this.isRegistered) {
|
await this.fetchMeetings();
|
||||||
await this.fetchMeetings();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches available onboarding meetings from the server
|
||||||
|
*
|
||||||
|
* This method retrieves the list of onboarding meetings that the user can join.
|
||||||
|
* It first checks if the user is already attending a meeting, and if so,
|
||||||
|
* displays that meeting instead of the full list.
|
||||||
|
*
|
||||||
|
* Workflow:
|
||||||
|
* 1. Check if user is already attending a meeting (groupOnboardMember endpoint)
|
||||||
|
* 2. If attending: Fetch meeting details and display single meeting view
|
||||||
|
* 3. If not attending: Fetch all available meetings (groupsOnboarding endpoint)
|
||||||
|
* 4. Handle loading states and error conditions
|
||||||
|
*
|
||||||
|
* API Endpoints Used:
|
||||||
|
* - GET /api/partner/groupOnboardMember - Check current attendance
|
||||||
|
* - GET /api/partner/groupOnboard/{id} - Get meeting details
|
||||||
|
* - GET /api/partner/groupsOnboarding - Get all available meetings
|
||||||
|
*
|
||||||
|
* State Management:
|
||||||
|
* - Sets isLoading flag during API calls
|
||||||
|
* - Updates attendingMeeting or meetings array
|
||||||
|
* - Handles error states with user notifications
|
||||||
|
*
|
||||||
|
* @author Matthew Raymer
|
||||||
|
*/
|
||||||
async fetchMeetings() {
|
async fetchMeetings() {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
try {
|
try {
|
||||||
@@ -226,20 +257,36 @@ export default class OnboardMeetingListView extends Vue {
|
|||||||
"Error fetching meetings: " + errorStringForLog(error),
|
"Error fetching meetings: " + errorStringForLog(error),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
this.$notify(
|
this.notify.error(
|
||||||
{
|
serverMessageForUser(error) || "Failed to fetch meetings.",
|
||||||
group: "alert",
|
TIMEOUTS.LONG,
|
||||||
type: "danger",
|
|
||||||
title: "Error",
|
|
||||||
text: serverMessageForUser(error) || "Failed to fetch meetings.",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the password dialog for joining a meeting
|
||||||
|
*
|
||||||
|
* This method initiates the process of joining an onboarding meeting by
|
||||||
|
* opening a modal dialog that prompts the user for the meeting password.
|
||||||
|
* The dialog is focused and ready for input when displayed.
|
||||||
|
*
|
||||||
|
* Workflow:
|
||||||
|
* 1. Clear any previous password input
|
||||||
|
* 2. Store the selected meeting for later use
|
||||||
|
* 3. Show the password dialog modal
|
||||||
|
* 4. Focus the password input field for immediate typing
|
||||||
|
*
|
||||||
|
* UI State Changes:
|
||||||
|
* - Sets showPasswordDialog to true (shows modal)
|
||||||
|
* - Clears password field for fresh input
|
||||||
|
* - Stores selectedMeeting for password submission
|
||||||
|
*
|
||||||
|
* @param meeting - The meeting object the user wants to join
|
||||||
|
* @author Matthew Raymer
|
||||||
|
*/
|
||||||
promptPassword(meeting: Meeting) {
|
promptPassword(meeting: Meeting) {
|
||||||
this.password = "";
|
this.password = "";
|
||||||
this.selectedMeeting = meeting;
|
this.selectedMeeting = meeting;
|
||||||
@@ -252,12 +299,61 @@ export default class OnboardMeetingListView extends Vue {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the password dialog and resets state
|
||||||
|
*
|
||||||
|
* This method handles the cancellation of the meeting password dialog.
|
||||||
|
* It cleans up the dialog state and resets all related variables to
|
||||||
|
* their initial state, ensuring a clean slate for future dialog interactions.
|
||||||
|
*
|
||||||
|
* State Cleanup:
|
||||||
|
* - Clears password input field
|
||||||
|
* - Removes selected meeting reference
|
||||||
|
* - Hides password dialog modal
|
||||||
|
*
|
||||||
|
* This ensures that if the user reopens the dialog, they start with
|
||||||
|
* a fresh state without any leftover data from previous attempts.
|
||||||
|
*
|
||||||
|
* @author Matthew Raymer
|
||||||
|
*/
|
||||||
cancelPasswordDialog() {
|
cancelPasswordDialog() {
|
||||||
this.password = "";
|
this.password = "";
|
||||||
this.selectedMeeting = null;
|
this.selectedMeeting = null;
|
||||||
this.showPasswordDialog = false;
|
this.showPasswordDialog = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits the password and joins the selected meeting
|
||||||
|
*
|
||||||
|
* This method handles the complete workflow of joining an onboarding meeting.
|
||||||
|
* It encrypts the user's member data with the provided password and sends
|
||||||
|
* it to the server to register the user as a meeting participant.
|
||||||
|
*
|
||||||
|
* Workflow:
|
||||||
|
* 1. Validate that a meeting is selected (safety check)
|
||||||
|
* 2. Create member data object with user information
|
||||||
|
* 3. Encrypt member data using the meeting password
|
||||||
|
* 4. Send encrypted data to server via groupOnboardMember endpoint
|
||||||
|
* 5. On success: Navigate to meeting members view with credentials
|
||||||
|
* 6. On failure: Show error notification to user
|
||||||
|
*
|
||||||
|
* Data Encryption:
|
||||||
|
* - Member data includes: name, DID, registration status
|
||||||
|
* - Data is encrypted using the meeting password for security
|
||||||
|
* - Encrypted data is sent to server for verification
|
||||||
|
*
|
||||||
|
* Navigation:
|
||||||
|
* - On successful join: Redirects to onboard-meeting-members view
|
||||||
|
* - Passes groupId, password, and memberId as route parameters
|
||||||
|
* - Allows user to see other meeting participants
|
||||||
|
*
|
||||||
|
* Error Handling:
|
||||||
|
* - Invalid passwords result in server rejection
|
||||||
|
* - Network errors are caught and displayed to user
|
||||||
|
* - All errors are logged for debugging purposes
|
||||||
|
*
|
||||||
|
* @author Matthew Raymer
|
||||||
|
*/
|
||||||
async submitPassword() {
|
async submitPassword() {
|
||||||
if (!this.selectedMeeting) {
|
if (!this.selectedMeeting) {
|
||||||
// this should never happen
|
// this should never happen
|
||||||
@@ -316,69 +412,93 @@ export default class OnboardMeetingListView extends Vue {
|
|||||||
"Error joining meeting: " + errorStringForLog(error),
|
"Error joining meeting: " + errorStringForLog(error),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
this.$notify(
|
this.notify.error(
|
||||||
{
|
serverMessageForUser(error) || "You failed to join the meeting.",
|
||||||
group: "alert",
|
TIMEOUTS.LONG,
|
||||||
type: "danger",
|
|
||||||
title: "Error",
|
|
||||||
text:
|
|
||||||
serverMessageForUser(error) || "You failed to join the meeting.",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompts user to confirm leaving the current meeting
|
||||||
|
*
|
||||||
|
* This method initiates the process of leaving an onboarding meeting.
|
||||||
|
* It shows a confirmation dialog to prevent accidental departures,
|
||||||
|
* then handles the server-side removal and UI updates.
|
||||||
|
*
|
||||||
|
* Workflow:
|
||||||
|
* 1. Display confirmation dialog asking user to confirm departure
|
||||||
|
* 2. On confirmation: Send DELETE request to groupOnboardMember endpoint
|
||||||
|
* 3. On success: Clear attending meeting state and refresh meeting list
|
||||||
|
* 4. Show success notification to user
|
||||||
|
* 5. On failure: Show error notification with details
|
||||||
|
*
|
||||||
|
* Server Interaction:
|
||||||
|
* - DELETE /api/partner/groupOnboardMember - Removes user from meeting
|
||||||
|
* - Requires authentication headers for user verification
|
||||||
|
* - Server handles the actual removal from meeting database
|
||||||
|
*
|
||||||
|
* State Management:
|
||||||
|
* - Clears attendingMeeting when successfully left
|
||||||
|
* - Refreshes meetings list to show updated availability
|
||||||
|
* - Updates UI to show meeting list instead of single meeting
|
||||||
|
*
|
||||||
|
* User Experience:
|
||||||
|
* - Confirmation prevents accidental departures
|
||||||
|
* - Clear feedback on success/failure
|
||||||
|
* - Seamless transition back to meeting list
|
||||||
|
*
|
||||||
|
* @author Matthew Raymer
|
||||||
|
*/
|
||||||
async leaveMeeting() {
|
async leaveMeeting() {
|
||||||
this.$notify(
|
this.notify.confirm(
|
||||||
{
|
"Are you sure you want to leave this meeting?",
|
||||||
group: "modal",
|
async () => {
|
||||||
type: "confirm",
|
try {
|
||||||
title: "Leave Meeting",
|
const headers = await getHeaders(this.activeDid);
|
||||||
text: "Are you sure you want to leave this meeting?",
|
await this.axios.delete(
|
||||||
onYes: async () => {
|
this.apiServer + "/api/partner/groupOnboardMember",
|
||||||
try {
|
{ headers },
|
||||||
const headers = await getHeaders(this.activeDid);
|
);
|
||||||
await this.axios.delete(
|
|
||||||
this.apiServer + "/api/partner/groupOnboardMember",
|
|
||||||
{ headers },
|
|
||||||
);
|
|
||||||
|
|
||||||
this.attendingMeeting = null;
|
this.attendingMeeting = null;
|
||||||
await this.fetchMeetings();
|
await this.fetchMeetings();
|
||||||
|
|
||||||
this.$notify(
|
this.notify.success("You left the meeting.", TIMEOUTS.LONG);
|
||||||
{
|
} catch (error) {
|
||||||
group: "alert",
|
this.$logAndConsole(
|
||||||
type: "success",
|
"Error leaving meeting: " + errorStringForLog(error),
|
||||||
title: "Success",
|
true,
|
||||||
text: "You left the meeting.",
|
);
|
||||||
},
|
this.notify.error(
|
||||||
5000,
|
serverMessageForUser(error) || "You failed to leave the meeting.",
|
||||||
);
|
TIMEOUTS.LONG,
|
||||||
} catch (error) {
|
);
|
||||||
this.$logAndConsole(
|
}
|
||||||
"Error leaving meeting: " + errorStringForLog(error),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
this.$notify(
|
|
||||||
{
|
|
||||||
group: "alert",
|
|
||||||
type: "danger",
|
|
||||||
title: "Error",
|
|
||||||
text:
|
|
||||||
serverMessageForUser(error) ||
|
|
||||||
"You failed to leave the meeting.",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
-1,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates to the meeting creation page
|
||||||
|
*
|
||||||
|
* This method handles the navigation to the meeting setup page where
|
||||||
|
* registered users can create new onboarding meetings. It's only
|
||||||
|
* available to users who are registered in the system.
|
||||||
|
*
|
||||||
|
* Navigation:
|
||||||
|
* - Routes to onboard-meeting-setup view
|
||||||
|
* - Allows user to configure new meeting settings
|
||||||
|
* - Only accessible to registered users (controlled by template)
|
||||||
|
*
|
||||||
|
* User Flow:
|
||||||
|
* - User clicks "Create Meeting" button
|
||||||
|
* - System navigates to setup page
|
||||||
|
* - User can configure meeting name, password, etc.
|
||||||
|
* - New meeting becomes available to other users
|
||||||
|
*
|
||||||
|
* @author Matthew Raymer
|
||||||
|
*/
|
||||||
createMeeting() {
|
createMeeting() {
|
||||||
this.$router.push({ name: "onboard-meeting-setup" });
|
this.$router.push({ name: "onboard-meeting-setup" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,15 +75,9 @@ export default class ShareMyContactInfoView extends Vue {
|
|||||||
isLoading = false;
|
isLoading = false;
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
// Debug logging for test diagnosis
|
|
||||||
const settings = await this.$settings();
|
const settings = await this.$settings();
|
||||||
|
|
||||||
const activeDid = settings?.activeDid;
|
const activeDid = settings?.activeDid;
|
||||||
// @ts-expect-error - Debug property for testing contact sharing functionality
|
|
||||||
window.__SHARE_CONTACT_DEBUG__ = { settings, activeDid };
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
if (!activeDid) {
|
if (!activeDid) {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
this.$router.push({ name: "home" });
|
this.$router.push({ name: "home" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user