Browse Source

fix alert when looking at one's own activity

nostr
Trent Larson 2 months ago
parent
commit
9b86af8508
  1. 7
      CHANGELOG.md
  2. 109
      src/views/DIDView.vue
  3. 18
      test-playwright/00-noid-tests.spec.ts
  4. 22
      test-playwright/testUtils.ts

7
CHANGELOG.md

@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.3.?]
### Added
- Separate 'isRegistered' flag for each account
### Fixed
- Alert when looking at one's own activity if not in contacts.
## [0.3.25] - 2024.08.30 - dcbe02d877aecb4cdef2643d90e6595d246a9f82 ## [0.3.25] - 2024.08.30 - dcbe02d877aecb4cdef2643d90e6595d246a9f82
### Added ### Added
- "Ideas" now jumps directly to giving prompt or contact list. - "Ideas" now jumps directly to giving prompt or contact list.

109
src/views/DIDView.vue

@ -19,14 +19,17 @@
</div> </div>
<!-- Identity Details --> <!-- Identity Details -->
<div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4"> <div
v-if="!!contactFromDid"
class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4"
>
<div> <div>
<h2 class="text-xl font-semibold"> <h2 class="text-xl font-semibold">
{{ contact?.name || "(no name)" }} {{ contactFromDid?.name || "(no name)" }}
<button <button
@click=" @click="
contactEdit = true; contactEdit = true;
contactNewName = contact.name || ''; contactNewName = (contactFromDid?.name as string) || '';
" "
title="Edit" title="Edit"
> >
@ -49,12 +52,15 @@
> >
</div> </div>
<div class="flex justify-center mt-4"> <div class="flex justify-center mt-4">
<span v-if="contact?.profileImageUrl" class="flex justify-between"> <span
v-if="contactFromDid?.profileImageUrl"
class="flex justify-between"
>
<EntityIcon <EntityIcon
:icon-size="96" :icon-size="96"
:profileImageUrl="contact?.profileImageUrl" :profileImageUrl="contactFromDid?.profileImageUrl"
class="inline-block align-text-bottom border border-slate-300 rounded" class="inline-block align-text-bottom border border-slate-300 rounded"
@click="showLargeIdenticonUrl = contact?.profileImageUrl" @click="showLargeIdenticonUrl = contactFromDid?.profileImageUrl"
/> />
</span> </span>
</div> </div>
@ -63,17 +69,21 @@
<div v-if="activeDid" class="flex justify-between"> <div v-if="activeDid" class="flex justify-between">
<div> <div>
<button <button
v-if="contact?.seesMe && contact.did !== activeDid" v-if="
contactFromDid?.seesMe && contactFromDid.did !== activeDid
"
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md" class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
@click="confirmSetVisibility(contact, false)" @click="confirmSetVisibility(contactFromDid, false)"
title="They can see you" title="They can see you"
> >
<fa icon="eye" class="fa-fw" /> <fa icon="eye" class="fa-fw" />
</button> </button>
<button <button
v-else-if="!contact?.seesMe && contact?.did !== activeDid" v-else-if="
!contactFromDid?.seesMe && contactFromDid?.did !== activeDid
"
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md" class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
@click="confirmSetVisibility(contact, true)" @click="confirmSetVisibility(contactFromDid, true)"
title="They cannot see you" title="They cannot see you"
> >
<fa icon="eye-slash" class="fa-fw" /> <fa icon="eye-slash" class="fa-fw" />
@ -83,9 +93,9 @@
<button <button
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md" class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
@click="checkVisibility(contact)" @click="checkVisibility(contactFromDid)"
title="Check Visibility" title="Check Visibility"
v-if="contact?.did !== activeDid" v-if="contactFromDid?.did !== activeDid"
> >
<fa icon="rotate" class="fa-fw" /> <fa icon="rotate" class="fa-fw" />
</button> </button>
@ -94,13 +104,13 @@
</div> </div>
<button <button
@click="confirmRegister(contact)" @click="confirmRegister(contactFromDid)"
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-6 mx-0.5 my-0.5 px-2 py-1.5 rounded-md" class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-6 mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
v-if="contact?.did !== activeDid" v-if="contactFromDid?.did !== activeDid"
title="Registration" title="Registration"
> >
<fa <fa
v-if="contact?.registered" v-if="contactFromDid?.registered"
icon="person-circle-check" icon="person-circle-check"
class="fa-fw" class="fa-fw"
/> />
@ -111,14 +121,14 @@
</div> </div>
<button <button
@click="confirmDeleteContact(contact)" @click="confirmDeleteContact(contactFromDid)"
class="text-sm uppercase bg-gradient-to-b from-rose-500 to-rose-800 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-6 mx-0.5 my-0.5 px-2 py-1.5 rounded-md" class="text-sm uppercase bg-gradient-to-b from-rose-500 to-rose-800 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-6 mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
title="Delete" title="Delete"
> >
<fa icon="trash-can" class="fa-fw" /> <fa icon="trash-can" class="fa-fw" />
</button> </button>
</div> </div>
<div v-if="!contact?.profileImageUrl"> <div v-if="!contactFromDid?.profileImageUrl">
<div>Auto-Generated Icon</div> <div>Auto-Generated Icon</div>
<div class="flex justify-center"> <div class="flex justify-center">
<EntityIcon <EntityIcon
@ -150,6 +160,15 @@
</div> </div>
</div> </div>
</div> </div>
<div v-else class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4">
<!-- !contactFromDid -->
<div>
<h2 class="text-xl font-semibold">
{{ isMyDid ? "You" : "(no name)" }}
</h2>
</div>
</div>
<div v-if="contactEdit" class="dialog-overlay"> <div v-if="contactEdit" class="dialog-overlay">
<div class="dialog"> <div class="dialog">
<h1 class="text-xl font-bold text-center mb-4">Edit Name</h1> <h1 class="text-xl font-bold text-center mb-4">Edit Name</h1>
@ -222,7 +241,8 @@
v-if="!isLoading && claims.length === 0" v-if="!isLoading && claims.length === 0"
class="flex justify-center mt-4" class="flex justify-center mt-4"
> >
<span>They are in no claims visible to you.</span> <span v-if="isMyDid">You have no claims yet.</span>
<span v-else>They are in no claims visible to you.</span>
</div> </div>
</section> </section>
</template> </template>
@ -270,15 +290,15 @@ export default class DIDView extends Vue {
yaml = yaml; yaml = yaml;
activeDid = ""; activeDid = "";
allMyDids: Array<string> = [];
apiServer = ""; apiServer = "";
claims: Array<GenericCredWrapper<GenericVerifiableCredential>> = []; claims: Array<GenericCredWrapper<GenericVerifiableCredential>> = [];
contact: Contact; contactFromDid?: Contact;
contactEdit = false; contactEdit = false;
contactNewName?: string; contactNewName: string = "";
contactYaml = ""; contactYaml = "";
hitEnd = false; hitEnd = false;
isLoading = false; isLoading = false;
isMyDid = false;
searchBox: { name: string; bbox: BoundingBox } | null = null; searchBox: { name: string; bbox: BoundingBox } | null = null;
showDidDetails = false; showDidDetails = false;
showLargeIdenticonId?: string; showLargeIdenticonId?: string;
@ -295,32 +315,23 @@ export default class DIDView extends Vue {
this.apiServer = settings.apiServer || ""; this.apiServer = settings.apiServer || "";
const pathParam = window.location.pathname.substring("/did/".length); const pathParam = window.location.pathname.substring("/did/".length);
let theContact: Contact | undefined;
if (pathParam) { if (pathParam) {
this.viewingDid = decodeURIComponent(pathParam); this.viewingDid = decodeURIComponent(pathParam);
theContact = await db.contacts.get(this.viewingDid); this.contactFromDid = await db.contacts.get(this.viewingDid);
if (this.contactFromDid) {
this.contactYaml = yaml.dump(this.contactFromDid);
} }
if (theContact) {
this.contact = theContact;
} else {
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "No valid claim ID was provided.",
},
-1,
);
return;
}
this.contactYaml = yaml.dump(this.contact);
await this.loadClaimsAbout(); await this.loadClaimsAbout();
await accountsDB.open(); await accountsDB.open();
const allAccounts = await accountsDB.accounts.toArray(); const allAccounts = await accountsDB.accounts.toArray();
this.allMyDids = allAccounts.map((acc) => acc.did); for (const account of allAccounts) {
if (account.did === this.viewingDid) {
this.isMyDid = true;
break;
}
}
}
} }
/** /**
@ -376,7 +387,7 @@ export default class DIDView extends Vue {
title: "Register", title: "Register",
text: text:
"Are you sure you want to register " + "Are you sure you want to register " +
libsUtil.nameForContact(this.contact, false) + libsUtil.nameForContact(this.contactFromDid, false) +
(contact.registered (contact.registered
? " -- especially since they are already marked as registered" ? " -- especially since they are already marked as registered"
: "") + : "") +
@ -557,9 +568,21 @@ export default class DIDView extends Vue {
} }
private async onClickSaveName(newName: string) { private async onClickSaveName(newName: string) {
this.contact.name = newName; if (!this.contactFromDid) {
this.$notify(
{
group: "alert",
type: "danger",
title: "Not A Contact",
text: "First add this on the contact page, then you can edit here.",
},
5000,
);
return;
}
this.contactFromDid.name = newName;
return db.contacts return db.contacts
.update(this.contact.did, { name: newName }) .update(this.contactFromDid.did, { name: newName })
.then(() => (this.contactEdit = false)); .then(() => (this.contactEdit = false));
} }

18
test-playwright/00-noid-tests.spec.ts

@ -1,5 +1,5 @@
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';
import { generateEthrUser, importUser } from './testUtils'; import { deleteContact, generateEthrUser, importUser } from './testUtils';
test('Check activity feed', async ({ page }) => { test('Check activity feed', async ({ page }) => {
// Load app homepage // Load app homepage
@ -123,4 +123,20 @@ test('Check User 0 can register a random person', async ({ page }) => {
// now ensure that alert goes away // now ensure that alert goes away
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss alert await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss alert
await expect(page.getByText('That gift was recorded.')).toBeHidden(); await expect(page.getByText('That gift was recorded.')).toBeHidden();
// now delete the contact to test that pages still do reasonable things
await deleteContact(page, newDid);
// go the activity page for this new person
await page.goto('./did/' + encodeURIComponent(newDid));
let error;
try {
await page.waitForSelector('div[role="alert"]', { timeout: 2000 });
error = new Error('Error alert should not show.');
} catch (error) {
// success
} finally {
if (error) {
throw error;
}
}
}); });

22
test-playwright/testUtils.ts

@ -40,6 +40,22 @@ export async function switchToUser(page: Page, did: string): Promise<void> {
await page.getByRole('code', { name: did }).click(); await page.getByRole('code', { name: did }).click();
} }
function createContactName(did: string): string {
return "User " + did.slice(11, 14);
}
export async function deleteContact(page: Page, did: string): Promise<void> {
await page.goto('./contacts');
const contactName = createContactName(did);
// go to the detail page for this contact
await page.locator(`li[data-testid="contactListItem"] h2:has-text("${contactName}") + a`).click();
// delete the contact
await page.locator('button > svg.fa-trash-can').click();
await page.locator('div[role="alert"] button:has-text("Yes")').click();
// for some reason, .isHidden() (without expect) doesn't work
await expect(page.locator('div[role="alert"] button:has-text("Yes")')).toBeHidden();
}
// Generate a new random user and register them. // Generate a new random user and register them.
// Note that this makes 000 the active user. Use switchToUser to switch to this DID. // Note that this makes 000 the active user. Use switchToUser to switch to this DID.
export async function generateEthrUser(page: Page): Promise<string> { export async function generateEthrUser(page: Page): Promise<string> {
@ -55,10 +71,10 @@ export async function generateEthrUser(page: Page): Promise<string> {
await importUser(page, '000'); // switch to user 000 await importUser(page, '000'); // switch to user 000
await page.goto('./contacts'); await page.goto('./contacts');
const threeChars = newDid.slice(11, 14); const contactName = createContactName(newDid);
await page.getByPlaceholder('URL or DID, Name, Public Key').fill(`${newDid}, User ${threeChars}`); await page.getByPlaceholder('URL or DID, Name, Public Key').fill(`${newDid}, ${contactName}`);
await page.locator('button > svg.fa-plus').click(); await page.locator('button > svg.fa-plus').click();
await page.locator('li', { hasText: threeChars }).click(); await page.locator('li', { hasText: contactName }).click();
// register them // register them
await page.locator('div[role="alert"] button:has-text("Yes")').click(); await page.locator('div[role="alert"] button:has-text("Yes")').click();
// wait for it to disappear because the next steps may depend on alerts being gone // wait for it to disappear because the next steps may depend on alerts being gone

Loading…
Cancel
Save