forked from trent_larson/crowd-funder-for-time-pwa
Merge branch 'master' into split_build_process
fix: image server references and test configurations - Update image server references to use test server by default for local dev - Fix registration status checks in tests - Remove verbose console logging - Update environment configurations for consistent image server usage - Fix alert handling in contact registration tests - Clean up component lifecycle logging - Add clarifying comments about shared image server usage - Update playwright test configurations for better reliability This commit ensures consistent image server behavior across environments and improves test reliability by properly handling registration status checks and alerts.
This commit is contained in:
@@ -21,9 +21,9 @@ import { Component, Vue } from "vue-facing-decorator";
|
||||
import { nextTick } from "vue";
|
||||
import QRCode from "qrcode";
|
||||
|
||||
import { APP_SERVER, NotificationIface } from "@/constants/app";
|
||||
import { db, retrieveSettingsForActiveAccount } from "@/db/index";
|
||||
import * as endorserServer from "@/libs/endorserServer";
|
||||
import { APP_SERVER, NotificationIface } from "../constants/app";
|
||||
import { db, retrieveSettingsForActiveAccount } from "../db/index";
|
||||
import * as endorserServer from "../libs/endorserServer";
|
||||
|
||||
@Component
|
||||
export default class ClaimReportCertificateView extends Vue {
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<fa icon="calendar" class="fa-fw text-slate-400" />
|
||||
Recorded
|
||||
{{ veriClaim.issuedAt?.replace(/T/, " ").replace(/Z/, " UTC") }}
|
||||
</div>
|
||||
<div v-if="veriClaim.claim.image" class="flex justify-center">
|
||||
|
||||
@@ -1416,11 +1416,11 @@ export default class ContactsView extends Vue {
|
||||
type: "confirm",
|
||||
title: "Onboarding Meeting",
|
||||
text: "Would you like to start a new meeting?",
|
||||
onYes: () => {
|
||||
onYes: async () => {
|
||||
(this.$router as Router).push({ name: "onboard-meeting-setup" });
|
||||
},
|
||||
yesText: "Start New Meeting",
|
||||
onNo: () => {
|
||||
onNo: async () => {
|
||||
(this.$router as Router).push({ name: "onboard-meeting-list" });
|
||||
},
|
||||
noText: "Join Existing Meeting",
|
||||
|
||||
@@ -281,7 +281,7 @@
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
<span class="col-span-10 justify-self-stretch">
|
||||
<span class="col-span-10 justify-self-stretch overflow-hidden">
|
||||
<!-- show giver and/or receiver profiles... which seemed like a good idea but actually adds clutter
|
||||
<span
|
||||
v-if="
|
||||
@@ -315,7 +315,7 @@
|
||||
/>
|
||||
</span>
|
||||
-->
|
||||
<span class="pl-2">
|
||||
<span class="pl-2 block break-words">
|
||||
{{ giveDescription(record) }}
|
||||
</span>
|
||||
<a @click="onClickLoadClaim(record.jwtId)">
|
||||
@@ -346,10 +346,18 @@
|
||||
</router-link>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="record.image" class="flex justify-center">
|
||||
<a :href="record.image" target="_blank">
|
||||
<img :src="record.image" class="h-48 mt-2 rounded-xl" />
|
||||
</a>
|
||||
<div v-if="record.image" class="w-full">
|
||||
<div
|
||||
class="cursor-pointer"
|
||||
@click="openImageViewer(record.image)"
|
||||
>
|
||||
<img
|
||||
:src="record.image"
|
||||
class="w-full aspect-[3/2] object-cover rounded-xl mt-2"
|
||||
alt="shared content"
|
||||
@load="cacheImageData($event, record.image)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -368,6 +376,12 @@
|
||||
</section>
|
||||
|
||||
<ChoiceButtonDialog ref="choiceButtonDialog" />
|
||||
|
||||
<ImageViewer
|
||||
:image-url="selectedImage"
|
||||
:image-data="selectedImageData"
|
||||
v-model:is-open="isImageViewerOpen"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -386,6 +400,7 @@ import QuickNav from "../components/QuickNav.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 {
|
||||
AppString,
|
||||
NotificationIface,
|
||||
@@ -450,6 +465,7 @@ interface GiveRecordWithContactInfo extends GiveSummaryRecord {
|
||||
QuickNav,
|
||||
TopMessage,
|
||||
UserNameDialog,
|
||||
ImageViewer,
|
||||
},
|
||||
})
|
||||
export default class HomeView extends Vue {
|
||||
@@ -485,6 +501,10 @@ export default class HomeView extends Vue {
|
||||
}> = [];
|
||||
showShortcutBvc = false;
|
||||
userAgentInfo = new UAParser(); // see https://docs.uaparser.js.org/v2/api/ua-parser-js/get-os.html
|
||||
selectedImage = "";
|
||||
selectedImageData: Blob | null = null;
|
||||
isImageViewerOpen = false;
|
||||
imageCache: Map<string, Blob | null> = new Map();
|
||||
|
||||
async mounted() {
|
||||
try {
|
||||
@@ -966,5 +986,21 @@ export default class HomeView extends Vue {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async cacheImageData(event: Event, imageUrl: string) {
|
||||
try {
|
||||
// For images that might fail CORS, just store the URL
|
||||
// The Web Share API will handle sharing the URL appropriately
|
||||
this.imageCache.set(imageUrl, null);
|
||||
} catch (error) {
|
||||
console.warn("Failed to cache image:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async openImageViewer(imageUrl: string) {
|
||||
this.selectedImageData = this.imageCache.get(imageUrl) ?? null;
|
||||
this.selectedImage = imageUrl;
|
||||
this.isImageViewerOpen = true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -71,17 +71,17 @@
|
||||
|
||||
<textarea
|
||||
placeholder="Description"
|
||||
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
||||
class="block w-full rounded border border-slate-400 px-3 py-2"
|
||||
rows="5"
|
||||
v-model="fullClaim.description"
|
||||
maxlength="5000"
|
||||
></textarea>
|
||||
<div class="text-xs text-slate-500 italic -mt-3 mb-4">
|
||||
<div class="text-xs text-slate-500 italic">
|
||||
If you want to be contacted, be sure to include your contact information
|
||||
-- just remember that this information is public and saved in a public
|
||||
history.
|
||||
</div>
|
||||
<div class="text-xs text-slate-500 italic -mt-3 mb-4">
|
||||
<div class="text-xs text-slate-500 italic">
|
||||
{{ fullClaim.description?.length }}/5000 max. characters
|
||||
</div>
|
||||
|
||||
@@ -89,28 +89,55 @@
|
||||
v-model="fullClaim.url"
|
||||
placeholder="Website"
|
||||
autocapitalize="none"
|
||||
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
||||
class="block w-full rounded border border-slate-400 mt-4 px-3 py-2"
|
||||
/>
|
||||
|
||||
<div class="flex mb-4 columns-3 w-full">
|
||||
<input
|
||||
v-model="startDateInput"
|
||||
placeholder="Start Date"
|
||||
type="date"
|
||||
class="col-span-1 w-full rounded border border-slate-400 px-3 py-2"
|
||||
/>
|
||||
<input
|
||||
:disabled="!startDateInput"
|
||||
placeholder="Start Time"
|
||||
v-model="startTimeInput"
|
||||
type="time"
|
||||
class="col-span-1 w-full rounded border border-slate-400 ml-2 px-3 py-2"
|
||||
/>
|
||||
<span class="col-span-1 w-full flex justify-center">{{ zoneName }}</span>
|
||||
<div>
|
||||
<div class="flex items-center mt-4">
|
||||
<span class="mr-2">Starts At</span>
|
||||
<input
|
||||
v-model="startDateInput"
|
||||
placeholder="Start Date"
|
||||
type="date"
|
||||
class="rounded border border-slate-400 px-3 py-2"
|
||||
/>
|
||||
<input
|
||||
:disabled="!startDateInput"
|
||||
placeholder="Start Time"
|
||||
v-model="startTimeInput"
|
||||
type="time"
|
||||
class="rounded border border-slate-400 ml-2 px-3 py-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full justify-end items-center">
|
||||
<span class="w-full flex justify-end items-center">
|
||||
{{ zoneName }} time zone
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="mr-2">
|
||||
<span>Ends at</span>
|
||||
</div>
|
||||
<input
|
||||
v-model="endDateInput"
|
||||
placeholder="End Date"
|
||||
type="date"
|
||||
class="ml-2 rounded border border-slate-400 px-3 py-2"
|
||||
/>
|
||||
<input
|
||||
:disabled="!endDateInput"
|
||||
placeholder="End Time"
|
||||
v-model="endTimeInput"
|
||||
type="time"
|
||||
class="rounded border border-slate-400 ml-2 px-3 py-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex items-center mb-4"
|
||||
class="flex items-center mt-4"
|
||||
@click="includeLocation = !includeLocation"
|
||||
>
|
||||
<input type="checkbox" class="mr-2" v-model="includeLocation" />
|
||||
@@ -202,18 +229,10 @@
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import { AxiosError, AxiosRequestHeaders } from "axios";
|
||||
import { DateTime } from "luxon";
|
||||
import { hexToBytes } from "@noble/hashes/utils";
|
||||
import { finalizeEvent, serializeEvent } from "nostr-tools";
|
||||
// these core imports could also be included as "import type ..."
|
||||
import {
|
||||
EventTemplate,
|
||||
UnsignedEvent,
|
||||
VerifiedEvent,
|
||||
} from "nostr-tools/lib/types/core";
|
||||
import {
|
||||
accountFromExtendedKey,
|
||||
extendedKeysFromSeedWords,
|
||||
} from "nostr-tools/nip06";
|
||||
import { finalizeEvent, serializeEvent } from "nostr-tools/pure";
|
||||
import { EventTemplate, UnsignedEvent, VerifiedEvent } from "nostr-tools/core";
|
||||
import * as nip06 from "nostr-tools/nip06";
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet";
|
||||
import { RouteLocationNormalizedLoaded, Router } from "vue-router";
|
||||
@@ -251,6 +270,8 @@ export default class NewEditProjectView extends Vue {
|
||||
activeDid = "";
|
||||
agentDid = "";
|
||||
apiServer = "";
|
||||
endDateInput?: string;
|
||||
endTimeInput?: string;
|
||||
errorMessage = "";
|
||||
fullClaim: PlanVerifiableCredential = {
|
||||
"@context": "https://schema.org",
|
||||
@@ -325,6 +346,13 @@ export default class NewEditProjectView extends Vue {
|
||||
this.startDateInput = localDateTime.toFormat("yyyy-MM-dd");
|
||||
this.startTimeInput = localDateTime.toFormat("HH:mm");
|
||||
}
|
||||
if (this.fullClaim.endTime) {
|
||||
const localDateTime = DateTime.fromISO(
|
||||
this.fullClaim.endTime as string,
|
||||
).toLocal();
|
||||
this.endDateInput = localDateTime.toFormat("yyyy-MM-dd");
|
||||
this.endTimeInput = localDateTime.toFormat("HH:mm");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Got error retrieving that project", error);
|
||||
@@ -468,7 +496,7 @@ export default class NewEditProjectView extends Vue {
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Date Error",
|
||||
text: "The date was invalid so it was not set.",
|
||||
text: "The start date was invalid so it was not set.",
|
||||
},
|
||||
5000,
|
||||
);
|
||||
@@ -476,6 +504,28 @@ export default class NewEditProjectView extends Vue {
|
||||
} else {
|
||||
delete vcClaim.startTime;
|
||||
}
|
||||
if (this.endDateInput) {
|
||||
try {
|
||||
const endTimeFull = this.endTimeInput || "23:59:59";
|
||||
const fullTimeString = this.endDateInput + " " + endTimeFull;
|
||||
// throw an error on an invalid date or time string
|
||||
vcClaim.endTime = new Date(fullTimeString).toISOString(); // ensure timezone is part of it
|
||||
} catch {
|
||||
// it's not a valid date so erase it and tell the user
|
||||
delete vcClaim.endTime;
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Date Error",
|
||||
text: "The end date was invalid so it was not set.",
|
||||
},
|
||||
5000,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
delete vcClaim.endTime;
|
||||
}
|
||||
const vcJwt = await createEndorserJwtVcFromClaim(this.activeDid, vcClaim);
|
||||
|
||||
// Make the xhr request payload
|
||||
@@ -618,15 +668,15 @@ export default class NewEditProjectView extends Vue {
|
||||
// remove any trailing '
|
||||
const finalDerNumNoApostrophe = finalDerNum?.replace(/'/g, "");
|
||||
const accountNum = Number(finalDerNumNoApostrophe || 0);
|
||||
const extPubPri = extendedKeysFromSeedWords(
|
||||
const extPubPri = nip06.extendedKeysFromSeedWords(
|
||||
account?.mnemonic as string,
|
||||
"",
|
||||
accountNum,
|
||||
);
|
||||
const publicExtendedKey: string = extPubPri?.publicExtendedKey;
|
||||
const privateExtendedKey = extPubPri?.privateExtendedKey;
|
||||
const privateKey = accountFromExtendedKey(privateExtendedKey).privateKey;
|
||||
const privateBytes = hexToBytes(privateKey);
|
||||
const privateBytes: Uint8Array =
|
||||
nip06.accountFromExtendedKey(privateExtendedKey).privateKey;
|
||||
// No real content is necessary, we just want something signed,
|
||||
// so we might as well use nostr libs for nostr functions.
|
||||
// Besides: someday we may create real content that we can relay.
|
||||
@@ -660,7 +710,8 @@ export default class NewEditProjectView extends Vue {
|
||||
const endorserPartnerUrl = partnerServer + "/api/partner/link";
|
||||
const timeSafariUrl = window.location.origin + "/claim/" + jwtId;
|
||||
const content = this.fullClaim.name + " - see " + timeSafariUrl;
|
||||
const publicKeyHex = accountFromExtendedKey(publicExtendedKey).publicKey;
|
||||
const publicKeyHex =
|
||||
nip06.accountFromExtendedKey(publicExtendedKey).publicKey;
|
||||
const unsignedPayload: UnsignedEvent = {
|
||||
// why doesn't "...signedPayload" work?
|
||||
kind: signedPayload.kind,
|
||||
|
||||
@@ -126,6 +126,7 @@ export default class OnboardMeetingListView extends Vue {
|
||||
attendingMeeting: Meeting | null = null;
|
||||
firstName = "";
|
||||
isLoading = false;
|
||||
isRegistered = false;
|
||||
meetings: Meeting[] = [];
|
||||
password = "";
|
||||
selectedMeeting: Meeting | null = null;
|
||||
@@ -136,6 +137,7 @@ export default class OnboardMeetingListView extends Vue {
|
||||
this.activeDid = settings.activeDid || "";
|
||||
this.apiServer = settings.apiServer || "";
|
||||
this.firstName = settings.firstName || "";
|
||||
this.isRegistered = !!settings.isRegistered;
|
||||
await this.fetchMeetings();
|
||||
}
|
||||
|
||||
@@ -232,6 +234,7 @@ export default class OnboardMeetingListView extends Vue {
|
||||
const memberData = {
|
||||
name: this.firstName,
|
||||
did: this.activeDid,
|
||||
isRegistered: this.isRegistered,
|
||||
};
|
||||
const memberDataString = JSON.stringify(memberData);
|
||||
const encryptedMemberData = await encryptMessage(
|
||||
|
||||
@@ -8,8 +8,16 @@
|
||||
Meeting Members
|
||||
</h1>
|
||||
|
||||
<!-- Loading Animation -->
|
||||
<div
|
||||
class="mt-16 text-center text-4xl bg-slate-400 text-white w-14 py-2.5 rounded-full mx-auto"
|
||||
v-if="isLoading"
|
||||
>
|
||||
<fa icon="spinner" class="fa-spin-pulse"></fa>
|
||||
</div>
|
||||
|
||||
<!-- Error State -->
|
||||
<div v-if="errorMessage">
|
||||
<div v-else-if="errorMessage">
|
||||
<div class="text-center text-red-600 py-8">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
@@ -19,13 +27,14 @@
|
||||
</div>
|
||||
|
||||
<!-- Members List -->
|
||||
<MembersList
|
||||
v-else
|
||||
:password="password"
|
||||
:decrypt-failure-message="'That password failed. You may be in the wrong meeting. Go back and try again.'"
|
||||
@error="handleError"
|
||||
/>
|
||||
<MembersList v-else :password="password" @error="handleError" />
|
||||
</section>
|
||||
|
||||
<UserNameDialog
|
||||
ref="userNameDialog"
|
||||
:callback-on-cancel="true"
|
||||
sharing-explanation="This is encrypted and shared only with people in this meeting."
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -35,16 +44,35 @@ import { RouteLocation } from "vue-router";
|
||||
import QuickNav from "../components/QuickNav.vue";
|
||||
import TopMessage from "../components/TopMessage.vue";
|
||||
import MembersList from "../components/MembersList.vue";
|
||||
import UserNameDialog from "../components/UserNameDialog.vue";
|
||||
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from "../db/index";
|
||||
import { encryptMessage } from "../libs/crypto";
|
||||
import {
|
||||
errorStringForLog,
|
||||
getHeaders,
|
||||
serverMessageForUser,
|
||||
} from "../libs/endorserServer";
|
||||
import { generateSaveAndActivateIdentity } from "../libs/util";
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
QuickNav,
|
||||
TopMessage,
|
||||
MembersList,
|
||||
UserNameDialog,
|
||||
},
|
||||
})
|
||||
export default class OnboardMeetingMembersView extends Vue {
|
||||
activeDid = "";
|
||||
apiServer = "";
|
||||
errorMessage = "";
|
||||
firstName = "";
|
||||
isRegistered = false;
|
||||
isLoading = true;
|
||||
|
||||
$refs!: {
|
||||
userNameDialog: InstanceType<typeof UserNameDialog>;
|
||||
};
|
||||
|
||||
get groupId(): string {
|
||||
return (this.$route as RouteLocation).params.groupId as string;
|
||||
@@ -63,6 +91,122 @@ export default class OnboardMeetingMembersView extends Vue {
|
||||
this.errorMessage = "The password is missing. Go back and try again.";
|
||||
return;
|
||||
}
|
||||
const settings = await retrieveSettingsForActiveAccount();
|
||||
this.activeDid = settings.activeDid || "";
|
||||
this.apiServer = settings.apiServer || "";
|
||||
this.firstName = settings.firstName || "";
|
||||
this.isRegistered = settings.isRegistered || false;
|
||||
try {
|
||||
if (!this.activeDid) {
|
||||
this.activeDid = await generateSaveAndActivateIdentity();
|
||||
this.isRegistered = false;
|
||||
}
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
const response = await this.axios.get(
|
||||
`${this.apiServer}/api/partner/groupOnboardMember`,
|
||||
{ headers },
|
||||
);
|
||||
const member = response.data?.data;
|
||||
if (!member) {
|
||||
if (!this.firstName) {
|
||||
this.$refs.userNameDialog.open(this.addMemberToMeeting);
|
||||
// addMemberToMeeting sets isLoading to false
|
||||
} else {
|
||||
await this.addMemberToMeeting(this.firstName);
|
||||
// addMemberToMeeting sets isLoading to false
|
||||
}
|
||||
} else if (String(member.groupId) !== this.groupId) {
|
||||
this.errorMessage =
|
||||
"You are already in a different meeting. Reload or go back and try again.";
|
||||
this.isLoading = false;
|
||||
} else {
|
||||
// must be already in the right meeting
|
||||
if (!this.firstName) {
|
||||
this.$refs.userNameDialog.open(this.updateMemberInMeeting);
|
||||
// updateMemberInMeeting sets isLoading to false
|
||||
} else {
|
||||
await this.updateMemberInMeeting(this.firstName);
|
||||
// updateMemberInMeeting sets isLoading to false
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.errorMessage =
|
||||
serverMessageForUser(error) ||
|
||||
"There was an error checking for that meeting. Reload or go back and try again.";
|
||||
logConsoleAndDb(
|
||||
"Error checking meeting: " + errorStringForLog(error),
|
||||
true,
|
||||
);
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async addMemberToMeeting(name?: string) {
|
||||
if (name != null) {
|
||||
this.firstName = name;
|
||||
}
|
||||
|
||||
const memberData = {
|
||||
name: this.firstName,
|
||||
did: this.activeDid,
|
||||
isRegistered: this.isRegistered,
|
||||
};
|
||||
const memberDataString = JSON.stringify(memberData);
|
||||
const encryptedMemberData = await encryptMessage(
|
||||
memberDataString,
|
||||
this.password,
|
||||
);
|
||||
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
try {
|
||||
await this.axios.post(
|
||||
`${this.apiServer}/api/partner/groupOnboardMember`,
|
||||
{ groupId: this.groupId, content: encryptedMemberData },
|
||||
{ headers },
|
||||
);
|
||||
} catch (error) {
|
||||
logConsoleAndDb(
|
||||
"Error adding member to meeting: " + errorStringForLog(error),
|
||||
true,
|
||||
);
|
||||
this.errorMessage =
|
||||
serverMessageForUser(error) ||
|
||||
"You're not in a meeting and couldn't be added to this one. Reload or go back and try again.";
|
||||
}
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
async updateMemberInMeeting(name?: string) {
|
||||
if (name != null) {
|
||||
this.firstName = name;
|
||||
}
|
||||
const memberData = {
|
||||
name: this.firstName,
|
||||
did: this.activeDid,
|
||||
isRegistered: this.isRegistered,
|
||||
};
|
||||
const memberDataString = JSON.stringify(memberData);
|
||||
const encryptedMemberData = await encryptMessage(
|
||||
memberDataString,
|
||||
this.password,
|
||||
);
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
try {
|
||||
await this.axios.put(
|
||||
`${this.apiServer}/api/partner/groupOnboardMember`,
|
||||
{ content: encryptedMemberData },
|
||||
{ headers },
|
||||
);
|
||||
} catch (error) {
|
||||
logConsoleAndDb(
|
||||
"Error updating member in meeting: " + errorStringForLog(error),
|
||||
true,
|
||||
);
|
||||
this.errorMessage =
|
||||
serverMessageForUser(error) ||
|
||||
"There was an error updating your name. Reload or go back and try again.";
|
||||
}
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
handleError(message: string) {
|
||||
|
||||
@@ -192,23 +192,23 @@
|
||||
|
||||
<!-- Members Section -->
|
||||
<div
|
||||
v-if="!isLoading && currentMeeting != null"
|
||||
v-if="!isLoading && currentMeeting != null && !!currentMeeting.password"
|
||||
class="mt-8 p-4 border rounded-lg bg-white shadow"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-2xl">Meeting Members</h2>
|
||||
</div>
|
||||
<router-link
|
||||
v-if="!!currentMeeting.password"
|
||||
:to="onboardMeetingMembersLink()"
|
||||
class="inline-block text-blue-600"
|
||||
target="_blank"
|
||||
>
|
||||
Open shortcut page for members <fa icon="external-link" />
|
||||
• Open shortcut page for members <fa icon="external-link" />
|
||||
</router-link>
|
||||
|
||||
<MembersList
|
||||
:password="currentMeeting.password || ''"
|
||||
:decrypt-failure-message="DECRYPT_FAILURE_MESSAGE"
|
||||
:show-organizer-tools="true"
|
||||
@error="handleMembersError"
|
||||
class="mt-4"
|
||||
@@ -264,15 +264,13 @@ export default class OnboardMeetingView extends Vue {
|
||||
timeout?: number,
|
||||
) => void;
|
||||
|
||||
DECRYPT_FAILURE_MESSAGE =
|
||||
"Unable to decrypt some member information. Check your password, or have them reset theirs if they don't show here.";
|
||||
|
||||
currentMeeting: ServerMeeting | null = null;
|
||||
newOrUpdatedMeeting: MeetingSetupInfo | null = null;
|
||||
activeDid = "";
|
||||
apiServer = "";
|
||||
isDeleting = false;
|
||||
isLoading = true;
|
||||
isRegistered = false;
|
||||
showDeleteConfirm = false;
|
||||
fullName = "";
|
||||
get minDateTime() {
|
||||
@@ -286,6 +284,7 @@ export default class OnboardMeetingView extends Vue {
|
||||
this.activeDid = settings.activeDid || "";
|
||||
this.apiServer = settings.apiServer || "";
|
||||
this.fullName = settings.firstName || "";
|
||||
this.isRegistered = !!settings.isRegistered;
|
||||
|
||||
await this.fetchCurrentMeeting();
|
||||
this.isLoading = false;
|
||||
@@ -409,6 +408,7 @@ export default class OnboardMeetingView extends Vue {
|
||||
const content = {
|
||||
name: this.newOrUpdatedMeeting.userFullName,
|
||||
did: this.activeDid,
|
||||
isRegistered: this.isRegistered,
|
||||
};
|
||||
const encryptedContent = await encryptMessage(
|
||||
JSON.stringify(content),
|
||||
@@ -598,6 +598,7 @@ export default class OnboardMeetingView extends Vue {
|
||||
const content = {
|
||||
name: this.newOrUpdatedMeeting.userFullName,
|
||||
did: this.activeDid,
|
||||
isRegistered: this.isRegistered,
|
||||
};
|
||||
const encryptedContent = await encryptMessage(
|
||||
JSON.stringify(content),
|
||||
|
||||
@@ -69,7 +69,11 @@
|
||||
</div>
|
||||
<div v-if="startTime">
|
||||
<fa icon="calendar" class="fa-fw text-slate-400"></fa>
|
||||
{{ startTime }}
|
||||
Starts {{ startTime }}
|
||||
</div>
|
||||
<div v-if="endTime">
|
||||
<fa icon="calendar" class="fa-fw text-slate-400"></fa>
|
||||
Ends {{ endTime }}
|
||||
</div>
|
||||
<div v-if="latitude || longitude">
|
||||
<fa icon="location-dot" class="fa-fw text-slate-400"></fa>
|
||||
@@ -541,6 +545,7 @@ export default class ProjectViewView extends Vue {
|
||||
apiServer = "";
|
||||
checkingConfirmationForJwtId = "";
|
||||
description = "";
|
||||
endTime = "";
|
||||
expanded = false;
|
||||
fulfilledByThis: PlanSummaryRecord | null = null;
|
||||
fulfillersToThis: Array<PlanSummaryRecord> = [];
|
||||
@@ -641,6 +646,14 @@ export default class ProjectViewView extends Vue {
|
||||
" " +
|
||||
startDateTime.toLocaleTimeString();
|
||||
}
|
||||
const endTime = resp.data.claim?.endTime;
|
||||
if (endTime != null) {
|
||||
const endDateTime = new Date(endTime);
|
||||
this.endTime =
|
||||
endDateTime.toLocaleDateString() +
|
||||
" " +
|
||||
endDateTime.toLocaleTimeString();
|
||||
}
|
||||
this.agentDid = resp.data.claim?.agent?.identifier;
|
||||
this.agentDidVisibleToDids =
|
||||
resp.data.claim?.agent?.identifierVisibleToDids || [];
|
||||
|
||||
Reference in New Issue
Block a user