forked from jsnbuchanan/crowd-funder-for-time-pwa
fix: when organizer adds people, they automatically register them as well
This commit is contained in:
@@ -134,8 +134,9 @@ import { Vue, Component, Prop } from "vue-facing-decorator";
|
|||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
||||||
import { MemberData } from "@/interfaces";
|
import { MemberData } from "@/interfaces";
|
||||||
import { setVisibilityUtil, getHeaders } from "@/libs/endorserServer";
|
import { setVisibilityUtil, getHeaders, register } from "@/libs/endorserServer";
|
||||||
import { createNotifyHelpers } from "@/utils/notify";
|
import { createNotifyHelpers } from "@/utils/notify";
|
||||||
|
import { Contact } from "@/db/tables/contacts";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
mixins: [PlatformServiceMixin],
|
mixins: [PlatformServiceMixin],
|
||||||
@@ -253,33 +254,37 @@ export default class BulkMembersDialog extends Vue {
|
|||||||
|
|
||||||
async handleMainAction() {
|
async handleMainAction() {
|
||||||
if (this.dialogType === "admit") {
|
if (this.dialogType === "admit") {
|
||||||
await this.admitWithVisibility();
|
await this.organizerAdmitAndAddWithVisibility();
|
||||||
} else {
|
} else {
|
||||||
await this.addContactWithVisibility();
|
await this.memberAddContactWithVisibility();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async admitWithVisibility() {
|
async organizerAdmitAndAddWithVisibility() {
|
||||||
try {
|
try {
|
||||||
const selectedMembers = this.membersData.filter((member) =>
|
const selectedMembers: MemberData[] = this.membersData.filter((member) =>
|
||||||
this.selectedMembers.includes(member.did),
|
this.selectedMembers.includes(member.did),
|
||||||
);
|
);
|
||||||
const notSelectedMembers = this.membersData.filter(
|
const notSelectedMembers: MemberData[] = this.membersData.filter(
|
||||||
(member) => !this.selectedMembers.includes(member.did),
|
(member) => !this.selectedMembers.includes(member.did),
|
||||||
);
|
);
|
||||||
|
|
||||||
let admittedCount = 0;
|
let admittedCount = 0;
|
||||||
let contactAddedCount = 0;
|
let contactAddedCount = 0;
|
||||||
|
let errors = 0;
|
||||||
|
|
||||||
for (const member of selectedMembers) {
|
for (const member of selectedMembers) {
|
||||||
try {
|
try {
|
||||||
// First, admit the member
|
// First, admit the member
|
||||||
await this.admitMember(member);
|
await this.admitMember(member);
|
||||||
|
|
||||||
|
// Register them
|
||||||
|
await this.registerMember(member);
|
||||||
admittedCount++;
|
admittedCount++;
|
||||||
|
|
||||||
// If they're not a contact yet, add them as a contact
|
// If they're not a contact yet, add them as a contact
|
||||||
if (!member.isContact) {
|
if (!member.isContact) {
|
||||||
await this.addAsContact(member);
|
await this.addAsContact(member, true);
|
||||||
contactAddedCount++;
|
contactAddedCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,19 +294,33 @@ export default class BulkMembersDialog extends Vue {
|
|||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(`Error processing member ${member.did}:`, error);
|
console.error(`Error processing member ${member.did}:`, error);
|
||||||
// Continue with other members even if one fails
|
// Continue with other members even if one fails
|
||||||
|
errors++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show success notification
|
// Show success notification
|
||||||
|
if (admittedCount > 0) {
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "success",
|
type: "success",
|
||||||
title: "Members Admitted Successfully",
|
title: "Members Admitted Successfully",
|
||||||
text: `${admittedCount} member${admittedCount === 1 ? "" : "s"} admitted${contactAddedCount === 0 ? "" : admittedCount === contactAddedCount ? " and" : `, ${contactAddedCount}`}${contactAddedCount === 0 ? "" : ` added as contact${contactAddedCount === 1 ? "" : "s"}`}.`,
|
text: `${admittedCount} member${admittedCount === 1 ? "" : "s"} admitted and registered${contactAddedCount === 0 ? "" : admittedCount === contactAddedCount ? " and" : `, ${contactAddedCount}`}${contactAddedCount === 0 ? "" : ` added as contact${contactAddedCount === 1 ? "" : "s"}`}.`,
|
||||||
},
|
},
|
||||||
10000,
|
10000,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
if (errors > 0) {
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Error",
|
||||||
|
text: "Failed to fully admit some members. Work with them individually below.",
|
||||||
|
},
|
||||||
|
5000,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this.close(notSelectedMembers.map((member) => member.did));
|
this.close(notSelectedMembers.map((member) => member.did));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -312,19 +331,19 @@ export default class BulkMembersDialog extends Vue {
|
|||||||
group: "alert",
|
group: "alert",
|
||||||
type: "danger",
|
type: "danger",
|
||||||
title: "Error",
|
title: "Error",
|
||||||
text: "Failed to admit some members. Please try again.",
|
text: "Some errors occurred. Work with members individually below.",
|
||||||
},
|
},
|
||||||
5000,
|
5000,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async addContactWithVisibility() {
|
async memberAddContactWithVisibility() {
|
||||||
try {
|
try {
|
||||||
const selectedMembers = this.membersData.filter((member) =>
|
const selectedMembers: MemberData[] = this.membersData.filter((member) =>
|
||||||
this.selectedMembers.includes(member.did),
|
this.selectedMembers.includes(member.did),
|
||||||
);
|
);
|
||||||
const notSelectedMembers = this.membersData.filter(
|
const notSelectedMembers: MemberData[] = this.membersData.filter(
|
||||||
(member) => !this.selectedMembers.includes(member.did),
|
(member) => !this.selectedMembers.includes(member.did),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -334,7 +353,7 @@ export default class BulkMembersDialog extends Vue {
|
|||||||
try {
|
try {
|
||||||
// If they're not a contact yet, add them as a contact first
|
// If they're not a contact yet, add them as a contact first
|
||||||
if (!member.isContact) {
|
if (!member.isContact) {
|
||||||
await this.addAsContact(member);
|
await this.addAsContact(member, undefined);
|
||||||
contactsAddedCount++;
|
contactsAddedCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,7 +386,7 @@ export default class BulkMembersDialog extends Vue {
|
|||||||
group: "alert",
|
group: "alert",
|
||||||
type: "danger",
|
type: "danger",
|
||||||
title: "Error",
|
title: "Error",
|
||||||
text: "Failed to add some members as contacts. Please try again.",
|
text: "Some errors occurred. Work with members individually below.",
|
||||||
},
|
},
|
||||||
5000,
|
5000,
|
||||||
);
|
);
|
||||||
@@ -393,11 +412,39 @@ export default class BulkMembersDialog extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async addAsContact(member: { did: string; name: string }) {
|
async registerMember(member: MemberData) {
|
||||||
try {
|
try {
|
||||||
const newContact = {
|
const contact: Contact = { did: member.did };
|
||||||
|
const result = await register(
|
||||||
|
this.activeDid,
|
||||||
|
this.apiServer,
|
||||||
|
this.axios,
|
||||||
|
contact,
|
||||||
|
);
|
||||||
|
if (result.success) {
|
||||||
|
if (result.embeddedRecordError) {
|
||||||
|
throw new Error(result.embeddedRecordError);
|
||||||
|
}
|
||||||
|
await this.$updateContact(member.did, { registered: true });
|
||||||
|
} else {
|
||||||
|
throw result;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error("Error registering member:", err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async addAsContact(
|
||||||
|
member: { did: string; name: string },
|
||||||
|
isRegistered?: boolean,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const newContact: Contact = {
|
||||||
did: member.did,
|
did: member.did,
|
||||||
name: member.name,
|
name: member.name,
|
||||||
|
registered: isRegistered,
|
||||||
};
|
};
|
||||||
|
|
||||||
await this.$insertContact(newContact);
|
await this.$insertContact(newContact);
|
||||||
|
|||||||
@@ -99,7 +99,7 @@
|
|||||||
<font-awesome
|
<font-awesome
|
||||||
v-if="member.did === activeDid"
|
v-if="member.did === activeDid"
|
||||||
icon="hand"
|
icon="hand"
|
||||||
class="fa-fw text-blue-500"
|
class="fa-fw text-slate-500"
|
||||||
/>
|
/>
|
||||||
<font-awesome
|
<font-awesome
|
||||||
v-if="
|
v-if="
|
||||||
@@ -113,10 +113,10 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<div
|
<div
|
||||||
v-if="!getContactFor(member.did) && member.did !== activeDid"
|
v-if="!getContactFor(member.did) && member.did !== activeDid"
|
||||||
class="flex items-center gap-1.5 ms-1"
|
class="flex items-center gap-1.5 ml-2 ms-1"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="btn-add-contact"
|
class="btn-add-contact ml-2"
|
||||||
title="Add as contact"
|
title="Add as contact"
|
||||||
@click="addAsContact(member)"
|
@click="addAsContact(member)"
|
||||||
>
|
>
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn-info-contact"
|
class="btn-info-contact ml-2"
|
||||||
title="Contact Info"
|
title="Contact Info"
|
||||||
@click="
|
@click="
|
||||||
informAboutAddingContact(
|
informAboutAddingContact(
|
||||||
@@ -135,6 +135,27 @@
|
|||||||
<font-awesome icon="circle-info" />
|
<font-awesome icon="circle-info" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="getContactFor(member.did) && member.did !== activeDid"
|
||||||
|
class="flex items-center gap-1.5 ms-1"
|
||||||
|
>
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'contact-edit', params: { did: member.did } }"
|
||||||
|
>
|
||||||
|
<font-awesome
|
||||||
|
icon="pen"
|
||||||
|
class="text-sm text-blue-500 ml-2 mb-1"
|
||||||
|
/>
|
||||||
|
</router-link>
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'did', params: { did: member.did } }"
|
||||||
|
>
|
||||||
|
<font-awesome
|
||||||
|
icon="arrow-up-right-from-square"
|
||||||
|
class="text-sm text-blue-500 ml-2 mb-1"
|
||||||
|
/>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
v-if="
|
v-if="
|
||||||
|
|||||||
@@ -510,14 +510,6 @@ export const NOTIFY_REGISTER_CONTACT = {
|
|||||||
text: "Do you want to register them?",
|
text: "Do you want to register them?",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used in: ContactsView.vue (showOnboardMeetingDialog method - complex modal for onboarding meeting)
|
|
||||||
export const NOTIFY_ONBOARDING_MEETING = {
|
|
||||||
title: "Onboarding Meeting",
|
|
||||||
text: "Would you like to start a new meeting?",
|
|
||||||
yesText: "Start New Meeting",
|
|
||||||
noText: "Join Existing Meeting",
|
|
||||||
};
|
|
||||||
|
|
||||||
// TestView.vue specific constants
|
// TestView.vue specific constants
|
||||||
// Used in: TestView.vue (executeSql method - SQL error handling)
|
// Used in: TestView.vue (executeSql method - SQL error handling)
|
||||||
export const NOTIFY_SQL_ERROR = {
|
export const NOTIFY_SQL_ERROR = {
|
||||||
|
|||||||
@@ -70,15 +70,6 @@ export interface AxiosErrorResponse {
|
|||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserInfo {
|
|
||||||
did: string;
|
|
||||||
name: string;
|
|
||||||
publicEncKey: string;
|
|
||||||
registered: boolean;
|
|
||||||
profileImageUrl?: string;
|
|
||||||
nextPublicEncKeyHash?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateAndSubmitClaimResult {
|
export interface CreateAndSubmitClaimResult {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
embeddedRecordError?: string;
|
embeddedRecordError?: string;
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ export * from "./common";
|
|||||||
export * from "./deepLinks";
|
export * from "./deepLinks";
|
||||||
export * from "./limits";
|
export * from "./limits";
|
||||||
export * from "./records";
|
export * from "./records";
|
||||||
|
export * from "./user";
|
||||||
|
|||||||
@@ -42,9 +42,6 @@ import {
|
|||||||
PlanActionClaim,
|
PlanActionClaim,
|
||||||
RegisterActionClaim,
|
RegisterActionClaim,
|
||||||
TenureClaim,
|
TenureClaim,
|
||||||
} from "../interfaces/claims";
|
|
||||||
|
|
||||||
import {
|
|
||||||
GenericCredWrapper,
|
GenericCredWrapper,
|
||||||
GenericVerifiableCredential,
|
GenericVerifiableCredential,
|
||||||
AxiosErrorResponse,
|
AxiosErrorResponse,
|
||||||
@@ -55,13 +52,11 @@ import {
|
|||||||
QuantitativeValue,
|
QuantitativeValue,
|
||||||
KeyMetaWithPrivate,
|
KeyMetaWithPrivate,
|
||||||
KeyMetaMaybeWithPrivate,
|
KeyMetaMaybeWithPrivate,
|
||||||
} from "../interfaces/common";
|
|
||||||
import {
|
|
||||||
OfferSummaryRecord,
|
OfferSummaryRecord,
|
||||||
OfferToPlanSummaryRecord,
|
OfferToPlanSummaryRecord,
|
||||||
PlanSummaryAndPreviousClaim,
|
PlanSummaryAndPreviousClaim,
|
||||||
PlanSummaryRecord,
|
PlanSummaryRecord,
|
||||||
} from "../interfaces/records";
|
} from "../interfaces";
|
||||||
import { logger, safeStringify } from "../utils/logger";
|
import { logger, safeStringify } from "../utils/logger";
|
||||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||||
import { APP_SERVER } from "@/constants/app";
|
import { APP_SERVER } from "@/constants/app";
|
||||||
@@ -1662,30 +1657,35 @@ export async function register(
|
|||||||
message?: string;
|
message?: string;
|
||||||
}>(url, { jwtEncoded: vcJwt });
|
}>(url, { jwtEncoded: vcJwt });
|
||||||
|
|
||||||
if (resp.data?.success?.handleId) {
|
if (resp.data?.success?.embeddedRecordError) {
|
||||||
return { success: true };
|
|
||||||
} else if (resp.data?.success?.embeddedRecordError) {
|
|
||||||
let message =
|
let message =
|
||||||
"There was some problem with the registration and so it may not be complete.";
|
"There was some problem with the registration and so it may not be complete.";
|
||||||
if (typeof resp.data.success.embeddedRecordError === "string") {
|
if (typeof resp.data.success.embeddedRecordError === "string") {
|
||||||
message += " " + resp.data.success.embeddedRecordError;
|
message += " " + resp.data.success.embeddedRecordError;
|
||||||
}
|
}
|
||||||
return { error: message };
|
return { error: message };
|
||||||
|
} else if (resp.data?.success?.handleId) {
|
||||||
|
return { success: true };
|
||||||
} else {
|
} else {
|
||||||
logger.error("Registration error:", JSON.stringify(resp.data));
|
logger.error("Registration non-thrown error:", JSON.stringify(resp.data));
|
||||||
return { error: "Got a server error when registering." };
|
return {
|
||||||
|
error:
|
||||||
|
(resp.data?.error as { message?: string })?.message ||
|
||||||
|
(resp.data?.error as string) ||
|
||||||
|
"Got a server error when registering.",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error && typeof error === "object") {
|
if (error && typeof error === "object") {
|
||||||
const err = error as AxiosErrorResponse;
|
const err = error as AxiosErrorResponse;
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
err.message ||
|
err.response?.data?.error?.message ||
|
||||||
(err.response?.data &&
|
err.response?.data?.error ||
|
||||||
typeof err.response.data === "object" &&
|
err.message;
|
||||||
"message" in err.response.data
|
logger.error(
|
||||||
? (err.response.data as { message: string }).message
|
"Registration thrown error:",
|
||||||
: undefined);
|
errorMessage || JSON.stringify(err),
|
||||||
logger.error("Registration error:", errorMessage || JSON.stringify(err));
|
);
|
||||||
return { error: errorMessage || "Got a server error when registering." };
|
return { error: errorMessage || "Got a server error when registering." };
|
||||||
}
|
}
|
||||||
return { error: "Got a server error when registering." };
|
return { error: "Got a server error when registering." };
|
||||||
|
|||||||
@@ -171,9 +171,11 @@ import {
|
|||||||
CONTACT_IMPORT_ONE_URL_PATH_TIME_SAFARI,
|
CONTACT_IMPORT_ONE_URL_PATH_TIME_SAFARI,
|
||||||
CONTACT_URL_PATH_ENDORSER_CH_OLD,
|
CONTACT_URL_PATH_ENDORSER_CH_OLD,
|
||||||
} from "../libs/endorserServer";
|
} from "../libs/endorserServer";
|
||||||
import { GiveSummaryRecord } from "@/interfaces/records";
|
import {
|
||||||
import { UserInfo } from "@/interfaces/common";
|
GiveSummaryRecord,
|
||||||
import { VerifiableCredential } from "@/interfaces/claims-result";
|
UserInfo,
|
||||||
|
VerifiableCredential,
|
||||||
|
} from "@/interfaces";
|
||||||
import * as libsUtil from "../libs/util";
|
import * as libsUtil from "../libs/util";
|
||||||
import {
|
import {
|
||||||
generateSaveAndActivateIdentity,
|
generateSaveAndActivateIdentity,
|
||||||
|
|||||||
@@ -473,6 +473,7 @@ export default class OnboardMeetingView extends Vue {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const password: string = this.newOrUpdatedMeetingInputs.password;
|
||||||
|
|
||||||
// create content with user's name & DID encrypted with password
|
// create content with user's name & DID encrypted with password
|
||||||
const content = {
|
const content = {
|
||||||
@@ -482,7 +483,7 @@ export default class OnboardMeetingView extends Vue {
|
|||||||
};
|
};
|
||||||
const encryptedContent = await encryptMessage(
|
const encryptedContent = await encryptMessage(
|
||||||
JSON.stringify(content),
|
JSON.stringify(content),
|
||||||
this.newOrUpdatedMeetingInputs.password,
|
password,
|
||||||
);
|
);
|
||||||
|
|
||||||
const headers = await getHeaders(this.activeDid);
|
const headers = await getHeaders(this.activeDid);
|
||||||
@@ -505,6 +506,11 @@ export default class OnboardMeetingView extends Vue {
|
|||||||
|
|
||||||
this.newOrUpdatedMeetingInputs = null;
|
this.newOrUpdatedMeetingInputs = null;
|
||||||
this.notify.success(NOTIFY_MEETING_CREATED.message, TIMEOUTS.STANDARD);
|
this.notify.success(NOTIFY_MEETING_CREATED.message, TIMEOUTS.STANDARD);
|
||||||
|
// redirect to the same page with the password parameter set
|
||||||
|
this.$router.push({
|
||||||
|
name: "onboard-meeting-setup",
|
||||||
|
query: { password: password },
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
throw { response: response };
|
throw { response: response };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user