From d9fc8e734b101e595bdd8d98333333e1a217463b Mon Sep 17 00:00:00 2001 From: Angelo Veltens Date: Wed, 17 Apr 2024 20:27:54 +0200 Subject: [PATCH] feat: detect user sign in and prepare listing address books --- contacts/src/components.d.ts | 62 ++++++++++++++----- contacts/src/components/app.tsx | 7 ++- .../list-address-books/list-address-books.tsx | 12 ++++ .../open-address-book/open-address-book.css | 33 ++++++++++ .../open-address-book/open-address-book.tsx | 48 ++++++++++++++ .../test/open-address-book.spec.tsx | 58 +++++++++++++++++ .../welcome-page/test/welcome-page.spec.tsx | 29 +++++++++ .../components/welcome-page/welcome-page.css | 21 ------- .../components/welcome-page/welcome-page.tsx | 20 +----- 9 files changed, 235 insertions(+), 55 deletions(-) create mode 100644 contacts/src/components/list-address-books/list-address-books.tsx create mode 100644 contacts/src/components/open-address-book/open-address-book.css create mode 100644 contacts/src/components/open-address-book/open-address-book.tsx create mode 100644 contacts/src/components/open-address-book/test/open-address-book.spec.tsx create mode 100644 contacts/src/components/welcome-page/test/welcome-page.spec.tsx diff --git a/contacts/src/components.d.ts b/contacts/src/components.d.ts index 4bffb58..99c29e8 100644 --- a/contacts/src/components.d.ts +++ b/contacts/src/components.d.ts @@ -45,9 +45,15 @@ export namespace Components { interface PosContactsGroupList { "groups": Group[]; } + interface PosContactsListAddressBooks { + "webId": string; + } interface PosContactsLoadingSpinner { "defer": number; } + interface PosContactsOpenAddressBook { + "webId": string | undefined; + } interface PosContactsPhoneNumbers { "phoneNumbers": PhoneNumber[]; } @@ -72,13 +78,13 @@ export interface PosContactsGroupListCustomEvent extends CustomEvent { detail: T; target: HTMLPosContactsGroupListElement; } -export interface PosContactsRouterCustomEvent extends CustomEvent { +export interface PosContactsOpenAddressBookCustomEvent extends CustomEvent { detail: T; - target: HTMLPosContactsRouterElement; + target: HTMLPosContactsOpenAddressBookElement; } -export interface PosContactsWelcomePageCustomEvent extends CustomEvent { +export interface PosContactsRouterCustomEvent extends CustomEvent { detail: T; - target: HTMLPosContactsWelcomePageElement; + target: HTMLPosContactsRouterElement; } declare global { interface HTMLPosContactsAddressBookPageElement extends Components.PosContactsAddressBookPage, HTMLStencilElement { @@ -193,12 +199,35 @@ declare global { prototype: HTMLPosContactsGroupListElement; new (): HTMLPosContactsGroupListElement; }; + interface HTMLPosContactsListAddressBooksElement extends Components.PosContactsListAddressBooks, HTMLStencilElement { + } + var HTMLPosContactsListAddressBooksElement: { + prototype: HTMLPosContactsListAddressBooksElement; + new (): HTMLPosContactsListAddressBooksElement; + }; interface HTMLPosContactsLoadingSpinnerElement extends Components.PosContactsLoadingSpinner, HTMLStencilElement { } var HTMLPosContactsLoadingSpinnerElement: { prototype: HTMLPosContactsLoadingSpinnerElement; new (): HTMLPosContactsLoadingSpinnerElement; }; + interface HTMLPosContactsOpenAddressBookElementEventMap { + "pod-os-contacts:open-address-book": string; + } + interface HTMLPosContactsOpenAddressBookElement extends Components.PosContactsOpenAddressBook, HTMLStencilElement { + addEventListener(type: K, listener: (this: HTMLPosContactsOpenAddressBookElement, ev: PosContactsOpenAddressBookCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLPosContactsOpenAddressBookElement, ev: PosContactsOpenAddressBookCustomEvent) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; + } + var HTMLPosContactsOpenAddressBookElement: { + prototype: HTMLPosContactsOpenAddressBookElement; + new (): HTMLPosContactsOpenAddressBookElement; + }; interface HTMLPosContactsPhoneNumbersElement extends Components.PosContactsPhoneNumbers, HTMLStencilElement { } var HTMLPosContactsPhoneNumbersElement: { @@ -222,18 +251,7 @@ declare global { prototype: HTMLPosContactsRouterElement; new (): HTMLPosContactsRouterElement; }; - interface HTMLPosContactsWelcomePageElementEventMap { - "pod-os-contacts:open-address-book": string; - } interface HTMLPosContactsWelcomePageElement extends Components.PosContactsWelcomePage, HTMLStencilElement { - addEventListener(type: K, listener: (this: HTMLPosContactsWelcomePageElement, ev: PosContactsWelcomePageCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; - addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; - addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; - addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; - removeEventListener(type: K, listener: (this: HTMLPosContactsWelcomePageElement, ev: PosContactsWelcomePageCustomEvent) => any, options?: boolean | EventListenerOptions): void; - removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; - removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; - removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; } var HTMLPosContactsWelcomePageElement: { prototype: HTMLPosContactsWelcomePageElement; @@ -251,7 +269,9 @@ declare global { "pos-contacts-group": HTMLPosContactsGroupElement; "pos-contacts-group-details": HTMLPosContactsGroupDetailsElement; "pos-contacts-group-list": HTMLPosContactsGroupListElement; + "pos-contacts-list-address-books": HTMLPosContactsListAddressBooksElement; "pos-contacts-loading-spinner": HTMLPosContactsLoadingSpinnerElement; + "pos-contacts-open-address-book": HTMLPosContactsOpenAddressBookElement; "pos-contacts-phone-numbers": HTMLPosContactsPhoneNumbersElement; "pos-contacts-router": HTMLPosContactsRouterElement; "pos-contacts-welcome-page": HTMLPosContactsWelcomePageElement; @@ -301,9 +321,16 @@ declare namespace LocalJSX { "groups"?: Group[]; "onPod-os-contacts:group-selected"?: (event: PosContactsGroupListCustomEvent) => void; } + interface PosContactsListAddressBooks { + "webId": string; + } interface PosContactsLoadingSpinner { "defer"?: number; } + interface PosContactsOpenAddressBook { + "onPod-os-contacts:open-address-book"?: (event: PosContactsOpenAddressBookCustomEvent) => void; + "webId"?: string | undefined; + } interface PosContactsPhoneNumbers { "phoneNumbers": PhoneNumber[]; } @@ -311,7 +338,6 @@ declare namespace LocalJSX { "onPod-os:init"?: (event: PosContactsRouterCustomEvent) => void; } interface PosContactsWelcomePage { - "onPod-os-contacts:open-address-book"?: (event: PosContactsWelcomePageCustomEvent) => void; } interface IntrinsicElements { "pos-contacts-address-book-page": PosContactsAddressBookPage; @@ -325,7 +351,9 @@ declare namespace LocalJSX { "pos-contacts-group": PosContactsGroup; "pos-contacts-group-details": PosContactsGroupDetails; "pos-contacts-group-list": PosContactsGroupList; + "pos-contacts-list-address-books": PosContactsListAddressBooks; "pos-contacts-loading-spinner": PosContactsLoadingSpinner; + "pos-contacts-open-address-book": PosContactsOpenAddressBook; "pos-contacts-phone-numbers": PosContactsPhoneNumbers; "pos-contacts-router": PosContactsRouter; "pos-contacts-welcome-page": PosContactsWelcomePage; @@ -346,7 +374,9 @@ declare module "@stencil/core" { "pos-contacts-group": LocalJSX.PosContactsGroup & JSXBase.HTMLAttributes; "pos-contacts-group-details": LocalJSX.PosContactsGroupDetails & JSXBase.HTMLAttributes; "pos-contacts-group-list": LocalJSX.PosContactsGroupList & JSXBase.HTMLAttributes; + "pos-contacts-list-address-books": LocalJSX.PosContactsListAddressBooks & JSXBase.HTMLAttributes; "pos-contacts-loading-spinner": LocalJSX.PosContactsLoadingSpinner & JSXBase.HTMLAttributes; + "pos-contacts-open-address-book": LocalJSX.PosContactsOpenAddressBook & JSXBase.HTMLAttributes; "pos-contacts-phone-numbers": LocalJSX.PosContactsPhoneNumbers & JSXBase.HTMLAttributes; "pos-contacts-router": LocalJSX.PosContactsRouter & JSXBase.HTMLAttributes; "pos-contacts-welcome-page": LocalJSX.PosContactsWelcomePage & JSXBase.HTMLAttributes; diff --git a/contacts/src/components/app.tsx b/contacts/src/components/app.tsx index 5ed1db5..f6750fa 100644 --- a/contacts/src/components/app.tsx +++ b/contacts/src/components/app.tsx @@ -1,4 +1,4 @@ -import { Component, h } from '@stencil/core'; +import { Component, h, Listen } from '@stencil/core'; import '@pod-os/elements'; @@ -6,6 +6,11 @@ import '@pod-os/elements'; tag: 'pos-contacts-app', }) export class App { + @Listen('pod-os:session-changed') + sessionChanged(ev) { + console.log('app session changed', ev.detail); + } + render() { return ( diff --git a/contacts/src/components/list-address-books/list-address-books.tsx b/contacts/src/components/list-address-books/list-address-books.tsx new file mode 100644 index 0000000..1f5b80b --- /dev/null +++ b/contacts/src/components/list-address-books/list-address-books.tsx @@ -0,0 +1,12 @@ +import { Component, h, Prop } from '@stencil/core'; + +@Component({ + tag: 'pos-contacts-list-address-books', +}) +export class ListAddressBooks { + @Prop() + webId!: string; + render() { + return
TODO: list address books of {this.webId}
; + } +} diff --git a/contacts/src/components/open-address-book/open-address-book.css b/contacts/src/components/open-address-book/open-address-book.css new file mode 100644 index 0000000..353392d --- /dev/null +++ b/contacts/src/components/open-address-book/open-address-book.css @@ -0,0 +1,33 @@ +#container { + display: flex; + flex-direction: column; + gap: var(--size-5); + align-items: flex-start; +} + +#sign-in { + display: flex; + gap: var(--size-1); + align-items: center; + color: var(--color-grey-800); + font-weight: var(--weight-light); +} + +button.open { + white-space: nowrap; + font-size: var(--scale-1); + border-radius: var(--radius-xs); + padding: var(--size-2); + background-color: white; + color: var(--pos-primary-color); + border: 1px solid var(--color-blue-700); + display: flex; + align-items: center; + gap: var(--size-2); +} + + +button.open:hover { + background-color: var(--color-grey-50); + filter: brightness(90%); +} diff --git a/contacts/src/components/open-address-book/open-address-book.tsx b/contacts/src/components/open-address-book/open-address-book.tsx new file mode 100644 index 0000000..2f2e5f3 --- /dev/null +++ b/contacts/src/components/open-address-book/open-address-book.tsx @@ -0,0 +1,48 @@ +import { SessionInfo } from '@pod-os/core'; +import { Component, Event, EventEmitter, h, Listen, Prop, State } from '@stencil/core'; + +@Component({ + tag: 'pos-contacts-open-address-book', + shadow: true, + styleUrl: 'open-address-book.css', +}) +export class OpenAddressBook { + @Event({ eventName: 'pod-os-contacts:open-address-book' }) openAddressBook: EventEmitter; + + @State() + sessionInfo: SessionInfo | undefined; + + @Listen('pod-os:session-changed', { target: 'window' }) + sessionChanged(event: CustomEvent) { + this.sessionInfo = event.detail; + } + + @Prop() + webId: string | undefined; + + promptAndOpen() { + const uri = prompt('Please enter URI of an address book', 'http://localhost:3000/alice/public-contacts/index.ttl#this'); + if (uri) { + this.openAddressBook.emit(uri); + } + } + + render() { + return ( +
+ {this.sessionInfo?.isLoggedIn ? ( + + ) : ( +
+ + Sign in to list your address books. +
+ )} + +
+ ); + } +} diff --git a/contacts/src/components/open-address-book/test/open-address-book.spec.tsx b/contacts/src/components/open-address-book/test/open-address-book.spec.tsx new file mode 100644 index 0000000..aacc9f5 --- /dev/null +++ b/contacts/src/components/open-address-book/test/open-address-book.spec.tsx @@ -0,0 +1,58 @@ +import { SessionInfo } from '@pod-os/core'; +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; +import { fireEvent } from '@testing-library/dom'; +import { OpenAddressBook } from '../open-address-book'; + +describe('open address book', () => { + let page; + beforeEach(async () => { + page = await newSpecPage({ + components: [OpenAddressBook], + template: () => , + supportsShadowDom: false, + }); + }); + + it('renders', () => { + expect(page.root).toEqualHtml(` + +
+
+ + Sign in to list your address books. +
+ +
+
`); + }); + + it('lists address books after login', async () => { + const sessionInfo: SessionInfo = { + isLoggedIn: true, + webId: 'https://alice.test/profile/card#me', + sessionId: 'test', + }; + fireEvent( + window, + new CustomEvent('pod-os:session-changed', { + detail: sessionInfo, + }), + ); + await page.waitForChanges(); + expect(page.root).toEqualHtml(` + +
+ + +
+
+ `); + }); +}); diff --git a/contacts/src/components/welcome-page/test/welcome-page.spec.tsx b/contacts/src/components/welcome-page/test/welcome-page.spec.tsx new file mode 100644 index 0000000..b673119 --- /dev/null +++ b/contacts/src/components/welcome-page/test/welcome-page.spec.tsx @@ -0,0 +1,29 @@ +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; +import { WelcomePage } from '../welcome-page'; + +describe('welcome page', () => { + let page; + beforeEach(async () => { + page = await newSpecPage({ + components: [WelcomePage], + template: () => , + supportsShadowDom: false, + }); + }); + + it('allows to sign in and open address book before login', () => { + expect(page.root).toEqualHtml(` + +
+

+ PodOS contacts +

+ +
+
+ +
+
`); + }); +}); diff --git a/contacts/src/components/welcome-page/welcome-page.css b/contacts/src/components/welcome-page/welcome-page.css index 2ae0767..39efa5e 100644 --- a/contacts/src/components/welcome-page/welcome-page.css +++ b/contacts/src/components/welcome-page/welcome-page.css @@ -15,7 +15,6 @@ header { padding: var(--size-10); } - main { grid-area: main; margin: var(--size-10); @@ -24,26 +23,6 @@ main { padding: var(--size-4) } -button.open { - color: white; - font-size: var(--scale-1); - border-radius: var(--radius-xs); - font-weight: var(--weight-bold); - padding: var(--size-4); - background-color: var(--color-blue-700); - border: 1px solid var(--color-blue-500); - box-shadow: var(--shadow-md); - display: flex; - align-items: center; - gap: var(--size-2); -} - - -button.open:hover { - cursor: pointer; - filter: brightness(110%); -} - .toolbar { display: flex; justify-items: center; diff --git a/contacts/src/components/welcome-page/welcome-page.tsx b/contacts/src/components/welcome-page/welcome-page.tsx index 2335898..df8bc06 100644 --- a/contacts/src/components/welcome-page/welcome-page.tsx +++ b/contacts/src/components/welcome-page/welcome-page.tsx @@ -1,4 +1,4 @@ -import { Component, Event, EventEmitter, h, Host } from '@stencil/core'; +import { Component, h, Host } from '@stencil/core'; @Component({ tag: 'pos-contacts-welcome-page', @@ -6,29 +6,15 @@ import { Component, Event, EventEmitter, h, Host } from '@stencil/core'; shadow: true, }) export class WelcomePage { - @Event({ eventName: 'pod-os-contacts:open-address-book' }) openAddressBook: EventEmitter; - - promptAndOpen() { - const uri = prompt('Please enter URI of an address book', 'http://localhost:3000/alice/public-contacts/index.ttl#this'); - if (uri) { - this.openAddressBook.emit(uri); - } - } - render() { return (

PodOS contacts

+
-

Sign in first to open private address books

-
- - -
+
);