@sneat/components 0.27.31 → 0.27.32
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.
- package/esm2022/index.js +1 -0
- package/esm2022/index.js.map +1 -1
- package/esm2022/lib/country-selector/countries-loader.service.js +9 -7
- package/esm2022/lib/country-selector/countries-loader.service.js.map +1 -1
- package/esm2022/lib/country-selector/country-selector.component.js +4 -3
- package/esm2022/lib/country-selector/country-selector.component.js.map +1 -1
- package/esm2022/lib/subscription-recovery/subscription-recovery.component.js +12 -6
- package/esm2022/lib/subscription-recovery/subscription-recovery.component.js.map +1 -1
- package/esm2022/lib/subscription-settings/subscription-settings.component.js +653 -0
- package/esm2022/lib/subscription-settings/subscription-settings.component.js.map +1 -0
- package/index.d.ts +1 -0
- package/lib/country-selector/countries-loader.service.d.ts +1 -0
- package/lib/subscription-recovery/subscription-recovery.component.d.ts +1 -0
- package/lib/subscription-settings/subscription-settings.component.d.ts +96 -0
- package/package.json +1 -1
- package/tsconfig.lib.prod.tsbuildinfo +1 -1
package/esm2022/index.js
CHANGED
|
@@ -13,4 +13,5 @@ export * from './lib/date-input/date-input.component';
|
|
|
13
13
|
export * from './lib/user-country/user-country.component';
|
|
14
14
|
export * from './lib/card-list/sneat-card-list.component';
|
|
15
15
|
export * from './lib/subscription-recovery/subscription-recovery.component';
|
|
16
|
+
export * from './lib/subscription-settings/subscription-settings.component';
|
|
16
17
|
//# sourceMappingURL=index.js.map
|
package/esm2022/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/components/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,qCAAqC,CAAC;AACpD,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uCAAuC,CAAC;AACtD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,6DAA6D,CAAC","sourcesContent":["export * from './lib/app-version';\nexport * from './lib/pipes';\nexport * from './lib/virtual-slider/virtual-slider';\nexport * from './lib/card-list';\nexport * from './lib/error-card';\nexport * from './lib/country-selector';\nexport * from './lib/filter-item';\nexport * from './lib/copyright';\nexport * from './lib/dialog-header';\nexport * from './lib/country-input';\nexport * from './lib/save-event';\nexport * from './lib/date-input/date-input.component';\nexport * from './lib/user-country/user-country.component';\nexport * from './lib/card-list/sneat-card-list.component';\nexport * from './lib/subscription-recovery/subscription-recovery.component';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/components/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,qCAAqC,CAAC;AACpD,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uCAAuC,CAAC;AACtD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,6DAA6D,CAAC;AAC5E,cAAc,6DAA6D,CAAC","sourcesContent":["export * from './lib/app-version';\nexport * from './lib/pipes';\nexport * from './lib/virtual-slider/virtual-slider';\nexport * from './lib/card-list';\nexport * from './lib/error-card';\nexport * from './lib/country-selector';\nexport * from './lib/filter-item';\nexport * from './lib/copyright';\nexport * from './lib/dialog-header';\nexport * from './lib/country-input';\nexport * from './lib/save-event';\nexport * from './lib/date-input/date-input.component';\nexport * from './lib/user-country/user-country.component';\nexport * from './lib/card-list/sneat-card-list.component';\nexport * from './lib/subscription-recovery/subscription-recovery.component';\nexport * from './lib/subscription-settings/subscription-settings.component';\n"]}
|
|
@@ -64,32 +64,34 @@ export class CountriesLoaderService {
|
|
|
64
64
|
* Automatically loads country data if not yet loaded.
|
|
65
65
|
*/
|
|
66
66
|
async getCountries() {
|
|
67
|
-
await this.
|
|
68
|
-
return Object.values(this.countriesData.countriesByID);
|
|
67
|
+
return Object.values((await this.loadedCountries()).countriesByID);
|
|
69
68
|
}
|
|
70
69
|
/**
|
|
71
70
|
* Gets countries by ID lookup.
|
|
72
71
|
* Automatically loads country data if not yet loaded.
|
|
73
72
|
*/
|
|
74
73
|
async getCountriesByID() {
|
|
75
|
-
await this.
|
|
76
|
-
return this.countriesData.countriesByID;
|
|
74
|
+
return (await this.loadedCountries()).countriesByID;
|
|
77
75
|
}
|
|
78
76
|
/**
|
|
79
77
|
* Gets a specific country by ID.
|
|
80
78
|
* Automatically loads country data if not yet loaded.
|
|
81
79
|
*/
|
|
82
80
|
async getCountryByID(id) {
|
|
83
|
-
await this.
|
|
84
|
-
return this.countriesData.countriesByID[id];
|
|
81
|
+
return (await this.loadedCountries()).countriesByID[id];
|
|
85
82
|
}
|
|
86
83
|
/**
|
|
87
84
|
* Gets the unknown country constant.
|
|
88
85
|
* Automatically loads country data if not yet loaded.
|
|
89
86
|
*/
|
|
90
87
|
async getUnknownCountry() {
|
|
88
|
+
return (await this.loadedCountries()).unknownCountry;
|
|
89
|
+
}
|
|
90
|
+
async loadedCountries() {
|
|
91
91
|
await this.loadCountries();
|
|
92
|
-
|
|
92
|
+
if (!this.countriesData)
|
|
93
|
+
throw new Error('Country data was cleared during loading');
|
|
94
|
+
return this.countriesData;
|
|
93
95
|
}
|
|
94
96
|
/**
|
|
95
97
|
* Gets countries filtered by geo region.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"countries-loader.service.js","sourceRoot":"","sources":["../../../../../../libs/components/src/lib/country-selector/countries-loader.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;;AAQtC;;;GAGG;AAEH,MAAM,OAAO,sBAAsB;IADnC;QAEmB,SAAI,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QAI3C;;;;WAIG;QACM,eAAU,GAAG,MAAM,CAAC,KAAK;uFAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"countries-loader.service.js","sourceRoot":"","sources":["../../../../../../libs/components/src/lib/country-selector/countries-loader.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { inject, Injectable, signal } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { firstValueFrom } from 'rxjs';\nimport { GeoRegion, ICountry } from './countries';\n\ninterface CountriesData {\n countriesByID: Record<string, ICountry>;\n unknownCountry: ICountry;\n}\n\n/**\n * Service to lazy load country data.\n * This reduces the initial bundle size by loading country data on-demand.\n */\n@Injectable({ providedIn: 'root' })\nexport class CountriesLoaderService {\n private readonly http = inject(HttpClient);\n private countriesData?: CountriesData;\n private loadPromise?: Promise<void>;\n\n /**\n * True when the last load attempt failed (countries.json couldn't be\n * fetched/parsed). Consumers can read this to show an error in the UI. Reset\n * to false on a successful (re)load. See `reload()` to retry.\n */\n readonly loadFailed = signal(false);\n\n /**\n * Loads country data from JSON file.\n * Subsequent calls return the same promise (singleton pattern).\n */\n private async loadCountries(): Promise<void> {\n if (this.countriesData) {\n return; // Already loaded\n }\n\n if (!this.loadPromise) {\n this.loadPromise = (async () => {\n try {\n this.countriesData = await firstValueFrom(\n this.http.get<CountriesData>('assets/data/countries.json'),\n );\n this.loadFailed.set(false);\n } catch (error) {\n console.error('Failed to load countries:', error);\n this.loadFailed.set(true);\n // Set to empty to avoid repeated failures\n this.countriesData = {\n countriesByID: {},\n unknownCountry: {\n id: '--',\n id3: '---',\n title: 'Unknown',\n geoRegions: [],\n emoji: '🏳️',\n },\n };\n }\n })();\n }\n\n return this.loadPromise;\n }\n\n /**\n * Discards the cached data and re-attempts the load — used to retry after a\n * failure (`loadFailed()` is true). Resolves once the reload settles.\n */\n async reload(): Promise<void> {\n this.countriesData = undefined;\n this.loadPromise = undefined;\n await this.loadCountries();\n }\n\n /**\n * Gets all countries as an array.\n * Automatically loads country data if not yet loaded.\n */\n async getCountries(): Promise<readonly ICountry[]> {\n return Object.values((await this.loadedCountries()).countriesByID);\n }\n\n /**\n * Gets countries by ID lookup.\n * Automatically loads country data if not yet loaded.\n */\n async getCountriesByID(): Promise<Record<string, ICountry>> {\n return (await this.loadedCountries()).countriesByID;\n }\n\n /**\n * Gets a specific country by ID.\n * Automatically loads country data if not yet loaded.\n */\n async getCountryByID(id: string): Promise<ICountry | undefined> {\n return (await this.loadedCountries()).countriesByID[id];\n }\n\n /**\n * Gets the unknown country constant.\n * Automatically loads country data if not yet loaded.\n */\n async getUnknownCountry(): Promise<ICountry> {\n return (await this.loadedCountries()).unknownCountry;\n }\n\n private async loadedCountries(): Promise<CountriesData> {\n await this.loadCountries();\n if (!this.countriesData)\n throw new Error('Country data was cleared during loading');\n return this.countriesData;\n }\n\n /**\n * Gets countries filtered by geo region.\n * Automatically loads country data if not yet loaded.\n */\n async getCountriesByRegion(region: GeoRegion): Promise<readonly ICountry[]> {\n const countries = await this.getCountries();\n return countries.filter((c) => c.geoRegions.includes(region));\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, computed, EventEmitter, inject, Input, Output, signal, ChangeDetectionStrategy, input } from '@angular/core';
|
|
1
|
+
import { Component, computed, EventEmitter, inject, Input, Output, signal, ChangeDetectionStrategy, input, } from '@angular/core';
|
|
2
2
|
import { FormsModule } from '@angular/forms';
|
|
3
3
|
import { IonButton, IonItem, IonLabel, IonSegment, IonSegmentButton, } from '@ionic/angular';
|
|
4
4
|
import { SelectFromListComponent } from '@sneat/ui';
|
|
@@ -48,8 +48,9 @@ export class CountrySelectorComponent {
|
|
|
48
48
|
(c.geoRegions.includes('North America') ||
|
|
49
49
|
c.geoRegions.includes('South America'))) ||
|
|
50
50
|
c.geoRegions.includes(this.geoRegion()));
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
const unknownCountry = this.unknownCountry();
|
|
52
|
+
if (this.canBeUnknown() && unknownCountry) {
|
|
53
|
+
countriesToShow = [...countriesToShow, unknownCountry];
|
|
53
54
|
}
|
|
54
55
|
return countriesToShow;
|
|
55
56
|
}, /* @ts-ignore */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"country-selector.component.js","sourceRoot":"","sources":["../../../../../../libs/components/src/lib/country-selector/country-selector.component.ts","../../../../../../libs/components/src/lib/country-selector/country-selector.component.html"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,MAAM,EACN,KAAK,EAGL,MAAM,EACN,MAAM,EAEN,uBAAuB,EACvB,KAAK,
|
|
1
|
+
{"version":3,"file":"country-selector.component.js","sourceRoot":"","sources":["../../../../../../libs/components/src/lib/country-selector/country-selector.component.ts","../../../../../../libs/components/src/lib/country-selector/country-selector.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n computed,\n EventEmitter,\n inject,\n Input,\n OnChanges,\n OnInit,\n Output,\n signal,\n SimpleChanges,\n ChangeDetectionStrategy,\n input,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport {\n IonButton,\n IonItem,\n IonLabel,\n IonSegment,\n IonSegmentButton,\n} from '@ionic/angular';\nimport { ISelectItem, SelectFromListComponent } from '@sneat/ui';\nimport { GeoRegion, ICountry } from './countries';\nimport { CountriesLoaderService } from './countries-loader.service';\n\n@Component({\n selector: 'sneat-country-selector',\n templateUrl: './country-selector.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n FormsModule,\n SelectFromListComponent,\n IonSegment,\n IonSegmentButton,\n IonItem,\n IonLabel,\n IonButton,\n ],\n})\nexport class CountrySelectorComponent implements OnInit, OnChanges {\n private readonly countriesLoader = inject(CountriesLoaderService);\n\n protected readonly geoRegions: readonly {\n readonly id: string;\n title?: string;\n }[] = [\n { id: 'Europe' },\n { id: 'Asia' },\n { id: 'Americas' },\n { id: 'Africa' },\n ];\n\n // TODO: Skipped for migration because:\n // Your application code writes to the input. This prevents migration.\n @Input({ required: true }) countryID?: string;\n\n readonly defaultCountryID = input<string>();\n\n readonly readonly = input(false);\n readonly disabled = input(false);\n readonly label = input('search');\n readonly canBeUnknown = input(false);\n\n @Output() readonly countryIDChange = new EventEmitter<string>();\n\n protected readonly filter = signal<string>('');\n protected readonly geoRegion = signal<GeoRegion | 'All' | 'Americas'>('All');\n protected readonly allCountries = signal<readonly ICountry[]>([]);\n protected readonly unknownCountry = signal<ICountry | undefined>(undefined);\n\n // True when the country list failed to load — surfaced in the UI with a Retry.\n protected readonly loadFailed = this.countriesLoader.loadFailed;\n\n protected readonly countries = computed<readonly ISelectItem[]>(() => {\n const allCountries = this.allCountries();\n if (allCountries.length === 0) {\n return []; // Data not loaded yet\n }\n\n const filter = this.filter();\n let countriesToShow: readonly ISelectItem[] = filter\n ? allCountries\n : allCountries.filter(\n (c) =>\n this.geoRegion() === 'All' ||\n (this.geoRegion() === 'Americas' &&\n (c.geoRegions.includes('North America') ||\n c.geoRegions.includes('South America'))) ||\n c.geoRegions.includes(this.geoRegion() as GeoRegion),\n );\n const unknownCountry = this.unknownCountry();\n if (this.canBeUnknown() && unknownCountry) {\n countriesToShow = [...countriesToShow, unknownCountry];\n }\n return countriesToShow;\n });\n\n ngOnInit(): void {\n this.load();\n }\n\n private load(): void {\n this.countriesLoader.getCountries().then((countries) => {\n this.allCountries.set(countries);\n });\n this.countriesLoader.getUnknownCountry().then((unknown) => {\n this.unknownCountry.set(unknown);\n });\n }\n\n // Retry loading the country list after a failure (loadFailed()).\n protected retry(): void {\n this.countriesLoader.reload().then(() => this.load());\n }\n\n // protected geoRegion: GeoRegion | 'All' | 'Americas' = 'All';\n\n protected onChanged(): void {\n this.countryIDChange.emit(this.countryID);\n }\n\n protected onFilterChanged(filter: string): void {\n this.filter.set(filter);\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['country'] && this.countryID === '--' && !this.canBeUnknown()) {\n this.countryID = undefined;\n }\n const defaultCountryID = this.defaultCountryID();\n if (\n changes['defaultCountryID'] &&\n !this.countryID &&\n defaultCountryID &&\n defaultCountryID !== '--'\n ) {\n this.countryID = defaultCountryID;\n this.countryIDChange.emit(this.countryID);\n }\n }\n\n protected onRegionChanged(event: Event): void {\n this.geoRegion.set(\n (event as CustomEvent).detail.value as GeoRegion | 'All' | 'Americas',\n );\n }\n\n protected readonly filterCountryByCode = (\n item: ISelectItem,\n filter: string,\n ) => {\n const f = filter.trim().toUpperCase();\n const c = item as ICountry;\n return c.id === f || c.id3.startsWith(f);\n };\n}\n","@if (loadFailed()) {\n <ion-item color=\"danger\" lines=\"none\">\n <ion-label class=\"ion-text-wrap\">\n Couldn't load the list of countries. Check your connection and try again.\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"retry()\">Retry</ion-button>\n </ion-item>\n}\n\n<sneat-select-from-list\n [value]=\"countryID || ''\"\n [isFilterable]=\"true\"\n [filterLabel]=\"label()\"\n [label]=\"label()\"\n [items]=\"countries()\"\n [(ngModel)]=\"countryID\"\n (ngModelChange)=\"onChanged()\"\n [isReadonly]=\"readonly()\"\n [filterItem]=\"filterCountryByCode\"\n (filterChanged)=\"onFilterChanged($event)\"\n labelPlacement=\"start\"\n>\n @if (!countryID && !filter()) {\n <ion-segment [(ngModel)]=\"geoRegion\" (ionChange)=\"onRegionChanged($event)\">\n <ion-segment-button value=\"All\">All</ion-segment-button>\n @for (region of geoRegions; track region.id) {\n <ion-segment-button [value]=\"region.id\">\n <small>{{ region.id }}</small>\n </ion-segment-button>\n }\n </ion-segment>\n }\n</sneat-select-from-list>\n"]}
|
|
@@ -102,7 +102,9 @@ export class SubscriptionRecoveryComponent {
|
|
|
102
102
|
}
|
|
103
103
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SubscriptionRecoveryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
104
104
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SubscriptionRecoveryComponent, isStandalone: true, selector: "sneat-subscription-recovery", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
105
|
-
@if (
|
|
105
|
+
@if (
|
|
106
|
+
port && signedIn() && (busy() || error() || items().length || next())
|
|
107
|
+
) {
|
|
106
108
|
<ion-card aria-label="Unfinished subscriptions">
|
|
107
109
|
<ion-card-header
|
|
108
110
|
><ion-card-title
|
|
@@ -124,13 +126,14 @@ export class SubscriptionRecoveryComponent {
|
|
|
124
126
|
>
|
|
125
127
|
<ion-label class="ion-text-wrap"
|
|
126
128
|
><h2>{{ item.label }}</h2>
|
|
129
|
+
@if (item.description) {
|
|
130
|
+
<p>{{ item.description }}</p>
|
|
131
|
+
}
|
|
127
132
|
<p>{{ item.action }}</p></ion-label
|
|
128
133
|
>
|
|
129
134
|
</ion-item>
|
|
130
135
|
}
|
|
131
136
|
</ion-list>
|
|
132
|
-
} @else if (!busy() && !error()) {
|
|
133
|
-
<p>No subscriptions waiting for setup.</p>
|
|
134
137
|
}
|
|
135
138
|
@if (busy()) {
|
|
136
139
|
<p role="status">Checking your purchases…</p>
|
|
@@ -171,7 +174,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
171
174
|
IonNote,
|
|
172
175
|
],
|
|
173
176
|
template: `
|
|
174
|
-
@if (
|
|
177
|
+
@if (
|
|
178
|
+
port && signedIn() && (busy() || error() || items().length || next())
|
|
179
|
+
) {
|
|
175
180
|
<ion-card aria-label="Unfinished subscriptions">
|
|
176
181
|
<ion-card-header
|
|
177
182
|
><ion-card-title
|
|
@@ -193,13 +198,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
193
198
|
>
|
|
194
199
|
<ion-label class="ion-text-wrap"
|
|
195
200
|
><h2>{{ item.label }}</h2>
|
|
201
|
+
@if (item.description) {
|
|
202
|
+
<p>{{ item.description }}</p>
|
|
203
|
+
}
|
|
196
204
|
<p>{{ item.action }}</p></ion-label
|
|
197
205
|
>
|
|
198
206
|
</ion-item>
|
|
199
207
|
}
|
|
200
208
|
</ion-list>
|
|
201
|
-
} @else if (!busy() && !error()) {
|
|
202
|
-
<p>No subscriptions waiting for setup.</p>
|
|
203
209
|
}
|
|
204
210
|
@if (busy()) {
|
|
205
211
|
<p role="status">Checking your purchases…</p>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscription-recovery.component.js","sourceRoot":"","sources":["../../../../../../libs/components/src/lib/subscription-recovery/subscription-recovery.component.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,SAAS,EACT,cAAc,EAEd,MAAM,EACN,KAAK,EACL,MAAM,GACP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;;AAiBlD,wEAAwE;AACxE,MAAM,CAAC,MAAM,qBAAqB,GAChC,IAAI,cAAc,CAA2B,sBAAsB,CAAC,CAAC;AAqEvE,MAAM,OAAO,6BAA6B;IAnE1C;QAoEqB,SAAI,GAAG,MAAM,CAAC,qBAAqB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,UAAK,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;QACvC,UAAK,GAAG,KAAK,CAAC,2BAA2B;kFAAC,CAAC;QACjC,UAAK,GAAG,MAAM,CAA6B,EAAE;kFAAC,CAAC;QAC/C,SAAI,GAAG,MAAM,CAA2B,MAAM;iFAAC,CAAC;QAChD,aAAQ,GAAG,MAAM,CAAC,KAAK;qFAAC,CAAC;QACzB,SAAI,GAAG,MAAM,CAAC,KAAK;iFAAC,CAAC;QACrB,UAAK,GAAG,MAAM,CAAC,EAAE;kFAAC,CAAC;QACnB,SAAI,GAAG,MAAM,CAAC,EAAE;iFAAC,CAAC;QAC7B,eAAU,GAAG,CAAC,CAAC;QACf,uBAAkB,GAAG,CAAC,CAAC;QACvB,aAAQ,GAAkB,IAAI,CAAC;QAEtB,sBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,SAAS,CACrE,CAAC,MAAM,EAAE,EAAE;YACT,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,MAAM,kBAAkB,GAAG,EAAE,IAAI,CAAC,kBAAkB,CAAC;YACrD,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACrB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACzB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACzC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;gBACpE,OAAO;YACT,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACtD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE;gBACpD,IAAI,kBAAkB,KAAK,IAAI,CAAC,kBAAkB;oBAAE,OAAO;gBAC3D,IAAI,CAAC,UAAU,EAAE,CAAC;gBAClB,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;gBACnB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC/B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACnB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAClB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACrB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACnB,IAAI,EAAE;oBAAE,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAChC,CAAC,CAAC,CAAC;QACL,CAAC,CACF,CAAC;KAmCH;IAjCW,KAAK,CAAC,IAAI,CAAC,IAAa;QAChC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO;QACxD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;YACtD,IAAI,UAAU,KAAK,IAAI,CAAC,UAAU;gBAAE,OAAO;YAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;YACnE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;gBACb,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE;aAC1D,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,UAAU,KAAK,IAAI,CAAC,UAAU;gBAChC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;QACzE,CAAC;gBAAS,CAAC;YACT,IAAI,UAAU,KAAK,IAAI,CAAC,UAAU;gBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IACD,OAAO;QACL,IAAI,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO;QACxB,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClB,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IACD,WAAW;QACT,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;QACtB,IAAI,CAAC,iBAAiB,CAAC,WAAW,EAAE,CAAC;IACvC,CAAC;8GA1EU,6BAA6B;kGAA7B,6BAA6B,2NApD9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDT,4DA7DC,UAAU,kPACV,SAAS,sRACT,OAAO,yLACP,cAAc,+EACd,aAAa,sGACb,YAAY,sFACZ,OAAO,0NACP,QAAQ,6FACR,OAAO,yFACP,OAAO;;2FAsDE,6BAA6B;kBAnEzC,SAAS;mBAAC;oBACT,QAAQ,EAAE,6BAA6B;oBACvC,eAAe,EAAE,uBAAuB,CAAC,MAAM;oBAC/C,OAAO,EAAE;wBACP,UAAU;wBACV,SAAS;wBACT,OAAO;wBACP,cAAc;wBACd,aAAa;wBACb,YAAY;wBACZ,OAAO;wBACP,QAAQ;wBACR,OAAO;wBACP,OAAO;qBACR;oBACD,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDT;iBACF","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n InjectionToken,\n OnDestroy,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport { ActivatedRoute, RouterLink } from '@angular/router';\nimport { IonButton } from '@ionic/angular/ion-button';\nimport { IonCard } from '@ionic/angular/ion-card';\nimport { IonCardContent } from '@ionic/angular/ion-card-content';\nimport { IonCardHeader } from '@ionic/angular/ion-card-header';\nimport { IonCardTitle } from '@ionic/angular/ion-card-title';\nimport { IonItem } from '@ionic/angular/ion-item';\nimport { IonLabel } from '@ionic/angular/ion-label';\nimport { IonList } from '@ionic/angular/ion-list';\nimport { IonNote } from '@ionic/angular/ion-note';\n\nexport type SubscriptionRecoveryMode = 'live' | 'test';\nexport interface SubscriptionRecoveryItem {\n id: string;\n label: string;\n action: string;\n path: string;\n query: Record<string, string>;\n}\nexport interface SubscriptionRecoveryPort {\n observeIdentity(callback: (id: string | null) => void): () => void;\n load(\n mode: SubscriptionRecoveryMode,\n after: string,\n ): Promise<{ items: SubscriptionRecoveryItem[]; next?: string }>;\n}\n/** The host supplies authenticated read-only billing infrastructure. */\nexport const SUBSCRIPTION_RECOVERY =\n new InjectionToken<SubscriptionRecoveryPort>('SubscriptionRecovery');\n\n@Component({\n selector: 'sneat-subscription-recovery',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n RouterLink,\n IonButton,\n IonCard,\n IonCardContent,\n IonCardHeader,\n IonCardTitle,\n IonItem,\n IonLabel,\n IonList,\n IonNote,\n ],\n template: `\n @if (port && signedIn()) {\n <ion-card aria-label=\"Unfinished subscriptions\">\n <ion-card-header\n ><ion-card-title\n >{{ title() }}\n @if (mode() === 'test') {\n · Test mode\n }\n </ion-card-title></ion-card-header\n >\n <ion-card-content>\n @if (items().length) {\n <ion-list>\n @for (item of items(); track item.id) {\n <ion-item\n [routerLink]=\"item.path\"\n [queryParams]=\"item.query\"\n button\n detail\n >\n <ion-label class=\"ion-text-wrap\"\n ><h2>{{ item.label }}</h2>\n <p>{{ item.action }}</p></ion-label\n >\n </ion-item>\n }\n </ion-list>\n } @else if (!busy() && !error()) {\n <p>No subscriptions waiting for setup.</p>\n }\n @if (busy()) {\n <p role=\"status\">Checking your purchases…</p>\n }\n @if (error()) {\n <p role=\"alert\">{{ error() }}</p>\n <ion-button fill=\"outline\" (click)=\"load(false)\">Retry</ion-button>\n }\n @if (next() && !busy() && !error()) {\n <ion-button fill=\"clear\" (click)=\"load(true)\"\n >Show more purchases</ion-button\n >\n }\n <ion-note\n >Opening a purchase checks its current status. It does not charge\n you again.</ion-note\n >\n </ion-card-content>\n </ion-card>\n }\n `,\n})\nexport class SubscriptionRecoveryComponent implements OnDestroy {\n protected readonly port = inject(SUBSCRIPTION_RECOVERY, { optional: true });\n private readonly route = inject(ActivatedRoute);\n readonly title = input('Finish subscription setup');\n protected readonly items = signal<SubscriptionRecoveryItem[]>([]);\n protected readonly mode = signal<SubscriptionRecoveryMode>('live');\n protected readonly signedIn = signal(false);\n protected readonly busy = signal(false);\n protected readonly error = signal('');\n protected readonly next = signal('');\n private generation = 0;\n private observerGeneration = 0;\n private identity: string | null = null;\n private stopIdentity?: () => void;\n private readonly routeSubscription = this.route.queryParamMap.subscribe(\n (params) => {\n this.generation++;\n const observerGeneration = ++this.observerGeneration;\n this.stopIdentity?.();\n this.items.set([]);\n this.next.set('');\n this.error.set('');\n this.busy.set(false);\n this.signedIn.set(false);\n const values = params.getAll('checkout');\n if (values.length > 1 || (values.length === 1 && values[0] !== 'test'))\n return;\n this.mode.set(values[0] === 'test' ? 'test' : 'live');\n this.stopIdentity = this.port?.observeIdentity((id) => {\n if (observerGeneration !== this.observerGeneration) return;\n this.generation++;\n this.identity = id;\n this.signedIn.set(Boolean(id));\n this.items.set([]);\n this.next.set('');\n this.busy.set(false);\n this.error.set('');\n if (id) void this.load(false);\n });\n },\n );\n\n protected async load(more: boolean): Promise<void> {\n if (!this.port || !this.identity || this.busy()) return;\n const generation = this.generation;\n const after = more ? this.next() : '';\n this.busy.set(true);\n this.error.set('');\n try {\n const page = await this.port.load(this.mode(), after);\n if (generation !== this.generation) return;\n const items = more ? [...this.items(), ...page.items] : page.items;\n this.items.set([\n ...new Map(items.map((item) => [item.id, item])).values(),\n ]);\n this.next.set(page.next ?? '');\n } catch {\n if (generation === this.generation)\n this.error.set('Your purchases could not be checked. Please retry.');\n } finally {\n if (generation === this.generation) this.busy.set(false);\n }\n }\n refresh(): void {\n if (this.busy()) return;\n this.generation++;\n this.next.set('');\n void this.load(false);\n }\n ngOnDestroy(): void {\n this.generation++;\n this.observerGeneration++;\n this.stopIdentity?.();\n this.routeSubscription.unsubscribe();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"subscription-recovery.component.js","sourceRoot":"","sources":["../../../../../../libs/components/src/lib/subscription-recovery/subscription-recovery.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n InjectionToken,\n OnDestroy,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport { ActivatedRoute, RouterLink } from '@angular/router';\nimport { IonButton } from '@ionic/angular/ion-button';\nimport { IonCard } from '@ionic/angular/ion-card';\nimport { IonCardContent } from '@ionic/angular/ion-card-content';\nimport { IonCardHeader } from '@ionic/angular/ion-card-header';\nimport { IonCardTitle } from '@ionic/angular/ion-card-title';\nimport { IonItem } from '@ionic/angular/ion-item';\nimport { IonLabel } from '@ionic/angular/ion-label';\nimport { IonList } from '@ionic/angular/ion-list';\nimport { IonNote } from '@ionic/angular/ion-note';\n\nexport type SubscriptionRecoveryMode = 'live' | 'test';\nexport interface SubscriptionRecoveryItem {\n id: string;\n label: string;\n description?: string;\n action: string;\n path: string;\n query: Record<string, string>;\n}\nexport interface SubscriptionRecoveryPort {\n observeIdentity(callback: (id: string | null) => void): () => void;\n load(\n mode: SubscriptionRecoveryMode,\n after: string,\n ): Promise<{ items: SubscriptionRecoveryItem[]; next?: string }>;\n}\n/** The host supplies authenticated read-only billing infrastructure. */\nexport const SUBSCRIPTION_RECOVERY =\n new InjectionToken<SubscriptionRecoveryPort>('SubscriptionRecovery');\n\n@Component({\n selector: 'sneat-subscription-recovery',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n RouterLink,\n IonButton,\n IonCard,\n IonCardContent,\n IonCardHeader,\n IonCardTitle,\n IonItem,\n IonLabel,\n IonList,\n IonNote,\n ],\n template: `\n @if (\n port && signedIn() && (busy() || error() || items().length || next())\n ) {\n <ion-card aria-label=\"Unfinished subscriptions\">\n <ion-card-header\n ><ion-card-title\n >{{ title() }}\n @if (mode() === 'test') {\n · Test mode\n }\n </ion-card-title></ion-card-header\n >\n <ion-card-content>\n @if (items().length) {\n <ion-list>\n @for (item of items(); track item.id) {\n <ion-item\n [routerLink]=\"item.path\"\n [queryParams]=\"item.query\"\n button\n detail\n >\n <ion-label class=\"ion-text-wrap\"\n ><h2>{{ item.label }}</h2>\n @if (item.description) {\n <p>{{ item.description }}</p>\n }\n <p>{{ item.action }}</p></ion-label\n >\n </ion-item>\n }\n </ion-list>\n }\n @if (busy()) {\n <p role=\"status\">Checking your purchases…</p>\n }\n @if (error()) {\n <p role=\"alert\">{{ error() }}</p>\n <ion-button fill=\"outline\" (click)=\"load(false)\">Retry</ion-button>\n }\n @if (next() && !busy() && !error()) {\n <ion-button fill=\"clear\" (click)=\"load(true)\"\n >Show more purchases</ion-button\n >\n }\n <ion-note\n >Opening a purchase checks its current status. It does not charge\n you again.</ion-note\n >\n </ion-card-content>\n </ion-card>\n }\n `,\n})\nexport class SubscriptionRecoveryComponent implements OnDestroy {\n protected readonly port = inject(SUBSCRIPTION_RECOVERY, { optional: true });\n private readonly route = inject(ActivatedRoute);\n readonly title = input('Finish subscription setup');\n protected readonly items = signal<SubscriptionRecoveryItem[]>([]);\n protected readonly mode = signal<SubscriptionRecoveryMode>('live');\n protected readonly signedIn = signal(false);\n protected readonly busy = signal(false);\n protected readonly error = signal('');\n protected readonly next = signal('');\n private generation = 0;\n private observerGeneration = 0;\n private identity: string | null = null;\n private stopIdentity?: () => void;\n private readonly routeSubscription = this.route.queryParamMap.subscribe(\n (params) => {\n this.generation++;\n const observerGeneration = ++this.observerGeneration;\n this.stopIdentity?.();\n this.items.set([]);\n this.next.set('');\n this.error.set('');\n this.busy.set(false);\n this.signedIn.set(false);\n const values = params.getAll('checkout');\n if (values.length > 1 || (values.length === 1 && values[0] !== 'test'))\n return;\n this.mode.set(values[0] === 'test' ? 'test' : 'live');\n this.stopIdentity = this.port?.observeIdentity((id) => {\n if (observerGeneration !== this.observerGeneration) return;\n this.generation++;\n this.identity = id;\n this.signedIn.set(Boolean(id));\n this.items.set([]);\n this.next.set('');\n this.busy.set(false);\n this.error.set('');\n if (id) void this.load(false);\n });\n },\n );\n\n protected async load(more: boolean): Promise<void> {\n if (!this.port || !this.identity || this.busy()) return;\n const generation = this.generation;\n const after = more ? this.next() : '';\n this.busy.set(true);\n this.error.set('');\n try {\n const page = await this.port.load(this.mode(), after);\n if (generation !== this.generation) return;\n const items = more ? [...this.items(), ...page.items] : page.items;\n this.items.set([\n ...new Map(items.map((item) => [item.id, item])).values(),\n ]);\n this.next.set(page.next ?? '');\n } catch {\n if (generation === this.generation)\n this.error.set('Your purchases could not be checked. Please retry.');\n } finally {\n if (generation === this.generation) this.busy.set(false);\n }\n }\n refresh(): void {\n if (this.busy()) return;\n this.generation++;\n this.next.set('');\n void this.load(false);\n }\n ngOnDestroy(): void {\n this.generation++;\n this.observerGeneration++;\n this.stopIdentity?.();\n this.routeSubscription.unsubscribe();\n }\n}\n"]}
|