Browse Source

make member view available to onboard meeting organizer and reorganize buttons

master
Trent Larson 2 days ago
parent
commit
fe71c3f754
  1. 241
      src/components/MembersList.vue
  2. 15
      src/libs/util.ts
  3. 17
      src/views/ContactsView.vue
  4. 2
      src/views/OnboardMeetingSetupView.vue

241
src/components/MembersList.vue

@ -27,37 +27,36 @@
v-if="showOrganizerTools && isOrganizer"
class="inline-flex items-center flex-wrap"
>
<span>Use these next to each person to add/remove them to/from the</span>
<span class="inline-flex items-center whitespace-nowrap">
<span>&nbsp;meeting:</span>
<span class="inline-flex items-center">
Use
<span
class="ml-2 w-6 h-6 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors"
class="mx-2 min-w-[24px] min-h-[24px] w-6 h-6 flex items-center justify-center rounded-full bg-blue-100 text-blue-600"
>
<fa icon="plus" class="text-sm" />
</span>
and
<span
class="ml-2 w-6 h-6 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors"
class="mx-2 min-w-[24px] min-h-[24px] w-6 h-6 flex items-center justify-center rounded-full bg-blue-100 text-blue-600"
>
<fa icon="minus" class="text-sm" />
</span>
to add/remove them to/from the meeting.
</span>
</span>
</div>
<div>
<span class="inline-flex items-center flex-wrap">
<span>Use this next to each person to add them to your</span>
<span class="inline-flex items-center whitespace-nowrap">
<span>&nbsp;contacts:</span>
<span class="inline-flex items-center">
Use
<span
class="ml-2 w-8 h-8 flex items-center justify-center rounded-full bg-green-100 text-green-600"
class="mx-2 w-8 h-8 flex items-center justify-center rounded-full bg-green-100 text-green-600"
>
<fa icon="circle-user" class="text-xl" />
</span>
</span>
to add them to your contacts.
</span>
</div>
<div v-if="members.length > 0" class="flex justify-end">
<div v-if="members.length > 0" class="flex justify-center">
<button
@click="fetchMembers"
class="w-8 h-8 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors"
@ -67,13 +66,37 @@
</button>
</div>
<div
v-for="member in decryptedMembers"
v-for="member in membersToShow()"
:key="member.member.memberId"
class="p-4 bg-gray-50 rounded-lg"
>
<div class="flex items-center justify-between">
<div class="flex">
<div class="flex items-center">
<h3 class="text-lg font-medium">{{ member.name }}</h3>
<div
v-if="!isContactAlready(member.did) && member.did !== activeDid"
class="flex justify-end"
>
<button
@click="addAsContact(member)"
class="ml-2 w-8 h-8 flex items-center justify-center rounded-full bg-green-100 text-green-600 hover:bg-green-200 hover:text-green-800 transition-colors"
title="Add as contact"
>
<fa icon="circle-user" class="text-xl" />
</button>
</div>
<button
v-if="member.did !== activeDid"
@click="
informAboutAddingContact(isContactAlready(member.did))
"
class="ml-2 mb-2 w-6 h-6 flex items-center justify-center rounded-full bg-slate-100 text-slate-500 hover:bg-slate-200 hover:text-slate-800 transition-colors"
title="Contact info"
>
<fa icon="circle-info" class="text-base" />
</button>
</div>
<div class="flex">
<span
v-if="
showOrganizerTools && isOrganizer && member.did !== activeDid
@ -82,7 +105,7 @@
>
<button
@click="toggleAdmission(member)"
class="ml-2 w-6 h-6 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors"
class="mr-2 w-6 h-6 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors"
:title="
member.member.admitted ? 'Remove member' : 'Admit member'
"
@ -94,36 +117,19 @@
</button>
<button
@click="informAboutAdmission()"
class="ml-2 mb-2 w-6 h-6 flex items-center justify-center rounded-full bg-gray-100 text-gray-600 hover:bg-gray-200 hover:text-gray-800 transition-colors"
class="mr-2 mb-2 w-6 h-6 flex items-center justify-center rounded-full bg-slate-100 text-slate-500 hover:bg-slate-200 hover:text-slate-800 transition-colors"
title="Admission info"
>
<fa icon="circle-info" class="text-base" />
</button>
</span>
</div>
<div
v-if="!isContactAlready(member.did) && member.did !== activeDid"
class="flex justify-end"
>
<button
@click="addAsContact(member)"
class="ml-1 w-8 h-8 flex items-center justify-center rounded-full bg-green-100 text-green-600 hover:bg-green-200 hover:text-green-800 transition-colors"
title="Add as contact"
>
<fa icon="circle-user" class="text-xl" />
</button>
<button
@click="informAboutAddingContact()"
class="ml-2 w-6 h-6 flex items-center justify-center rounded-full bg-gray-100 text-gray-600 hover:bg-gray-200 hover:text-gray-800 transition-colors"
title="Contact info"
>
<fa icon="circle-info" class="text-base" />
</button>
</div>
</div>
<p class="text-sm text-gray-600">{{ member.did }}</p>
<p class="text-sm text-gray-600 truncate">
{{ member.did }}
</p>
</div>
<div v-if="members.length > 0" class="flex justify-end mt-4">
<div v-if="members.length > 0" class="flex justify-center mt-4">
<button
@click="fetchMembers"
class="w-8 h-8 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors"
@ -156,6 +162,7 @@ import {
} from "@/libs/endorserServer";
import { decryptMessage } from "@/libs/crypto";
import { Contact } from "@/db/tables/contacts";
import * as libsUtil from "@/libs/util";
interface Member {
admitted: boolean;
@ -177,6 +184,8 @@ export default class MembersList extends Vue {
timeout?: number,
) => void;
libsUtil = libsUtil;
@Prop({ required: true }) password!: string;
@Prop({ default: "Your password failed. Please go back and try again." })
decryptFailureMessage!: string;
@ -192,69 +201,6 @@ export default class MembersList extends Vue {
apiServer = "";
contacts: Array<Contact> = [];
async toggleAdmission(member: DecryptedMember) {
try {
const headers = await getHeaders(this.activeDid);
await this.axios.put(
`${this.apiServer}/api/partner/groupOnboardMember/${member.member.memberId}`,
{ admitted: !member.member.admitted },
{ headers },
);
// Update local state
member.member.admitted = !member.member.admitted;
// if admitted, now register that user if they are not registered
if (member.member.admitted && !member.member.registered) {
const contact = {
did: member.did,
name: member.name,
};
const result = await register(
this.activeDid,
this.apiServer,
this.axios,
contact,
);
if (result.success) {
member.member.registered = true;
await db.contacts.update(member.did, { registered: true });
this.$notify(
{
group: "alert",
type: "success",
title: "Registered",
text: "Besides being admitted, they were also registered.",
},
3000,
);
} else {
const additionalInfo = result.error || "";
this.$notify(
{
group: "alert",
type: "danger",
title: "Registration failed",
text:
"They were admitted, but registration failed. You can try again, or register from your contacts screen. " +
additionalInfo,
},
10000,
);
}
}
} catch (error) {
logConsoleAndDb(
"Error toggling admission: " + errorStringForLog(error),
true,
);
this.$emit(
"error",
serverMessageForUser(error) ||
"Failed to update member admission status.",
);
}
}
async created() {
const settings = await retrieveSettingsForActiveAccount();
this.activeDid = settings.activeDid || "";
@ -327,6 +273,20 @@ export default class MembersList extends Vue {
this.missingMyself = !foundMyself;
}
membersToShow(): DecryptedMember[] {
if (this.isOrganizer) {
if (this.showOrganizerTools) {
return this.decryptedMembers;
} else {
return this.decryptedMembers.filter(
(member: DecryptedMember) => member.member.admitted,
);
}
}
// non-organizers only get visible members from server
return this.decryptedMembers;
}
informAboutAdmission() {
this.$notify(
{
@ -339,17 +299,29 @@ export default class MembersList extends Vue {
);
}
informAboutAddingContact() {
informAboutAddingContact(isContactAlready: boolean) {
if (isContactAlready) {
this.$notify(
{
group: "alert",
type: "info",
title: "Contact info",
text: "This is to add people to your contacts. If you want to remove them, you must do that from the contacts screen.",
title: "Contact Exists",
text: "They are in your contacts. If you want to remove them, you must do that from the contacts screen.",
},
10000,
);
} else {
this.$notify(
{
group: "alert",
type: "info",
title: "Contact Available",
text: "This is to add them to your contacts. If you want to remove them later, you must do that from the contacts screen.",
},
10000,
);
}
}
async loadContacts() {
this.contacts = await db.contacts.toArray();
@ -359,6 +331,69 @@ export default class MembersList extends Vue {
return this.contacts.some((contact) => contact.did === did);
}
async toggleAdmission(member: DecryptedMember) {
try {
const headers = await getHeaders(this.activeDid);
await this.axios.put(
`${this.apiServer}/api/partner/groupOnboardMember/${member.member.memberId}`,
{ admitted: !member.member.admitted },
{ headers },
);
// Update local state
member.member.admitted = !member.member.admitted;
// if admitted, now register that user if they are not registered
if (member.member.admitted && !member.member.registered) {
const contact = {
did: member.did,
name: member.name,
};
const result = await register(
this.activeDid,
this.apiServer,
this.axios,
contact,
);
if (result.success) {
member.member.registered = true;
await db.contacts.update(member.did, { registered: true });
this.$notify(
{
group: "alert",
type: "success",
title: "Registered",
text: "Besides being admitted, they were also registered.",
},
3000,
);
} else {
const additionalInfo = result.error || "";
this.$notify(
{
group: "alert",
type: "danger",
title: "Registration failed",
text:
"They were admitted, but registration failed. You can try again, or register from your contacts screen. " +
additionalInfo,
},
10000,
);
}
}
} catch (error) {
logConsoleAndDb(
"Error toggling admission: " + errorStringForLog(error),
true,
);
this.$emit(
"error",
serverMessageForUser(error) ||
"Failed to update member admission status.",
);
}
}
async addAsContact(member: DecryptedMember) {
try {
const newContact = {

15
src/libs/util.ts

@ -112,6 +112,21 @@ export const isGiveAction = (
return isGiveClaimType(veriClaim.claimType);
};
export const shortDid = (did: string) => {
if (did.startsWith("did:peer:")) {
return (
did.substring(0, "did:peer:".length + 2) +
"..." +
did.substring("did:peer:".length + 18, "did:peer:".length + 25) +
"..."
);
} else if (did.startsWith("did:ethr:")) {
return did.substring(0, "did:ethr:".length + 9) + "...";
} else {
return did.substring(0, did.indexOf(":", 4) + 7) + "...";
}
}
export const nameForDid = (
activeDid: string,
contacts: Array<Contact>,

17
src/views/ContactsView.vue

@ -219,7 +219,7 @@
</router-link>
<span class="ml-4 text-sm overflow-hidden">{{
shortDid(contact.did)
libsUtil.shortDid(contact.did)
}}</span>
</div>
<div class="ml-4 text-sm">
@ -1373,21 +1373,6 @@ export default class ContactsView extends Vue {
});
}
private shortDid(did: string) {
if (did.startsWith("did:peer:")) {
return (
did.substring(0, "did:peer:".length + 2) +
"..." +
did.substring("did:peer:".length + 18, "did:peer:".length + 25) +
"..."
);
} else if (did.startsWith("did:ethr:")) {
return did.substring(0, "did:ethr:".length + 9) + "...";
} else {
return did.substring(0, did.indexOf(":", 4) + 7) + "...";
}
}
private showCopySelectionsInfo() {
this.$notify(
{

2
src/views/OnboardMeetingSetupView.vue

@ -265,7 +265,7 @@ export default class OnboardMeetingView extends Vue {
) => void;
DECRYPT_FAILURE_MESSAGE =
"Unable to decrypt some member information. Check your password, or have them reset theirs.";
"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;

Loading…
Cancel
Save