@things-factory/auth-ui 10.1.183 → 10.1.192

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist-client/components/abstract-auth-page.d.ts +0 -6
  2. package/dist-client/components/abstract-auth-page.js +16 -17
  3. package/dist-client/components/abstract-auth-page.js.map +1 -1
  4. package/dist-client/components/abstract-password-reset.js +1 -11
  5. package/dist-client/components/abstract-password-reset.js.map +1 -1
  6. package/dist-client/components/abstract-sign.d.ts +0 -1
  7. package/dist-client/components/abstract-sign.js +0 -14
  8. package/dist-client/components/abstract-sign.js.map +1 -1
  9. package/dist-client/components/accept-invitation.d.ts +0 -1
  10. package/dist-client/components/accept-invitation.js +1 -12
  11. package/dist-client/components/accept-invitation.js.map +1 -1
  12. package/dist-client/components/toasts-go-through-notify.test.d.ts +1 -0
  13. package/dist-client/components/toasts-go-through-notify.test.js +48 -0
  14. package/dist-client/components/toasts-go-through-notify.test.js.map +1 -0
  15. package/dist-client/entries/auth/activate-page.test.d.ts +1 -0
  16. package/dist-client/entries/auth/activate-page.test.js +26 -0
  17. package/dist-client/entries/auth/activate-page.test.js.map +1 -0
  18. package/dist-client/entries/auth/activate.d.ts +0 -5
  19. package/dist-client/entries/auth/activate.js +23 -24
  20. package/dist-client/entries/auth/activate.js.map +1 -1
  21. package/dist-client/entries/auth/forgot-password.d.ts +0 -4
  22. package/dist-client/entries/auth/forgot-password.js +5 -9
  23. package/dist-client/entries/auth/forgot-password.js.map +1 -1
  24. package/dist-client/tsconfig.tsbuildinfo +1 -1
  25. package/package.json +5 -5
  26. package/translations/en.json +2 -0
  27. package/translations/es.json +2 -0
  28. package/translations/ja.json +2 -0
  29. package/translations/ko.json +2 -0
  30. package/translations/ms.json +2 -0
  31. package/translations/zh.json +2 -0
@@ -28,12 +28,6 @@ export declare abstract class AbstractAuthPage extends AbstractAuthPage_base {
28
28
  _onSubmit(e: any): Promise<boolean>;
29
29
  checkValidity(): boolean;
30
30
  abstract submit(): any;
31
- showSnackbar({ level, message, timer }?: {
32
- level?: string;
33
- message?: string;
34
- timer?: number;
35
- }): void;
36
- hideSnackbar(): void;
37
31
  get applicationMeta(): {
38
32
  icon: string;
39
33
  title: string;
@@ -10,6 +10,8 @@ import '@operato/layout/ox-snack-bar.js';
10
10
  import { css, html, LitElement, nothing } from 'lit';
11
11
  import { property, query } from 'lit/decorators.js';
12
12
  import { i18next, localize } from '@operato/i18n';
13
+ import { CLOSE_SNACKBAR, notify } from '@operato/layout';
14
+ import { store } from '@operato/shell';
13
15
  import { ScrollbarStyles } from '@operato/styles';
14
16
  import { isSafari } from '@operato/utils';
15
17
  import { AUTH_STYLE_SIGN } from '../auth-style-sign.js';
@@ -122,7 +124,7 @@ export class AbstractAuthPage extends localize(i18next)(LitElement) {
122
124
  <md-icon-button class="home" @click=${e => (window.location.href = '/')}
123
125
  ><md-icon>home</md-icon></md-icon-button
124
126
  >
125
- <ox-snack-bar id="snackbar" level="error" .message=${this.message}></ox-snack-bar>
127
+ <ox-snack-bar id="snackbar"></ox-snack-bar>
126
128
 
127
129
  ${isSafari()
128
130
  ? html ``
@@ -148,6 +150,19 @@ export class AbstractAuthPage extends localize(i18next)(LitElement) {
148
150
  this.message = this.data.message;
149
151
  this.redirectTo = this.data.redirectTo;
150
152
  }
153
+ /*
154
+ * `message` is a refusal - the server sends a success to the result page instead. It goes out
155
+ * through notify, the one way a toast is raised; ox-snack-bar draws the store, not properties
156
+ * set on it.
157
+ */
158
+ if (changed.has('message')) {
159
+ if (this.message) {
160
+ notify({ level: 'error', message: this.message });
161
+ }
162
+ else if (changed.get('message')) {
163
+ store.dispatch({ type: CLOSE_SNACKBAR });
164
+ }
165
+ }
151
166
  }
152
167
  get formElements() {
153
168
  return Array.from(this.formEl.querySelectorAll('[name]'));
@@ -224,22 +239,6 @@ export class AbstractAuthPage extends localize(i18next)(LitElement) {
224
239
  checkValidity() {
225
240
  return this.formElements.every(el => el.checkValidity());
226
241
  }
227
- showSnackbar({ level, message, timer = 3000 } = {}) {
228
- const snackbar = this.renderRoot.querySelector('#snackbar');
229
- if (level)
230
- snackbar.level = level;
231
- if (message)
232
- snackbar.message = message;
233
- snackbar.active = true;
234
- if (timer > -1)
235
- setTimeout(() => {
236
- this.hideSnackbar();
237
- }, timer);
238
- }
239
- hideSnackbar() {
240
- const snackbar = this.renderRoot.querySelector('#snackbar');
241
- snackbar.active = false;
242
- }
243
242
  get applicationMeta() {
244
243
  if (!this._applicationMeta) {
245
244
  var iconLink = document.querySelector('link[rel="application-icon"]');
@@ -1 +1 @@
1
- {"version":3,"file":"abstract-auth-page.js","sourceRoot":"","sources":["../../client/components/abstract-auth-page.ts"],"names":[],"mappings":";AAAA,OAAO,4BAA4B,CAAA;AACnC,OAAO,yCAAyC,CAAA;AAChD,OAAO,qCAAqC,CAAA;AAC5C,OAAO,8CAA8C,CAAA;AAErD,OAAO,wBAAwB,CAAA;AAC/B,OAAO,0BAA0B,CAAA;AACjC,OAAO,mCAAmC,CAAA;AAC1C,OAAO,iCAAiC,CAAA;AAExC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACpD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAS,MAAM,mBAAmB,CAAA;AAE1D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAA;AAExE,MAAM,OAAgB,gBAAiB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;aACnE,WAAM,GAAG;QACd,sBAAsB;QACtB,eAAe;QACf,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA+CF;QACD,eAAe;KAChB,CAAA;IAeD,IAAI,eAAe;QACjB,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM;QACJ,MAAM,EAAE,0BAA0B,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAA;QACjE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,eAAe,CAAA;QAEvD,OAAO,IAAI,CAAA;;;;uBAIQ,IAAI;mCACQ,KAAK;wCACA,WAAW;;;;wCAIX,IAAI,CAAC,QAAQ;;;;wBAI7B,IAAI,CAAC,SAAS;;6BAET,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK;0BACtC,CAAC,CAAC,EAAE;YACd,IAAI,CAAC,CAAC,GAAG,IAAI,OAAO;gBAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;QACzC,CAAC;wBACS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE;;gBAE/B,IAAI,CAAC,UAAU;;cAEjB,IAAI,CAAC,KAAK;cACV,CAAC,0BAA0B;YAC3B,CAAC,CAAC,IAAI,CAAA;;;;4BAIQ,OAAO,CAAC,QAAQ,IAAI,OAAO;iCACtB,SAAS;8BACZ,CAAC,CAAC,EAAE;gBACZ,IAAI,MAAM,GAAG,CAAC,CAAC,MAAM,CAAA;gBACrB,IAAI,CAAC,MAAM;oBAAE,OAAM;gBAEnB,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;YAChC,CAAC;;uBAEE;YACT,CAAC,CAAC,OAAO;;;;8CAIuB,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,CAAC;;;6DAGlB,IAAI,CAAC,OAAO;;UAE/D,QAAQ,EAAE;YACV,CAAC,CAAC,IAAI,CAAA,EAAE;YACR,CAAC,CAAC,IAAI,CAAA;;;;aAIH;;KAER,CAAA;IACH,CAAC;IAED,YAAY;QACV,UAAU,CAAC,GAAG,EAAE;YACd,CAAC;YAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,sBAAsB,CAAS,CAAC,KAAK,EAAE,CAAA;QACzE,CAAC,EAAE,GAAG,CAAC,CAAA;QAEP,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,GAAG,EAAE;YACvB,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAA;QACpG,CAAC,CAAA;IACH,CAAC;IAED,OAAO,CAAC,OAAO;QACb,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACrC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAA;YAChC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAA;QACxC,CAAC;IACH,CAAC;IAKD,IAAI,YAAY;QACd,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC3D,CAAC;IAED,IAAI,UAAU;QACZ,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAA;QAC1C,mEAAmE;QAEnE,OAAO,IAAI,CAAA;sEACuD,IAAI,CAAC,UAAU,IAAI,GAAG;;;;;;kBAM1E,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC;;mBAE1C,QAAQ;;;;;;;;;;kBAUT,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;;;;;;;mEAOc,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;gCACzD,IAAI,CAAC,QAAQ;;KAExC,CAAA;IACH,CAAC;IAED,IAAI,KAAK;QACP,MAAM,EAAE,wBAAwB,EAAE,QAAQ,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAA;QAEnE,OAAO,IAAI,CAAA;QACP,CAAC,wBAAwB;YACzB,CAAC,CAAC,IAAI,CAAA;;;;;;;;;;;;;WAaH;YACH,CAAC,CAAC,OAAO;QACT,QAAQ,CAAC,GAAG,CACZ,GAAG,CAAC,EAAE,CAAC,IAAI,CAAA;iCACc,GAAG,CAAC,IAAI;;;gBAGzB,OAAO,CAAC,CAAC,CAAC,mBAAmB,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;;;SAG3D,CACF;KACF,CAAA;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,CAAC;QACf,IAAI,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,EAAE,CAAA;QACf,CAAC;QAED,OAAO,KAAK,CAAA;IACd,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,CAAA;IAC1D,CAAC;IAID,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,GAAG,IAAI,KAA2D,EAAE;QACtG,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAIzD,CAAA;QAED,IAAI,KAAK;YAAE,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAA;QACjC,IAAI,OAAO;YAAE,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAA;QACvC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAA;QAEtB,IAAI,KAAK,GAAG,CAAC,CAAC;YACZ,UAAU,CAAC,GAAG,EAAE;gBACd,IAAI,CAAC,YAAY,EAAE,CAAA;YACrB,CAAC,EAAE,KAAK,CAAC,CAAA;IACb,CAAC;IAED,YAAY;QACV,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAIzD,CAAA;QAED,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAA;IACzB,CAAC;IAED,IAAI,eAAe;QACjB,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3B,IAAI,QAAQ,GAA2B,QAAQ,CAAC,aAAa,CAAC,8BAA8B,CAAC,CAAA;YAC7F,IAAI,SAAS,GAA2B,QAAQ,CAAC,aAAa,CAAC,+BAA+B,CAAC,CAAA;YAC/F,IAAI,eAAe,GAA2B,QAAQ,CAAC,aAAa,CAAC,sCAAsC,CAAC,CAAA;YAE5G,IAAI,CAAC,gBAAgB,GAAG;gBACtB,IAAI,EAAE,QAAQ,EAAE,IAAI,IAAI,EAAE;gBAC1B,KAAK,EAAE,SAAS,EAAE,OAAO,IAAI,gBAAgB;gBAC7C,WAAW,EAAE,eAAe,EAAE,OAAO,IAAI,sBAAsB;aAChE,CAAA;QACH,CAAC;QAED,OAAO,IAAI,CAAC,gBAAgB,CAAA;IAC9B,CAAC;;AArO2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;8CAAU;AACT;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;iDAAiB;AAChB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;gDAAY;AACX;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;oDAAoB;AAE/B;IAAf,KAAK,CAAC,OAAO,CAAC;8BAAU,eAAe;gDAAA","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/textfield/filled-text-field.js'\n\nimport '@operato/lottie-player'\nimport '@operato/i18n/ox-i18n.js'\nimport '@operato/i18n/ox-i18n-selector.js'\nimport '@operato/layout/ox-snack-bar.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { property, query, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { isSafari } from '@operato/utils'\n\nimport { AUTH_STYLE_SIGN } from '../auth-style-sign.js'\nimport { EntryControlSizeStyles } from '../entry-control-size-styles.js'\n\nexport abstract class AbstractAuthPage extends localize(i18next)(LitElement) {\n static styles = [\n EntryControlSizeStyles,\n ScrollbarStyles,\n css`\n :host {\n position: relative;\n overflow: hidden;\n\n display: flex;\n flex-direction: row;\n\n width: 100vw;\n height: 100vh;\n height: 100dvh;\n }\n\n .content {\n flex: 1;\n overflow: auto;\n }\n\n .home {\n position: absolute;\n padding: var(--padding-dufault, 9px);\n left: 20px;\n top: 10px;\n color: var(--md-sys-color-on-primary);\n }\n\n div.field {\n margin-bottom: var(--spacing-medium);\n }\n\n [hidden] {\n display: none;\n }\n\n #snackbar {\n width: 100%;\n z-index: 10;\n }\n\n @media print {\n :host {\n width: 100%;\n height: 100%;\n min-height: 100vh;\n min-height: 100dvh;\n }\n }\n `,\n AUTH_STYLE_SIGN\n ]\n\n @property({ type: Object }) data: any\n @property({ type: String }) message?: string\n @property({ type: Object }) detail: any\n @property({ type: String }) redirectTo?: string\n\n @query('#form') formEl!: HTMLFormElement\n\n private _applicationMeta?: {\n icon: string\n title: string\n description: string\n }\n\n get autocompletable() {\n return false\n }\n\n render() {\n const { disableUserFavoredLanguage, languages } = this.data || {}\n var { icon, title, description } = this.applicationMeta\n\n return html`\n <div class=\"content md-typescale-display-medium\">\n <div class=\"wrap\">\n <div class=\"auth-brand\">\n <img src=${icon} />\n <strong class=\"name\">${title}</strong>\n <span class=\"welcome-msg\">${description}</span>\n </div>\n\n <div class=\"auth-form\">\n <h3><ox-i18n msgid=\"title.${this.pageName}\"></ox-i18n></h3>\n\n <form\n id=\"form\"\n action=\"${this.actionUrl}\"\n method=\"post\"\n autocomplete=${this.autocompletable ? 'on' : 'off'}\n @keypress=${e => {\n if (e.key == 'Enter') this._onSubmit(e)\n }}\n @submit=${e => e.preventDefault()}\n >\n ${this.formfields}\n </form>\n ${this.links}\n ${!disableUserFavoredLanguage\n ? html` <div id=\"locale-area\">\n <label for=\"locale-selector\"><md-icon>language</md-icon></label>\n <ox-i18n-selector\n id=\"locale-selector\"\n value=${i18next.language || 'en-US'}\n .languages=${languages}\n @change=${e => {\n var locale = e.detail\n if (!locale) return\n\n i18next.changeLanguage(locale)\n }}\n ></ox-i18n-selector>\n </div>`\n : nothing}\n </div>\n </div>\n\n <md-icon-button class=\"home\" @click=${e => (window.location.href = '/')}\n ><md-icon>home</md-icon></md-icon-button\n >\n <ox-snack-bar id=\"snackbar\" level=\"error\" .message=${this.message}></ox-snack-bar>\n\n ${isSafari()\n ? html``\n : html`\n <div class=\"lottie-container\">\n <lottie-player autoplay loop src=\"../../assets/images/background-animation.json\"></lottie-player>\n </div>\n `}\n </div>\n `\n }\n\n firstUpdated() {\n setTimeout(() => {\n ;(this.renderRoot.querySelector('md-filled-text-field') as any).focus()\n }, 100)\n\n this.formEl.reset = () => {\n this.formElements.filter(el => !(el.hidden || el.type == 'hidden')).forEach(el => (el.value = ''))\n }\n }\n\n updated(changed) {\n if (changed.has('data') && this.data) {\n this.message = this.data.message\n this.redirectTo = this.data.redirectTo\n }\n }\n\n abstract get pageName(): string\n abstract get actionUrl(): string\n\n get formElements(): HTMLInputElement[] {\n return Array.from(this.formEl.querySelectorAll('[name]'))\n }\n\n get formfields() {\n const username = this.data?.username || ''\n // .validationMessage=${String(i18next.t('text.invalid-username'))}\n\n return html`\n <input id=\"redirectTo\" type=\"hidden\" name=\"redirectTo\" .value=${this.redirectTo || '/'} />\n\n <div class=\"field\">\n <md-filled-text-field\n name=\"username\"\n type=\"text\"\n label=${String(i18next.t('field.user-id or email'))}\n required\n .value=${username}\n autocomplete=\"off\"\n autocapitalize=\"off\"\n ><md-icon slot=\"leading-icon\">id_card</md-icon></md-filled-text-field\n >\n </div>\n <div class=\"field\">\n <md-filled-text-field\n name=\"password\"\n type=\"password\"\n label=${String(i18next.t('field.password'))}\n autocomplete=\"off\"\n required\n ><md-icon slot=\"leading-icon\">password</md-icon></md-filled-text-field\n >\n </div>\n\n <md-elevated-button class=\"ui\" type=\"button\" raised @click=${e => this._onSubmit(e)}>\n <ox-i18n msgid=\"field.${this.pageName}\"> </ox-i18n>\n </md-elevated-button>\n `\n }\n\n get links() {\n const { disableUserSignupProcess, ssoLinks = [] } = this.data || {}\n\n return html`\n ${!disableUserSignupProcess\n ? html`\n <a class=\"link\" href=\"/auth/signup\">\n <md-text-button>\n <md-icon slot=\"icon\">add_task</md-icon>\n <ox-i18n msgid=\"field.sign up\"></ox-i18n>\n </md-text-button>\n </a>\n <a class=\"link\" href=\"/auth/forgot-password\">\n <md-text-button>\n <md-icon slot=\"icon\">lock_open</md-icon>\n <ox-i18n msgid=\"field.forgot-password\"></ox-i18n>\n </md-text-button>\n </a>\n `\n : nothing}\n ${ssoLinks.map(\n sso => html`\n <a class=\"link\" href=${sso.link}>\n <md-text-button>\n <md-icon slot=\"icon\">id_card</md-icon>\n ${i18next.t('label.signin with', { title: sso.title })}\n </md-text-button>\n </a>\n `\n )}\n `\n }\n\n async _onSubmit(e) {\n if (this.checkValidity()) {\n this.submit()\n }\n\n return false\n }\n\n checkValidity() {\n return this.formElements.every(el => el.checkValidity())\n }\n\n abstract submit()\n\n showSnackbar({ level, message, timer = 3000 }: { level?: string; message?: string; timer?: number } = {}) {\n const snackbar = this.renderRoot.querySelector('#snackbar') as HTMLElement & {\n level: string\n message: string\n active: boolean\n }\n\n if (level) snackbar.level = level\n if (message) snackbar.message = message\n snackbar.active = true\n\n if (timer > -1)\n setTimeout(() => {\n this.hideSnackbar()\n }, timer)\n }\n\n hideSnackbar() {\n const snackbar = this.renderRoot.querySelector('#snackbar') as HTMLElement & {\n level: string\n message: string\n active: boolean\n }\n\n snackbar.active = false\n }\n\n get applicationMeta() {\n if (!this._applicationMeta) {\n var iconLink: HTMLLinkElement | null = document.querySelector('link[rel=\"application-icon\"]')\n var titleMeta: HTMLMetaElement | null = document.querySelector('meta[name=\"application-name\"]')\n var descriptionMeta: HTMLMetaElement | null = document.querySelector('meta[name=\"application-description\"]')\n\n this._applicationMeta = {\n icon: iconLink?.href || '',\n title: titleMeta?.content || 'Things Factory',\n description: descriptionMeta?.content || 'Reimagining Software'\n }\n }\n\n return this._applicationMeta\n }\n}\n"]}
1
+ {"version":3,"file":"abstract-auth-page.js","sourceRoot":"","sources":["../../client/components/abstract-auth-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/textfield/filled-text-field.js'\n\nimport '@operato/lottie-player'\nimport '@operato/i18n/ox-i18n.js'\nimport '@operato/i18n/ox-i18n-selector.js'\nimport '@operato/layout/ox-snack-bar.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { property, query, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { CLOSE_SNACKBAR, notify } from '@operato/layout'\nimport { store } from '@operato/shell'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { isSafari } from '@operato/utils'\n\nimport { AUTH_STYLE_SIGN } from '../auth-style-sign.js'\nimport { EntryControlSizeStyles } from '../entry-control-size-styles.js'\n\nexport abstract class AbstractAuthPage extends localize(i18next)(LitElement) {\n static styles = [\n EntryControlSizeStyles,\n ScrollbarStyles,\n css`\n :host {\n position: relative;\n overflow: hidden;\n\n display: flex;\n flex-direction: row;\n\n width: 100vw;\n height: 100vh;\n height: 100dvh;\n }\n\n .content {\n flex: 1;\n overflow: auto;\n }\n\n .home {\n position: absolute;\n padding: var(--padding-dufault, 9px);\n left: 20px;\n top: 10px;\n color: var(--md-sys-color-on-primary);\n }\n\n div.field {\n margin-bottom: var(--spacing-medium);\n }\n\n [hidden] {\n display: none;\n }\n\n #snackbar {\n width: 100%;\n z-index: 10;\n }\n\n @media print {\n :host {\n width: 100%;\n height: 100%;\n min-height: 100vh;\n min-height: 100dvh;\n }\n }\n `,\n AUTH_STYLE_SIGN\n ]\n\n @property({ type: Object }) data: any\n @property({ type: String }) message?: string\n @property({ type: Object }) detail: any\n @property({ type: String }) redirectTo?: string\n\n @query('#form') formEl!: HTMLFormElement\n\n private _applicationMeta?: {\n icon: string\n title: string\n description: string\n }\n\n get autocompletable() {\n return false\n }\n\n render() {\n const { disableUserFavoredLanguage, languages } = this.data || {}\n var { icon, title, description } = this.applicationMeta\n\n return html`\n <div class=\"content md-typescale-display-medium\">\n <div class=\"wrap\">\n <div class=\"auth-brand\">\n <img src=${icon} />\n <strong class=\"name\">${title}</strong>\n <span class=\"welcome-msg\">${description}</span>\n </div>\n\n <div class=\"auth-form\">\n <h3><ox-i18n msgid=\"title.${this.pageName}\"></ox-i18n></h3>\n\n <form\n id=\"form\"\n action=\"${this.actionUrl}\"\n method=\"post\"\n autocomplete=${this.autocompletable ? 'on' : 'off'}\n @keypress=${e => {\n if (e.key == 'Enter') this._onSubmit(e)\n }}\n @submit=${e => e.preventDefault()}\n >\n ${this.formfields}\n </form>\n ${this.links}\n ${!disableUserFavoredLanguage\n ? html` <div id=\"locale-area\">\n <label for=\"locale-selector\"><md-icon>language</md-icon></label>\n <ox-i18n-selector\n id=\"locale-selector\"\n value=${i18next.language || 'en-US'}\n .languages=${languages}\n @change=${e => {\n var locale = e.detail\n if (!locale) return\n\n i18next.changeLanguage(locale)\n }}\n ></ox-i18n-selector>\n </div>`\n : nothing}\n </div>\n </div>\n\n <md-icon-button class=\"home\" @click=${e => (window.location.href = '/')}\n ><md-icon>home</md-icon></md-icon-button\n >\n <ox-snack-bar id=\"snackbar\"></ox-snack-bar>\n\n ${isSafari()\n ? html``\n : html`\n <div class=\"lottie-container\">\n <lottie-player autoplay loop src=\"../../assets/images/background-animation.json\"></lottie-player>\n </div>\n `}\n </div>\n `\n }\n\n firstUpdated() {\n setTimeout(() => {\n ;(this.renderRoot.querySelector('md-filled-text-field') as any).focus()\n }, 100)\n\n this.formEl.reset = () => {\n this.formElements.filter(el => !(el.hidden || el.type == 'hidden')).forEach(el => (el.value = ''))\n }\n }\n\n updated(changed) {\n if (changed.has('data') && this.data) {\n this.message = this.data.message\n this.redirectTo = this.data.redirectTo\n }\n\n /*\n * `message` is a refusal - the server sends a success to the result page instead. It goes out\n * through notify, the one way a toast is raised; ox-snack-bar draws the store, not properties\n * set on it.\n */\n if (changed.has('message')) {\n if (this.message) {\n notify({ level: 'error', message: this.message })\n } else if (changed.get('message')) {\n store.dispatch({ type: CLOSE_SNACKBAR })\n }\n }\n }\n\n abstract get pageName(): string\n abstract get actionUrl(): string\n\n get formElements(): HTMLInputElement[] {\n return Array.from(this.formEl.querySelectorAll('[name]'))\n }\n\n get formfields() {\n const username = this.data?.username || ''\n // .validationMessage=${String(i18next.t('text.invalid-username'))}\n\n return html`\n <input id=\"redirectTo\" type=\"hidden\" name=\"redirectTo\" .value=${this.redirectTo || '/'} />\n\n <div class=\"field\">\n <md-filled-text-field\n name=\"username\"\n type=\"text\"\n label=${String(i18next.t('field.user-id or email'))}\n required\n .value=${username}\n autocomplete=\"off\"\n autocapitalize=\"off\"\n ><md-icon slot=\"leading-icon\">id_card</md-icon></md-filled-text-field\n >\n </div>\n <div class=\"field\">\n <md-filled-text-field\n name=\"password\"\n type=\"password\"\n label=${String(i18next.t('field.password'))}\n autocomplete=\"off\"\n required\n ><md-icon slot=\"leading-icon\">password</md-icon></md-filled-text-field\n >\n </div>\n\n <md-elevated-button class=\"ui\" type=\"button\" raised @click=${e => this._onSubmit(e)}>\n <ox-i18n msgid=\"field.${this.pageName}\"> </ox-i18n>\n </md-elevated-button>\n `\n }\n\n get links() {\n const { disableUserSignupProcess, ssoLinks = [] } = this.data || {}\n\n return html`\n ${!disableUserSignupProcess\n ? html`\n <a class=\"link\" href=\"/auth/signup\">\n <md-text-button>\n <md-icon slot=\"icon\">add_task</md-icon>\n <ox-i18n msgid=\"field.sign up\"></ox-i18n>\n </md-text-button>\n </a>\n <a class=\"link\" href=\"/auth/forgot-password\">\n <md-text-button>\n <md-icon slot=\"icon\">lock_open</md-icon>\n <ox-i18n msgid=\"field.forgot-password\"></ox-i18n>\n </md-text-button>\n </a>\n `\n : nothing}\n ${ssoLinks.map(\n sso => html`\n <a class=\"link\" href=${sso.link}>\n <md-text-button>\n <md-icon slot=\"icon\">id_card</md-icon>\n ${i18next.t('label.signin with', { title: sso.title })}\n </md-text-button>\n </a>\n `\n )}\n `\n }\n\n async _onSubmit(e) {\n if (this.checkValidity()) {\n this.submit()\n }\n\n return false\n }\n\n checkValidity() {\n return this.formElements.every(el => el.checkValidity())\n }\n\n abstract submit()\n\n get applicationMeta() {\n if (!this._applicationMeta) {\n var iconLink: HTMLLinkElement | null = document.querySelector('link[rel=\"application-icon\"]')\n var titleMeta: HTMLMetaElement | null = document.querySelector('meta[name=\"application-name\"]')\n var descriptionMeta: HTMLMetaElement | null = document.querySelector('meta[name=\"application-description\"]')\n\n this._applicationMeta = {\n icon: iconLink?.href || '',\n title: titleMeta?.content || 'Things Factory',\n description: descriptionMeta?.content || 'Reimagining Software'\n }\n }\n\n return this._applicationMeta\n }\n}\n"]}
@@ -135,7 +135,7 @@ export class AbstractPasswordReset extends AbstractAuthPage {
135
135
  </div>
136
136
  </div>
137
137
 
138
- <ox-snack-bar id="snackbar" level="error" .message=${this.message}></ox-snack-bar>
138
+ <ox-snack-bar id="snackbar"></ox-snack-bar>
139
139
  `;
140
140
  }
141
141
  updated(changed) {
@@ -144,16 +144,6 @@ export class AbstractPasswordReset extends AbstractAuthPage {
144
144
  if (changed.has('data') && this.data?.token) {
145
145
  this.token = this.data.token;
146
146
  }
147
- if (changed.has('message')) {
148
- if (!this.message) {
149
- this.hideSnackbar();
150
- }
151
- else {
152
- this.showSnackbar({
153
- timer: -1
154
- });
155
- }
156
- }
157
147
  }
158
148
  languageUpdated() {
159
149
  this.passwordPattern = generatePasswordPatternRegExp(this.data.passwordRule).source;
@@ -1 +1 @@
1
- {"version":3,"file":"abstract-password-reset.js","sourceRoot":"","sources":["../../client/components/abstract-password-reset.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\nimport '@material/web/textfield/filled-text-field.js'\n\nimport '@operato/i18n/ox-i18n.js'\nimport '@operato/i18n/ox-i18n-selector.js'\nimport '@operato/layout/ox-snack-bar.js'\n\nimport '../components/profile-component.js'\n\nimport { css, html, nothing } from 'lit'\nimport { property, query } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { AUTH_STYLE_SIGN } from '../auth-style-sign.js'\nimport { generatePasswordPatternHelp, generatePasswordPatternRegExp } from '../utils/password-rule.js'\nimport { AbstractAuthPage } from './abstract-auth-page.js'\nimport { readLinkToken } from '../utils/link-token.js'\nimport { EntryControlSizeStyles } from '../entry-control-size-styles.js'\n\nexport abstract class AbstractPasswordReset extends AbstractAuthPage {\n static styles = [\n EntryControlSizeStyles,\n css`\n :host {\n position: relative;\n overflow: hidden;\n\n display: flex;\n flex-direction: row;\n\n width: 100vw;\n height: 100vh;\n height: 100dvh;\n }\n\n @media print {\n :host {\n width: 100%;\n height: 100%;\n min-height: 100vh;\n min-height: 100dvh;\n }\n }\n `,\n AUTH_STYLE_SIGN\n ]\n\n @property({ type: Object }) data: any\n @property({ type: String }) token?: string\n\n @query('#confirm-password') confirmPass!: HTMLElement\n\n private passwordPattern: string = ''\n private passwordHelp: string = ''\n\n abstract get submitButtonLabel(): string\n\n /**\n * The mailed link is `…#<token>`: the browser does not send what follows `#`, so the token stays\n * out of server and proxy logs. Taken out of the address bar here as well — `replaceState`, so\n * going back does not put it on screen.\n */\n connectedCallback() {\n super.connectedCallback()\n\n const { token, clean } = readLinkToken(window.location.href)\n\n this.token = token\n\n if (clean && clean !== window.location.href) {\n window.history.replaceState(null, '', clean)\n }\n }\n\n render() {\n var { icon, title, description } = this.applicationMeta\n const { disableUserFavoredLanguage, languages } = this.data || {}\n\n return html`\n <div class=\"wrap\">\n <div class=\"auth-brand\">\n <img src=${icon} />\n <strong class=\"name\">${title}</strong>\n <span class=\"welcome-msg\">${description}</span>\n </div>\n\n <div class=\"auth-form\">\n <h3><ox-i18n msgid=\"title.${this.title}\"></ox-i18n></h3>\n <form\n id=\"form\"\n action=\"${this.actionUrl}\"\n method=\"post\"\n @keypress=${e => {\n if (e.key == 'Enter') this._onSubmit(e)\n }}\n >\n <input name=\"token\" type=\"hidden\" .value=${this.token || ''} required />\n <div class=\"field\">\n <md-filled-text-field\n name=\"password\"\n type=\"password\"\n label=${String(i18next.t('label.password'))}\n pattern=${this.passwordPattern || ''}\n supporting-text=${this.passwordHelp}\n autocomplete=\"off\"\n @input=${e => {\n var val = e.target.value\n this.confirmPass.setAttribute('pattern', val.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '[$&]'))\n }}\n required\n ><md-icon slot=\"leading-icon\">password</md-icon></md-filled-text-field\n >\n </div>\n\n <div class=\"field\">\n <md-filled-text-field\n id=\"confirm-password\"\n name=\"confirm-password\"\n type=\"password\"\n label=${String(i18next.t('field.confirm password'))}\n autocomplete=\"off\"\n required\n ><md-icon slot=\"leading-icon\">password</md-icon></md-filled-text-field\n >\n </div>\n\n <md-elevated-button id=\"submit-button\" type=\"button\" @click=${e => this._onSubmit(e)}>\n <ox-i18n msgid=\"${this.submitButtonLabel}\"></ox-i18n>\n </md-elevated-button>\n\n ${!disableUserFavoredLanguage\n ? html` <div id=\"locale-area\">\n <label for=\"locale-selector\"><md-icon>language</md-icon></label>\n <ox-i18n-selector\n id=\"locale-selector\"\n value=${i18next.language || 'en-US'}\n .languages=${languages}\n @change=${e => {\n var locale = e.detail\n if (!locale) return\n\n i18next.changeLanguage(locale)\n }}\n ></ox-i18n-selector>\n </div>`\n : nothing}\n </form>\n </div>\n </div>\n\n <ox-snack-bar id=\"snackbar\" level=\"error\" .message=${this.message}></ox-snack-bar>\n `\n }\n\n updated(changed) {\n super.updated(changed)\n\n /* A refused post renders the form again with the token it carried, in the page, not the address. */\n if (changed.has('data') && this.data?.token) {\n this.token = this.data.token\n }\n\n if (changed.has('message')) {\n if (!this.message) {\n this.hideSnackbar()\n } else {\n this.showSnackbar({\n timer: -1\n })\n }\n }\n }\n\n languageUpdated() {\n this.passwordPattern = generatePasswordPatternRegExp(this.data.passwordRule).source\n this.passwordHelp = generatePasswordPatternHelp(this.data.passwordRule)\n }\n\n async submit() {\n this.formEl.submit()\n }\n}\n"]}
1
+ {"version":3,"file":"abstract-password-reset.js","sourceRoot":"","sources":["../../client/components/abstract-password-reset.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\nimport '@material/web/textfield/filled-text-field.js'\n\nimport '@operato/i18n/ox-i18n.js'\nimport '@operato/i18n/ox-i18n-selector.js'\nimport '@operato/layout/ox-snack-bar.js'\n\nimport '../components/profile-component.js'\n\nimport { css, html, nothing } from 'lit'\nimport { property, query } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { AUTH_STYLE_SIGN } from '../auth-style-sign.js'\nimport { generatePasswordPatternHelp, generatePasswordPatternRegExp } from '../utils/password-rule.js'\nimport { AbstractAuthPage } from './abstract-auth-page.js'\nimport { readLinkToken } from '../utils/link-token.js'\nimport { EntryControlSizeStyles } from '../entry-control-size-styles.js'\n\nexport abstract class AbstractPasswordReset extends AbstractAuthPage {\n static styles = [\n EntryControlSizeStyles,\n css`\n :host {\n position: relative;\n overflow: hidden;\n\n display: flex;\n flex-direction: row;\n\n width: 100vw;\n height: 100vh;\n height: 100dvh;\n }\n\n @media print {\n :host {\n width: 100%;\n height: 100%;\n min-height: 100vh;\n min-height: 100dvh;\n }\n }\n `,\n AUTH_STYLE_SIGN\n ]\n\n @property({ type: Object }) data: any\n @property({ type: String }) token?: string\n\n @query('#confirm-password') confirmPass!: HTMLElement\n\n private passwordPattern: string = ''\n private passwordHelp: string = ''\n\n abstract get submitButtonLabel(): string\n\n /**\n * The mailed link is `…#<token>`: the browser does not send what follows `#`, so the token stays\n * out of server and proxy logs. Taken out of the address bar here as well — `replaceState`, so\n * going back does not put it on screen.\n */\n connectedCallback() {\n super.connectedCallback()\n\n const { token, clean } = readLinkToken(window.location.href)\n\n this.token = token\n\n if (clean && clean !== window.location.href) {\n window.history.replaceState(null, '', clean)\n }\n }\n\n render() {\n var { icon, title, description } = this.applicationMeta\n const { disableUserFavoredLanguage, languages } = this.data || {}\n\n return html`\n <div class=\"wrap\">\n <div class=\"auth-brand\">\n <img src=${icon} />\n <strong class=\"name\">${title}</strong>\n <span class=\"welcome-msg\">${description}</span>\n </div>\n\n <div class=\"auth-form\">\n <h3><ox-i18n msgid=\"title.${this.title}\"></ox-i18n></h3>\n <form\n id=\"form\"\n action=\"${this.actionUrl}\"\n method=\"post\"\n @keypress=${e => {\n if (e.key == 'Enter') this._onSubmit(e)\n }}\n >\n <input name=\"token\" type=\"hidden\" .value=${this.token || ''} required />\n <div class=\"field\">\n <md-filled-text-field\n name=\"password\"\n type=\"password\"\n label=${String(i18next.t('label.password'))}\n pattern=${this.passwordPattern || ''}\n supporting-text=${this.passwordHelp}\n autocomplete=\"off\"\n @input=${e => {\n var val = e.target.value\n this.confirmPass.setAttribute('pattern', val.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '[$&]'))\n }}\n required\n ><md-icon slot=\"leading-icon\">password</md-icon></md-filled-text-field\n >\n </div>\n\n <div class=\"field\">\n <md-filled-text-field\n id=\"confirm-password\"\n name=\"confirm-password\"\n type=\"password\"\n label=${String(i18next.t('field.confirm password'))}\n autocomplete=\"off\"\n required\n ><md-icon slot=\"leading-icon\">password</md-icon></md-filled-text-field\n >\n </div>\n\n <md-elevated-button id=\"submit-button\" type=\"button\" @click=${e => this._onSubmit(e)}>\n <ox-i18n msgid=\"${this.submitButtonLabel}\"></ox-i18n>\n </md-elevated-button>\n\n ${!disableUserFavoredLanguage\n ? html` <div id=\"locale-area\">\n <label for=\"locale-selector\"><md-icon>language</md-icon></label>\n <ox-i18n-selector\n id=\"locale-selector\"\n value=${i18next.language || 'en-US'}\n .languages=${languages}\n @change=${e => {\n var locale = e.detail\n if (!locale) return\n\n i18next.changeLanguage(locale)\n }}\n ></ox-i18n-selector>\n </div>`\n : nothing}\n </form>\n </div>\n </div>\n\n <ox-snack-bar id=\"snackbar\"></ox-snack-bar>\n `\n }\n\n updated(changed) {\n super.updated(changed)\n\n /* A refused post renders the form again with the token it carried, in the page, not the address. */\n if (changed.has('data') && this.data?.token) {\n this.token = this.data.token\n }\n }\n\n languageUpdated() {\n this.passwordPattern = generatePasswordPatternRegExp(this.data.passwordRule).source\n this.passwordHelp = generatePasswordPatternHelp(this.data.passwordRule)\n }\n\n async submit() {\n this.formEl.submit()\n }\n}\n"]}
@@ -2,7 +2,6 @@ import '@operato/i18n/ox-i18n.js';
2
2
  import { AbstractAuthPage } from './abstract-auth-page.js';
3
3
  export declare abstract class AbstractSign extends AbstractAuthPage {
4
4
  submit(): Promise<void>;
5
- updated(changed: any): void;
6
5
  get formfields(): import("lit-html").TemplateResult<1>;
7
6
  authenticateUser(): Promise<void>;
8
7
  }
@@ -9,20 +9,6 @@ export class AbstractSign extends AbstractAuthPage {
9
9
  async submit() {
10
10
  this.formEl.submit();
11
11
  }
12
- updated(changed) {
13
- super.updated(changed);
14
- if (changed.has('message')) {
15
- if (!this.message) {
16
- this.hideSnackbar();
17
- }
18
- else {
19
- this.showSnackbar({
20
- level: 'error',
21
- timer: -1
22
- });
23
- }
24
- }
25
- }
26
12
  get formfields() {
27
13
  const username = this.data?.username || '';
28
14
  const autocompletable = this.autocompletable;
@@ -1 +1 @@
1
- {"version":3,"file":"abstract-sign.js","sourceRoot":"","sources":["../../client/components/abstract-sign.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/i18n/ox-i18n.js'\n\nimport { html, nothing } from 'lit'\nimport { startAuthentication } from '@simplewebauthn/browser'\n\nimport { i18next } from '@operato/i18n'\nimport { notify } from '@operato/layout'\n\nimport { AbstractAuthPage } from './abstract-auth-page.js'\n\nconst isAvailableWebauthn = 'PublicKeyCredential' in window\n\ninterface AssertionResponse {\n id: string\n rawId?: number[]\n response: {\n authenticatorData: string\n clientDataJSON: string\n signature: string\n userHandle: string | null\n }\n type: PublicKeyCredentialType\n authenticatorAttachment?: AuthenticatorAttachment\n}\n\nexport abstract class AbstractSign extends AbstractAuthPage {\n async submit() {\n this.formEl.submit()\n }\n\n updated(changed) {\n super.updated(changed)\n\n if (changed.has('message')) {\n if (!this.message) {\n this.hideSnackbar()\n } else {\n this.showSnackbar({\n level: 'error',\n timer: -1\n })\n }\n }\n }\n\n get formfields() {\n const username = this.data?.username || ''\n const autocompletable = this.autocompletable\n\n return html`\n <input id=\"redirectTo\" type=\"hidden\" name=\"redirectTo\" .value=${this.redirectTo || '/'} />\n\n <div class=\"field\">\n <md-filled-text-field\n name=\"username\"\n type=\"text\"\n label=${String(i18next.t('field.user-id or email'))}\n required\n .value=${username}\n autocomplete=${autocompletable ? 'username' : 'off'}\n autocapitalize=\"off\"\n pattern=\"^(?:[A-Za-z0-9]*|[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,})$\"\n @input=${(e: Event) => {\n const target = e.target as HTMLInputElement\n if (target.validity.typeMismatch) {\n target.setCustomValidity(i18next.t('text.invalid-username'))\n } else {\n target.setCustomValidity('')\n }\n }}\n ><md-icon slot=\"leading-icon\">id_card</md-icon></md-filled-text-field\n >\n </div>\n <div class=\"field\">\n <md-filled-text-field\n name=\"password\"\n type=\"password\"\n label=${String(i18next.t('field.password'))}\n autocomplete=${autocompletable ? 'current-password' : 'off'}\n required\n ><md-icon slot=\"leading-icon\">password</md-icon></md-filled-text-field\n >\n </div>\n\n <div class=\"submit-buttons-container\">\n <md-elevated-button class=\"submit-button\" type=\"button\" raised @click=${e => this._onSubmit(e)}>\n <ox-i18n msgid=\"field.${this.pageName}\"> </ox-i18n>\n </md-elevated-button>\n ${isAvailableWebauthn\n ? html` <md-icon class=\"fingerprint\" raised @click=${e => this.authenticateUser()}> fingerprint </md-icon>`\n : nothing}\n </div>\n `\n }\n\n async authenticateUser() {\n try {\n const options = await fetch('/auth/signin-webauthn/challenge').then(res => res.json())\n const assertionResp = await startAuthentication(options)\n const verification = await fetch('/auth/signin-webauthn', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(assertionResp)\n }).then(res => res.json())\n\n if (verification.verified) {\n const { redirectURL } = verification\n\n if (redirectURL) {\n window.location.href = redirectURL\n }\n } else {\n notify({\n level: 'error',\n message: verification.message\n })\n }\n } catch (error) {\n notify({\n level: 'error',\n message: i18next.t('error.authn verification failed')\n })\n }\n }\n}\n"]}
1
+ {"version":3,"file":"abstract-sign.js","sourceRoot":"","sources":["../../client/components/abstract-sign.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/i18n/ox-i18n.js'\n\nimport { html, nothing } from 'lit'\nimport { startAuthentication } from '@simplewebauthn/browser'\n\nimport { i18next } from '@operato/i18n'\nimport { notify } from '@operato/layout'\n\nimport { AbstractAuthPage } from './abstract-auth-page.js'\n\nconst isAvailableWebauthn = 'PublicKeyCredential' in window\n\ninterface AssertionResponse {\n id: string\n rawId?: number[]\n response: {\n authenticatorData: string\n clientDataJSON: string\n signature: string\n userHandle: string | null\n }\n type: PublicKeyCredentialType\n authenticatorAttachment?: AuthenticatorAttachment\n}\n\nexport abstract class AbstractSign extends AbstractAuthPage {\n async submit() {\n this.formEl.submit()\n }\n\n get formfields() {\n const username = this.data?.username || ''\n const autocompletable = this.autocompletable\n\n return html`\n <input id=\"redirectTo\" type=\"hidden\" name=\"redirectTo\" .value=${this.redirectTo || '/'} />\n\n <div class=\"field\">\n <md-filled-text-field\n name=\"username\"\n type=\"text\"\n label=${String(i18next.t('field.user-id or email'))}\n required\n .value=${username}\n autocomplete=${autocompletable ? 'username' : 'off'}\n autocapitalize=\"off\"\n pattern=\"^(?:[A-Za-z0-9]*|[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,})$\"\n @input=${(e: Event) => {\n const target = e.target as HTMLInputElement\n if (target.validity.typeMismatch) {\n target.setCustomValidity(i18next.t('text.invalid-username'))\n } else {\n target.setCustomValidity('')\n }\n }}\n ><md-icon slot=\"leading-icon\">id_card</md-icon></md-filled-text-field\n >\n </div>\n <div class=\"field\">\n <md-filled-text-field\n name=\"password\"\n type=\"password\"\n label=${String(i18next.t('field.password'))}\n autocomplete=${autocompletable ? 'current-password' : 'off'}\n required\n ><md-icon slot=\"leading-icon\">password</md-icon></md-filled-text-field\n >\n </div>\n\n <div class=\"submit-buttons-container\">\n <md-elevated-button class=\"submit-button\" type=\"button\" raised @click=${e => this._onSubmit(e)}>\n <ox-i18n msgid=\"field.${this.pageName}\"> </ox-i18n>\n </md-elevated-button>\n ${isAvailableWebauthn\n ? html` <md-icon class=\"fingerprint\" raised @click=${e => this.authenticateUser()}> fingerprint </md-icon>`\n : nothing}\n </div>\n `\n }\n\n async authenticateUser() {\n try {\n const options = await fetch('/auth/signin-webauthn/challenge').then(res => res.json())\n const assertionResp = await startAuthentication(options)\n const verification = await fetch('/auth/signin-webauthn', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(assertionResp)\n }).then(res => res.json())\n\n if (verification.verified) {\n const { redirectURL } = verification\n\n if (redirectURL) {\n window.location.href = redirectURL\n }\n } else {\n notify({\n level: 'error',\n message: verification.message\n })\n }\n } catch (error) {\n notify({\n level: 'error',\n message: i18next.t('error.authn verification failed')\n })\n }\n }\n}\n"]}
@@ -44,5 +44,4 @@ export declare class AcceptInvitation extends AbstractAuthPage {
44
44
  private fieldValue;
45
45
  submit(): Promise<void>;
46
46
  private accept;
47
- updated(changed: any): void;
48
47
  }
@@ -99,7 +99,7 @@ export class AcceptInvitation extends AbstractAuthPage {
99
99
  </div>
100
100
  </div>
101
101
 
102
- <ox-snack-bar id="snackbar" level="error" .message=${this.message}></ox-snack-bar>
102
+ <ox-snack-bar id="snackbar"></ox-snack-bar>
103
103
  `;
104
104
  }
105
105
  renderDone() {
@@ -238,17 +238,6 @@ export class AcceptInvitation extends AbstractAuthPage {
238
238
  this.busy = false;
239
239
  }
240
240
  }
241
- updated(changed) {
242
- super.updated(changed);
243
- if (changed.has('message')) {
244
- if (!this.message) {
245
- this.hideSnackbar();
246
- }
247
- else {
248
- this.showSnackbar({ timer: -1 });
249
- }
250
- }
251
- }
252
241
  }
253
242
  __decorate([
254
243
  property({ type: String }),
@@ -1 +1 @@
1
- {"version":3,"file":"accept-invitation.js","sourceRoot":"","sources":["../../client/components/accept-invitation.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\nimport '@material/web/textfield/filled-text-field.js'\n\nimport '@operato/i18n/ox-i18n.js'\nimport '@operato/i18n/ox-i18n-selector.js'\nimport '@operato/layout/ox-snack-bar.js'\n\nimport { css, html, nothing } from 'lit'\nimport { property } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { AUTH_STYLE_SIGN } from '../auth-style-sign.js'\nimport { EntryControlSizeStyles } from '../entry-control-size-styles.js'\nimport { AbstractAuthPage } from './abstract-auth-page.js'\nimport { readLinkToken } from '../utils/link-token.js'\n\n/**\n * Accepting an invitation.\n *\n * ── Why the token is not in the address ─────────────────────────────────────\n * The link is `…/accept-invitation#<token>`, and a browser does not send what follows the `#`.\n * So the request that loads this page carries nothing, and the token stays out of server access\n * logs, proxy logs and `Referer`. This page reads it from the fragment and then clears it from\n * the address bar, so it does not sit in the browser's history either.\n *\n * The one place it can still travel is the mail being forwarded, and nothing about where the\n * token sits can prevent that. So the address it was sent to is asked for as well: holding the\n * link is not enough on its own.\n *\n * ── Why the password fields only appear after a try ─────────────────────────\n * Whether an address already has an account is not this page's to announce. It sends what it\n * has, and the server answers `invitation-needs-a-password` when it needs one — so the extra\n * fields appear for the person who is actually creating an account, and nobody learns anything\n * by loading the page.\n */\nexport class AcceptInvitation extends AbstractAuthPage {\n static styles = [\n EntryControlSizeStyles,\n css`\n :host {\n position: relative;\n overflow: hidden;\n\n display: flex;\n flex-direction: row;\n\n width: 100vw;\n height: 100vh;\n height: 100dvh;\n }\n\n .note {\n margin: 0 0 var(--spacing-medium) 0;\n font-size: 0.85rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant);\n }\n `,\n AUTH_STYLE_SIGN\n ]\n\n get pageName() {\n return 'accept invitation'\n }\n\n /*\n * ⚠ `/auth/…` — the server router carries that prefix. This read `/accept-invitation` until\n * 2026-09-24, which is no route; see the note beside `ACCEPT_INVITATION_PATH` in auth-base.\n */\n get actionUrl() {\n return '/auth/accept-invitation'\n }\n\n get title() {\n return 'accept invitation'\n }\n\n /** Read from the fragment on connect. Empty when the link was opened without one. */\n @property({ type: String }) token?: string\n\n /** The server asked for a password, so this person is creating an account. */\n @property({ type: Boolean }) needsPassword?: boolean\n\n /** Joined. The page stops being a form and says where to go. */\n @property({ type: Boolean }) joined?: boolean\n\n @property({ type: Boolean }) busy?: boolean\n\n connectedCallback() {\n super.connectedCallback()\n\n const { token, clean } = readLinkToken(window.location.href)\n\n this.token = token\n\n /*\n * Taken out of the address bar and out of this history entry. `replaceState` rather than\n * `pushState`: going back should not put the token back on screen.\n */\n if (clean && clean !== window.location.href) {\n window.history.replaceState(null, '', clean)\n }\n }\n\n render() {\n const { icon, title, description } = this.applicationMeta\n const { disableUserFavoredLanguage, languages } = this.data || {}\n\n return html`\n <div class=\"wrap\">\n <div class=\"auth-brand\">\n <img src=${icon} />\n <strong class=\"name\">${title}</strong>\n <span class=\"welcome-msg\">${description}</span>\n </div>\n\n <div class=\"auth-form\">\n <h3><ox-i18n msgid=\"title.accept invitation\"></ox-i18n></h3>\n\n ${this.joined ? this.renderDone() : this.renderForm(disableUserFavoredLanguage, languages)}\n </div>\n </div>\n\n <ox-snack-bar id=\"snackbar\" level=\"error\" .message=${this.message}></ox-snack-bar>\n `\n }\n\n private renderDone() {\n return html`\n <p class=\"note\">${i18next.t('text.you have joined. sign in to continue')}</p>\n <md-elevated-button @click=${() => (window.location.href = '/auth/signin')}>\n <ox-i18n msgid=\"button.sign in\"></ox-i18n>\n </md-elevated-button>\n `\n }\n\n private renderForm(disableUserFavoredLanguage: boolean, languages: any) {\n return html`\n <form id=\"form\" @keypress=${(e: KeyboardEvent) => e.key === 'Enter' && this.accept()}>\n <p class=\"note\">${i18next.t('text.confirm the address this invitation was sent to')}</p>\n\n <div class=\"field\">\n <md-filled-text-field\n name=\"email\"\n type=\"email\"\n label=${String(i18next.t('field.email'))}\n autocomplete=\"email\"\n required\n ><md-icon slot=\"leading-icon\">mail</md-icon></md-filled-text-field\n >\n </div>\n\n ${this.token\n ? nothing\n : html`\n <div class=\"field\">\n <md-filled-text-field\n name=\"token\"\n label=${String(i18next.t('label.invitation code'))}\n supporting-text=${String(i18next.t('text.the code is printed in the invitation email'))}\n autocomplete=\"off\"\n required\n ><md-icon slot=\"leading-icon\">key</md-icon></md-filled-text-field\n >\n </div>\n `}\n ${this.needsPassword ? this.renderNewAccount() : nothing}\n\n <md-elevated-button id=\"submit-button\" type=\"button\" ?disabled=${this.busy} @click=${() => this.accept()}>\n <ox-i18n msgid=\"button.accept invitation\"></ox-i18n>\n </md-elevated-button>\n\n ${!disableUserFavoredLanguage\n ? html`<div id=\"locale-area\">\n <label for=\"locale-selector\"><md-icon>language</md-icon></label>\n <ox-i18n-selector\n id=\"locale-selector\"\n value=${i18next.language || 'en-US'}\n .languages=${languages}\n @change=${(e: CustomEvent) => e.detail && i18next.changeLanguage(e.detail as any)}\n ></ox-i18n-selector>\n </div>`\n : nothing}\n </form>\n `\n }\n\n private renderNewAccount() {\n return html`\n <p class=\"note\">${i18next.t('text.choose a password to finish joining')}</p>\n\n <div class=\"field\">\n <md-filled-text-field name=\"name\" label=${String(i18next.t('field.name'))} autocomplete=\"name\"\n ><md-icon slot=\"leading-icon\">person</md-icon></md-filled-text-field\n >\n </div>\n\n <div class=\"field\">\n <md-filled-text-field\n name=\"password\"\n type=\"password\"\n label=${String(i18next.t('label.password'))}\n autocomplete=\"new-password\"\n required\n ><md-icon slot=\"leading-icon\">password</md-icon></md-filled-text-field\n >\n </div>\n `\n }\n\n /*\n * ⚠ Not called `valueOf`. Overriding `Object.prototype.valueOf` with a different signature\n * makes the class type unassignable to `Object`, and every `@property` decorator on it then\n * fails to resolve — with an error that talks about decorators and never mentions the method.\n */\n private fieldValue(name: string): string {\n return (this.renderRoot.querySelector(`[name=\"${name}\"]`) as any)?.value?.trim() || ''\n }\n\n /*\n * The base class calls this to commit the page. It does not submit the form element: the\n * answer here is read rather than navigated to, because a refusal can mean \"now ask for a\n * password\" instead of \"stop\".\n */\n async submit() {\n await this.accept()\n }\n\n private async accept() {\n if (this.busy) {\n return\n }\n\n this.busy = true\n this.message = ''\n\n try {\n const response = await fetch(this.actionUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n token: this.token || this.fieldValue('token'),\n email: this.fieldValue('email'),\n name: this.fieldValue('name'),\n password: this.fieldValue('password')\n })\n })\n\n if (response.ok) {\n this.joined = true\n return\n }\n\n const refusal = await response.json().catch(() => ({}))\n\n /*\n * The one refusal that is not the end of the attempt — it means this person has no account\n * yet, which is exactly who this page is for.\n */\n if (refusal?.refusal?.code === 'invitation-needs-a-password' || refusal?.code === 'invitation-needs-a-password') {\n this.needsPassword = true\n this.message = String(i18next.t('text.choose a password to finish joining'))\n return\n }\n\n this.message = refusal?.message || String(i18next.t('text.this invitation cannot be used'))\n } catch (e) {\n this.message = String(i18next.t('text.this invitation cannot be used'))\n } finally {\n this.busy = false\n }\n }\n\n updated(changed: any) {\n super.updated(changed)\n\n if (changed.has('message')) {\n if (!this.message) {\n this.hideSnackbar()\n } else {\n this.showSnackbar({ timer: -1 })\n }\n }\n }\n}\n"]}
1
+ {"version":3,"file":"accept-invitation.js","sourceRoot":"","sources":["../../client/components/accept-invitation.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\nimport '@material/web/textfield/filled-text-field.js'\n\nimport '@operato/i18n/ox-i18n.js'\nimport '@operato/i18n/ox-i18n-selector.js'\nimport '@operato/layout/ox-snack-bar.js'\n\nimport { css, html, nothing } from 'lit'\nimport { property } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { AUTH_STYLE_SIGN } from '../auth-style-sign.js'\nimport { EntryControlSizeStyles } from '../entry-control-size-styles.js'\nimport { AbstractAuthPage } from './abstract-auth-page.js'\nimport { readLinkToken } from '../utils/link-token.js'\n\n/**\n * Accepting an invitation.\n *\n * ── Why the token is not in the address ─────────────────────────────────────\n * The link is `…/accept-invitation#<token>`, and a browser does not send what follows the `#`.\n * So the request that loads this page carries nothing, and the token stays out of server access\n * logs, proxy logs and `Referer`. This page reads it from the fragment and then clears it from\n * the address bar, so it does not sit in the browser's history either.\n *\n * The one place it can still travel is the mail being forwarded, and nothing about where the\n * token sits can prevent that. So the address it was sent to is asked for as well: holding the\n * link is not enough on its own.\n *\n * ── Why the password fields only appear after a try ─────────────────────────\n * Whether an address already has an account is not this page's to announce. It sends what it\n * has, and the server answers `invitation-needs-a-password` when it needs one — so the extra\n * fields appear for the person who is actually creating an account, and nobody learns anything\n * by loading the page.\n */\nexport class AcceptInvitation extends AbstractAuthPage {\n static styles = [\n EntryControlSizeStyles,\n css`\n :host {\n position: relative;\n overflow: hidden;\n\n display: flex;\n flex-direction: row;\n\n width: 100vw;\n height: 100vh;\n height: 100dvh;\n }\n\n .note {\n margin: 0 0 var(--spacing-medium) 0;\n font-size: 0.85rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant);\n }\n `,\n AUTH_STYLE_SIGN\n ]\n\n get pageName() {\n return 'accept invitation'\n }\n\n /*\n * ⚠ `/auth/…` — the server router carries that prefix. This read `/accept-invitation` until\n * 2026-09-24, which is no route; see the note beside `ACCEPT_INVITATION_PATH` in auth-base.\n */\n get actionUrl() {\n return '/auth/accept-invitation'\n }\n\n get title() {\n return 'accept invitation'\n }\n\n /** Read from the fragment on connect. Empty when the link was opened without one. */\n @property({ type: String }) token?: string\n\n /** The server asked for a password, so this person is creating an account. */\n @property({ type: Boolean }) needsPassword?: boolean\n\n /** Joined. The page stops being a form and says where to go. */\n @property({ type: Boolean }) joined?: boolean\n\n @property({ type: Boolean }) busy?: boolean\n\n connectedCallback() {\n super.connectedCallback()\n\n const { token, clean } = readLinkToken(window.location.href)\n\n this.token = token\n\n /*\n * Taken out of the address bar and out of this history entry. `replaceState` rather than\n * `pushState`: going back should not put the token back on screen.\n */\n if (clean && clean !== window.location.href) {\n window.history.replaceState(null, '', clean)\n }\n }\n\n render() {\n const { icon, title, description } = this.applicationMeta\n const { disableUserFavoredLanguage, languages } = this.data || {}\n\n return html`\n <div class=\"wrap\">\n <div class=\"auth-brand\">\n <img src=${icon} />\n <strong class=\"name\">${title}</strong>\n <span class=\"welcome-msg\">${description}</span>\n </div>\n\n <div class=\"auth-form\">\n <h3><ox-i18n msgid=\"title.accept invitation\"></ox-i18n></h3>\n\n ${this.joined ? this.renderDone() : this.renderForm(disableUserFavoredLanguage, languages)}\n </div>\n </div>\n\n <ox-snack-bar id=\"snackbar\"></ox-snack-bar>\n `\n }\n\n private renderDone() {\n return html`\n <p class=\"note\">${i18next.t('text.you have joined. sign in to continue')}</p>\n <md-elevated-button @click=${() => (window.location.href = '/auth/signin')}>\n <ox-i18n msgid=\"button.sign in\"></ox-i18n>\n </md-elevated-button>\n `\n }\n\n private renderForm(disableUserFavoredLanguage: boolean, languages: any) {\n return html`\n <form id=\"form\" @keypress=${(e: KeyboardEvent) => e.key === 'Enter' && this.accept()}>\n <p class=\"note\">${i18next.t('text.confirm the address this invitation was sent to')}</p>\n\n <div class=\"field\">\n <md-filled-text-field\n name=\"email\"\n type=\"email\"\n label=${String(i18next.t('field.email'))}\n autocomplete=\"email\"\n required\n ><md-icon slot=\"leading-icon\">mail</md-icon></md-filled-text-field\n >\n </div>\n\n ${this.token\n ? nothing\n : html`\n <div class=\"field\">\n <md-filled-text-field\n name=\"token\"\n label=${String(i18next.t('label.invitation code'))}\n supporting-text=${String(i18next.t('text.the code is printed in the invitation email'))}\n autocomplete=\"off\"\n required\n ><md-icon slot=\"leading-icon\">key</md-icon></md-filled-text-field\n >\n </div>\n `}\n ${this.needsPassword ? this.renderNewAccount() : nothing}\n\n <md-elevated-button id=\"submit-button\" type=\"button\" ?disabled=${this.busy} @click=${() => this.accept()}>\n <ox-i18n msgid=\"button.accept invitation\"></ox-i18n>\n </md-elevated-button>\n\n ${!disableUserFavoredLanguage\n ? html`<div id=\"locale-area\">\n <label for=\"locale-selector\"><md-icon>language</md-icon></label>\n <ox-i18n-selector\n id=\"locale-selector\"\n value=${i18next.language || 'en-US'}\n .languages=${languages}\n @change=${(e: CustomEvent) => e.detail && i18next.changeLanguage(e.detail as any)}\n ></ox-i18n-selector>\n </div>`\n : nothing}\n </form>\n `\n }\n\n private renderNewAccount() {\n return html`\n <p class=\"note\">${i18next.t('text.choose a password to finish joining')}</p>\n\n <div class=\"field\">\n <md-filled-text-field name=\"name\" label=${String(i18next.t('field.name'))} autocomplete=\"name\"\n ><md-icon slot=\"leading-icon\">person</md-icon></md-filled-text-field\n >\n </div>\n\n <div class=\"field\">\n <md-filled-text-field\n name=\"password\"\n type=\"password\"\n label=${String(i18next.t('label.password'))}\n autocomplete=\"new-password\"\n required\n ><md-icon slot=\"leading-icon\">password</md-icon></md-filled-text-field\n >\n </div>\n `\n }\n\n /*\n * ⚠ Not called `valueOf`. Overriding `Object.prototype.valueOf` with a different signature\n * makes the class type unassignable to `Object`, and every `@property` decorator on it then\n * fails to resolve — with an error that talks about decorators and never mentions the method.\n */\n private fieldValue(name: string): string {\n return (this.renderRoot.querySelector(`[name=\"${name}\"]`) as any)?.value?.trim() || ''\n }\n\n /*\n * The base class calls this to commit the page. It does not submit the form element: the\n * answer here is read rather than navigated to, because a refusal can mean \"now ask for a\n * password\" instead of \"stop\".\n */\n async submit() {\n await this.accept()\n }\n\n private async accept() {\n if (this.busy) {\n return\n }\n\n this.busy = true\n this.message = ''\n\n try {\n const response = await fetch(this.actionUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n token: this.token || this.fieldValue('token'),\n email: this.fieldValue('email'),\n name: this.fieldValue('name'),\n password: this.fieldValue('password')\n })\n })\n\n if (response.ok) {\n this.joined = true\n return\n }\n\n const refusal = await response.json().catch(() => ({}))\n\n /*\n * The one refusal that is not the end of the attempt — it means this person has no account\n * yet, which is exactly who this page is for.\n */\n if (refusal?.refusal?.code === 'invitation-needs-a-password' || refusal?.code === 'invitation-needs-a-password') {\n this.needsPassword = true\n this.message = String(i18next.t('text.choose a password to finish joining'))\n return\n }\n\n this.message = refusal?.message || String(i18next.t('text.this invitation cannot be used'))\n } catch (e) {\n this.message = String(i18next.t('text.this invitation cannot be used'))\n } finally {\n this.busy = false\n }\n }\n}\n"]}
@@ -0,0 +1,48 @@
1
+ import { readdirSync, readFileSync, statSync } from 'fs';
2
+ import { join } from 'path';
3
+ /**
4
+ * An auth screen raises a toast through `notify`, never by setting properties on its ox-snack-bar.
5
+ *
6
+ * Since @operato/layout moved ox-snack-bar onto SnackbarController it draws the store and
7
+ * nothing else: `level`, `message` and `active` written on the element are overwritten on the
8
+ * next render. The auth pages still wrote them, so a wrong password, a refused reset and the
9
+ * "mail sent" answer of forgot-password all reached the page and never showed. Nothing failed -
10
+ * the element simply stayed hidden.
11
+ *
12
+ * ⚠ Comments are stripped first - this file and the screens explain the old way by naming it.
13
+ */
14
+ const CLIENT = join(__dirname, '..');
15
+ function tsFilesUnder(dir) {
16
+ return readdirSync(dir).flatMap(entry => {
17
+ const path = join(dir, entry);
18
+ if (statSync(path).isDirectory()) {
19
+ return tsFilesUnder(path);
20
+ }
21
+ return entry.endsWith('.ts') && !entry.endsWith('.test.ts') ? [path] : [];
22
+ });
23
+ }
24
+ const withoutComments = (source) => source.replace(/\/\*[\s\S]*?\*\//g, '').replace(/^\s*\/\/.*$/gm, '');
25
+ const SOURCES = tsFilesUnder(CLIENT).map(path => ({
26
+ name: path.slice(CLIENT.length + 1),
27
+ source: withoutComments(readFileSync(path, 'utf-8'))
28
+ }));
29
+ describe('auth screens raise toasts through notify', () => {
30
+ it('no ox-snack-bar is given a level, a message or active', () => {
31
+ const offenders = SOURCES.filter(({ source }) => /<ox-snack-bar\b[^>]*\b(level|\.?message|\.?active|\?active)=/.test(source)).map(({ name }) => name);
32
+ expect(offenders).toEqual([]);
33
+ });
34
+ it('no screen reaches into its snack bar', () => {
35
+ const offenders = SOURCES.filter(({ source }) => /querySelector\(\s*['"]#snackbar['"]\s*\)|\bshowSnackbar\s*\(|\bhideSnackbar\s*\(/.test(source)).map(({ name }) => name);
36
+ expect(offenders).toEqual([]);
37
+ });
38
+ it('the refusal a page is rendered with is raised by the base page', () => {
39
+ const base = SOURCES.find(({ name }) => name === join('components', 'abstract-auth-page.ts')).source;
40
+ expect(/notify\(\s*\{\s*level:\s*'error',\s*message:\s*this\.message\s*\}\s*\)/.test(base)).toBe(true);
41
+ });
42
+ it.each([join('entries', 'auth', 'forgot-password.ts'), join('entries', 'auth', 'activate.ts')])('%s says what the mail request came to, and that it failed when it did', name => {
43
+ const source = SOURCES.find(entry => entry.name === name).source;
44
+ expect(/notify\(\{\s*level:\s*response\.ok \? 'success' : 'error',\s*message:\s*await response\.text\(\)/.test(source)).toBe(true);
45
+ expect(/catch \(e\) \{\s*notify\(/.test(source)).toBe(true);
46
+ });
47
+ });
48
+ //# sourceMappingURL=toasts-go-through-notify.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toasts-go-through-notify.test.js","sourceRoot":"","sources":["../../client/components/toasts-go-through-notify.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdirSync, readFileSync, statSync } from 'fs'\nimport { join } from 'path'\n\n/**\n * An auth screen raises a toast through `notify`, never by setting properties on its ox-snack-bar.\n *\n * Since @operato/layout moved ox-snack-bar onto SnackbarController it draws the store and\n * nothing else: `level`, `message` and `active` written on the element are overwritten on the\n * next render. The auth pages still wrote them, so a wrong password, a refused reset and the\n * \"mail sent\" answer of forgot-password all reached the page and never showed. Nothing failed -\n * the element simply stayed hidden.\n *\n * ⚠ Comments are stripped first - this file and the screens explain the old way by naming it.\n */\n\nconst CLIENT = join(__dirname, '..')\n\nfunction tsFilesUnder(dir: string): string[] {\n return readdirSync(dir).flatMap(entry => {\n const path = join(dir, entry)\n\n if (statSync(path).isDirectory()) {\n return tsFilesUnder(path)\n }\n\n return entry.endsWith('.ts') && !entry.endsWith('.test.ts') ? [path] : []\n })\n}\n\nconst withoutComments = (source: string) => source.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '').replace(/^\\s*\\/\\/.*$/gm, '')\n\nconst SOURCES = tsFilesUnder(CLIENT).map(path => ({\n name: path.slice(CLIENT.length + 1),\n source: withoutComments(readFileSync(path, 'utf-8'))\n}))\n\ndescribe('auth screens raise toasts through notify', () => {\n it('no ox-snack-bar is given a level, a message or active', () => {\n const offenders = SOURCES.filter(({ source }) =>\n /<ox-snack-bar\\b[^>]*\\b(level|\\.?message|\\.?active|\\?active)=/.test(source)\n ).map(({ name }) => name)\n\n expect(offenders).toEqual([])\n })\n\n it('no screen reaches into its snack bar', () => {\n const offenders = SOURCES.filter(({ source }) =>\n /querySelector\\(\\s*['\"]#snackbar['\"]\\s*\\)|\\bshowSnackbar\\s*\\(|\\bhideSnackbar\\s*\\(/.test(source)\n ).map(({ name }) => name)\n\n expect(offenders).toEqual([])\n })\n\n it('the refusal a page is rendered with is raised by the base page', () => {\n const base = SOURCES.find(({ name }) => name === join('components', 'abstract-auth-page.ts'))!.source\n\n expect(/notify\\(\\s*\\{\\s*level:\\s*'error',\\s*message:\\s*this\\.message\\s*\\}\\s*\\)/.test(base)).toBe(true)\n })\n\n it.each([join('entries', 'auth', 'forgot-password.ts'), join('entries', 'auth', 'activate.ts')])(\n '%s says what the mail request came to, and that it failed when it did',\n name => {\n const source = SOURCES.find(entry => entry.name === name)!.source\n\n expect(/notify\\(\\{\\s*level:\\s*response\\.ok \\? 'success' : 'error',\\s*message:\\s*await response\\.text\\(\\)/.test(source)).toBe(true)\n expect(/catch \\(e\\) \\{\\s*notify\\(/.test(source)).toBe(true)\n }\n )\n})\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import { readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ /**
4
+ * The activate page says which of its two cases it is in.
5
+ *
6
+ * The server sends a locked account here as well as one not yet confirmed. The page said "your
7
+ * account is not activated" to both and offered the activation mail to both. A locked person had
8
+ * already been sent the unlock mail; the activation mail on top was a second, different way back,
9
+ * and nothing on the page said why they were stopped.
10
+ *
11
+ * `auth-base/tests/activate-page-case.test.ts` holds the code the server sends; this holds what the
12
+ * page does with it.
13
+ */
14
+ const page = readFileSync(join(__dirname, 'activate.ts'), 'utf-8');
15
+ describe('the activate page', () => {
16
+ it('tells a locked account from one not yet confirmed', () => {
17
+ expect(page).toContain("i18next.t(locked ? 'text.your account is locked' : 'text.your account is not activated')");
18
+ });
19
+ it('tells a locked account the unlock mail has gone', () => {
20
+ expect(page).toContain("${locked ? html`<p>${i18next.t('text.unlock email sent')}</p>` : nothing}");
21
+ });
22
+ it('offers the activation mail only to an account not yet confirmed', () => {
23
+ expect(/\$\{locked\s*\?\s*nothing\s*:\s*html`\s*<md-elevated-button @click=\$\{e => this\.requestResend\(e\)\}>/.test(page)).toBe(true);
24
+ });
25
+ });
26
+ //# sourceMappingURL=activate-page.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activate-page.test.js","sourceRoot":"","sources":["../../../client/entries/auth/activate-page.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE3B;;;;;;;;;;GAUG;AAEH,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,CAAC,CAAA;AAElE,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,0FAA0F,CAAC,CAAA;IACpH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,2EAA2E,CAAC,CAAA;IACrG,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,CAAC,yGAAyG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzI,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import { readFileSync } from 'fs'\nimport { join } from 'path'\n\n/**\n * The activate page says which of its two cases it is in.\n *\n * The server sends a locked account here as well as one not yet confirmed. The page said \"your\n * account is not activated\" to both and offered the activation mail to both. A locked person had\n * already been sent the unlock mail; the activation mail on top was a second, different way back,\n * and nothing on the page said why they were stopped.\n *\n * `auth-base/tests/activate-page-case.test.ts` holds the code the server sends; this holds what the\n * page does with it.\n */\n\nconst page = readFileSync(join(__dirname, 'activate.ts'), 'utf-8')\n\ndescribe('the activate page', () => {\n it('tells a locked account from one not yet confirmed', () => {\n expect(page).toContain(\"i18next.t(locked ? 'text.your account is locked' : 'text.your account is not activated')\")\n })\n\n it('tells a locked account the unlock mail has gone', () => {\n expect(page).toContain(\"${locked ? html`<p>${i18next.t('text.unlock email sent')}</p>` : nothing}\")\n })\n\n it('offers the activation mail only to an account not yet confirmed', () => {\n expect(/\\$\\{locked\\s*\\?\\s*nothing\\s*:\\s*html`\\s*<md-elevated-button @click=\\$\\{e => this\\.requestResend\\(e\\)\\}>/.test(page)).toBe(true)\n })\n})\n"]}
@@ -20,10 +20,5 @@ export declare class AuthActivate extends AuthActivate_base {
20
20
  firstUpdated(): void;
21
21
  updated(changed: any): void;
22
22
  requestResend(e: any): Promise<void>;
23
- showSnackbar({ level, message, timer }: {
24
- level: 'info' | 'error';
25
- message: string;
26
- timer?: number;
27
- }): void;
28
23
  }
29
24
  export {};
@@ -4,9 +4,10 @@ import '@material/web/button/elevated-button.js';
4
4
  import '@operato/i18n/ox-i18n.js';
5
5
  import '@operato/layout/ox-snack-bar.js';
6
6
  import '@operato/lottie-player';
7
- import { css, html, LitElement } from 'lit';
7
+ import { css, html, LitElement, nothing } from 'lit';
8
8
  import { customElement, property, query } from 'lit/decorators.js';
9
9
  import { i18next, localize } from '@operato/i18n';
10
+ import { notify } from '@operato/layout';
10
11
  import { EntryControlSizeStyles } from '../../entry-control-size-styles.js';
11
12
  let AuthActivate = class AuthActivate extends localize(i18next)(LitElement) {
12
13
  static { this.styles = [
@@ -118,6 +119,12 @@ let AuthActivate = class AuthActivate extends localize(i18next)(LitElement) {
118
119
  ]; }
119
120
  render() {
120
121
  var { icon, title, description } = this.applicationMeta;
122
+ /*
123
+ * The server sends a locked account here as well as one not yet confirmed. Locked, it has
124
+ * already mailed the unlock link - an activation mail on top would be a second, different way
125
+ * back, so that button is for the unconfirmed only.
126
+ */
127
+ const locked = this.data?.errorCode === 'user-locked';
121
128
  return html `
122
129
  <div class="wrap">
123
130
  <div class="auth-brand">
@@ -126,10 +133,9 @@ let AuthActivate = class AuthActivate extends localize(i18next)(LitElement) {
126
133
  <span class="welcome-msg">${description}</span>
127
134
  </div>
128
135
 
129
- <h1><ox-i18n msgid="text.your account is not activated"></ox-i18n></h1>
136
+ <h1>${i18next.t(locked ? 'text.your account is locked' : 'text.your account is not activated')}</h1>
130
137
 
131
- <!--description message container-->
132
- <p></p>
138
+ ${locked ? html `<p>${i18next.t('text.unlock email sent')}</p>` : nothing}
133
139
 
134
140
  <form
135
141
  id="form"
@@ -145,10 +151,14 @@ let AuthActivate = class AuthActivate extends localize(i18next)(LitElement) {
145
151
  </form>
146
152
 
147
153
  <div id="button-area">
148
- <md-elevated-button @click=${e => this.requestResend(e)}>
149
- <md-icon slot="icon">mail_outline</md-icon>
150
- ${i18next.t('label.send activation email')}
151
- </md-elevated-button>
154
+ ${locked
155
+ ? nothing
156
+ : html `
157
+ <md-elevated-button @click=${e => this.requestResend(e)}>
158
+ <md-icon slot="icon">mail_outline</md-icon>
159
+ ${i18next.t('label.send activation email')}
160
+ </md-elevated-button>
161
+ `}
152
162
  <md-elevated-button
153
163
  @click=${e => {
154
164
  window.location.replace('/auth/signin');
@@ -212,30 +222,19 @@ let AuthActivate = class AuthActivate extends localize(i18next)(LitElement) {
212
222
  body: JSON.stringify(Object.fromEntries(formData.entries())),
213
223
  signal
214
224
  });
215
- if (response) {
216
- this.showSnackbar({
217
- level: response.ok ? 'info' : 'error',
218
- message: await response.text()
219
- });
220
- }
225
+ notify({
226
+ level: response.ok ? 'success' : 'error',
227
+ message: await response.text()
228
+ });
221
229
  }
222
230
  catch (e) {
231
+ notify({ level: 'error', message: i18next.t('text.please try again'), ex: e });
223
232
  }
224
233
  finally {
225
234
  button.disabled = false;
226
235
  clearTimeout(timer);
227
236
  }
228
237
  }
229
- showSnackbar({ level, message, timer = 3000 }) {
230
- const snackbar = this.renderRoot.querySelector('#snackbar');
231
- snackbar.level = level;
232
- snackbar.message = message;
233
- snackbar.active = true;
234
- if (timer > -1)
235
- setTimeout(() => {
236
- snackbar.active = false;
237
- }, timer);
238
- }
239
238
  };
240
239
  __decorate([
241
240
  property({ type: Object }),