@sneat/app 0.6.1 → 0.8.0
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 +3 -0
- package/esm2022/index.js.map +1 -1
- package/esm2022/lib/base-app.component.js +6 -21
- package/esm2022/lib/base-app.component.js.map +1 -1
- package/esm2022/lib/get-standard-sneat-providers.js +8 -1
- package/esm2022/lib/get-standard-sneat-providers.js.map +1 -1
- package/esm2022/lib/init-firebase.js +20 -2
- package/esm2022/lib/init-firebase.js.map +1 -1
- package/esm2022/lib/init-helpers.js +26 -0
- package/esm2022/lib/init-helpers.js.map +1 -1
- package/esm2022/lib/page-title.service.js +42 -0
- package/esm2022/lib/page-title.service.js.map +1 -0
- package/esm2022/lib/route-title.js +24 -0
- package/esm2022/lib/route-title.js.map +1 -0
- package/esm2022/lib/sneat-title.strategy.js +28 -0
- package/esm2022/lib/sneat-title.strategy.js.map +1 -0
- package/index.d.ts +3 -0
- package/lib/base-app.component.d.ts +0 -2
- package/lib/init-helpers.d.ts +2 -0
- package/lib/page-title.service.d.ts +10 -0
- package/lib/route-title.d.ts +2 -0
- package/lib/sneat-title.strategy.d.ts +8 -0
- package/package.json +1 -1
- package/tsconfig.lib.prod.tsbuildinfo +1 -1
package/esm2022/index.js
CHANGED
|
@@ -10,4 +10,7 @@ export * from './lib/contact-extensions';
|
|
|
10
10
|
export * from './lib/get-standard-sneat-providers';
|
|
11
11
|
export * from './lib/app-specific-providers';
|
|
12
12
|
export * from './lib/capacitator-http.service';
|
|
13
|
+
export * from './lib/page-title.service';
|
|
14
|
+
export * from './lib/sneat-title.strategy';
|
|
15
|
+
export * from './lib/route-title';
|
|
13
16
|
//# sourceMappingURL=index.js.map
|
package/esm2022/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/app/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oCAAoC,CAAC;AACnD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC","sourcesContent":["export * from './environments';\nexport * from './lib/base-app.component';\nexport * from './lib/sneat-base-app';\nexport * from './lib/get-standard-sneat-imports';\nexport * from './lib/app-component.service';\nexport * from './environments/environment.local';\nexport * from './lib/init-firebase';\nexport * from './lib/init-helpers';\nexport * from './lib/contact-extensions';\nexport * from './lib/get-standard-sneat-providers';\nexport * from './lib/app-specific-providers';\nexport * from './lib/capacitator-http.service';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/app/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oCAAoC,CAAC;AACnD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC","sourcesContent":["export * from './environments';\nexport * from './lib/base-app.component';\nexport * from './lib/sneat-base-app';\nexport * from './lib/get-standard-sneat-imports';\nexport * from './lib/app-component.service';\nexport * from './environments/environment.local';\nexport * from './lib/init-firebase';\nexport * from './lib/init-helpers';\nexport * from './lib/contact-extensions';\nexport * from './lib/get-standard-sneat-providers';\nexport * from './lib/app-specific-providers';\nexport * from './lib/capacitator-http.service';\nexport * from './lib/page-title.service';\nexport * from './lib/sneat-title.strategy';\nexport * from './lib/route-title';\n"]}
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { inject } from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
|
2
|
+
import { NavigationEnd, Router } from '@angular/router';
|
|
4
3
|
import { SneatAuthStateService, TelegramAuthService, } from '@sneat/auth-core';
|
|
5
|
-
import { AnalyticsService, clearCurrentSpace, TopMenuService } from '@sneat/core';
|
|
4
|
+
import { AnalyticsService, clearCurrentSpace, TopMenuService, } from '@sneat/core';
|
|
6
5
|
import { getRedirectResult } from 'firebase/auth';
|
|
7
6
|
import { filter } from 'rxjs';
|
|
7
|
+
import { getRouteTitle } from './route-title';
|
|
8
8
|
export class BaseAppComponent {
|
|
9
9
|
constructor() {
|
|
10
10
|
this.telegramAuthService = inject(TelegramAuthService);
|
|
11
11
|
this.router = inject(Router);
|
|
12
|
-
this.activatedRoute = inject(ActivatedRoute);
|
|
13
12
|
this.analyticsService = inject(AnalyticsService);
|
|
14
|
-
this.titleService = inject(Title);
|
|
15
13
|
this.authStateService = inject(SneatAuthStateService);
|
|
16
14
|
this.topMenuService = inject(TopMenuService); // used in templates
|
|
17
15
|
this.telegramAuthService.authenticateIfTelegramWebApp();
|
|
@@ -27,23 +25,13 @@ export class BaseAppComponent {
|
|
|
27
25
|
this.router.events
|
|
28
26
|
.pipe(filter((event) => event instanceof NavigationEnd))
|
|
29
27
|
.subscribe((event) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
let title = route?.snapshot.data['title'];
|
|
35
|
-
if (title) {
|
|
36
|
-
const spaceType = route?.snapshot?.paramMap?.get('spaceType');
|
|
37
|
-
if (spaceType) {
|
|
38
|
-
title = `${capitalizeFirstLetter(spaceType)} ${title}`;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
28
|
+
// The document title itself is owned by SneatTitleStrategy; here we only
|
|
29
|
+
// reuse the same derived page title for the analytics pageview event.
|
|
30
|
+
const title = getRouteTitle(this.router.routerState.snapshot);
|
|
41
31
|
this.analyticsService.logEvent('$pageview', {
|
|
42
32
|
page_path: event.urlAfterRedirects,
|
|
43
33
|
title,
|
|
44
34
|
});
|
|
45
|
-
title = title ? `${title} @ Sneat.App` : 'Sneat.App'; // Default title
|
|
46
|
-
this.titleService.setTitle(title);
|
|
47
35
|
this.forgetCurrentSpaceIfLeftSpaces(event.urlAfterRedirects);
|
|
48
36
|
});
|
|
49
37
|
}
|
|
@@ -64,7 +52,4 @@ export class BaseAppComponent {
|
|
|
64
52
|
clearCurrentSpace();
|
|
65
53
|
}
|
|
66
54
|
}
|
|
67
|
-
function capitalizeFirstLetter(text) {
|
|
68
|
-
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
69
|
-
}
|
|
70
55
|
//# sourceMappingURL=base-app.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-app.component.js","sourceRoot":"","sources":["../../../../../libs/app/src/lib/base-app.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"base-app.component.js","sourceRoot":"","sources":["../../../../../libs/app/src/lib/base-app.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAEL,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,OAAO,gBAAgB;IAS3B;QARiB,wBAAmB,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;QAClD,WAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QACxB,qBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAC5C,qBAAgB,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAC/C,mBAAc,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,oBAAoB;QAK9E,IAAI,CAAC,mBAAmB,CAAC,4BAA4B,EAAE,CAAC;QACxD,kEAAkE;QAClE,mEAAmE;QACnE,2EAA2E;QAC3E,qEAAqE;QACrE,yDAAyD;QACzD,iBAAiB,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAC5D,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,GAAG,CAAC,CAC/C,CAAC;QACF,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE;YAC9C,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,MAAM;aACf,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,YAAY,aAAa,CAAC,CAAC;aACvD,SAAS,CAAC,CAAC,KAAoB,EAAE,EAAE;YAClC,yEAAyE;YACzE,sEAAsE;YACtE,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YAC9D,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,WAAW,EAAE;gBAC1C,SAAS,EAAE,KAAK,CAAC,iBAAiB;gBAClC,KAAK;aACN,CAAC,CAAC;YAEH,IAAI,CAAC,8BAA8B,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;IACP,CAAC;IAED,gFAAgF;IAChF,kFAAkF;IAClF,+EAA+E;IAC/E,gFAAgF;IAChF,iEAAiE;IACzD,8BAA8B,CAAC,GAAW;QAChD,IAAI,IAAI,CAAC,UAAU,KAAK,eAAe,EAAE,CAAC;YACxC,OAAO;QACT,CAAC;QACD,IACE,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC;YACzB,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC;YACxB,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,EAC7B,CAAC;YACD,OAAO;QACT,CAAC;QACD,iBAAiB,EAAE,CAAC;IACtB,CAAC;CACF","sourcesContent":["import { inject } from '@angular/core';\nimport { NavigationEnd, Router } from '@angular/router';\nimport {\n AuthStatus,\n SneatAuthStateService,\n TelegramAuthService,\n} from '@sneat/auth-core';\nimport {\n AnalyticsService,\n clearCurrentSpace,\n TopMenuService,\n} from '@sneat/core';\nimport { getRedirectResult } from 'firebase/auth';\nimport { filter } from 'rxjs';\nimport { getRouteTitle } from './route-title';\n\nexport class BaseAppComponent {\n private readonly telegramAuthService = inject(TelegramAuthService);\n private readonly router = inject(Router);\n private readonly analyticsService = inject(AnalyticsService);\n private readonly authStateService = inject(SneatAuthStateService);\n protected readonly topMenuService = inject(TopMenuService); // used in templates\n\n private authStatus?: AuthStatus;\n\n constructor() {\n this.telegramAuthService.authenticateIfTelegramWebApp();\n // Complete any pending signInWithRedirect when the app boots. The\n // authStateService's onIdTokenChanged listener then propagates the\n // signed-in user. Resolves to null (harmless) when sign-in used a popup or\n // there is no pending redirect. Lives here so every app that extends\n // BaseAppComponent gets redirect-based sign-in for free.\n getRedirectResult(this.authStateService.fbAuth).catch((err) =>\n console.error('getRedirectResult failed', err),\n );\n this.authStateService.authState.subscribe((s) => {\n this.authStatus = s.status;\n });\n this.router.events\n .pipe(filter((event) => event instanceof NavigationEnd))\n .subscribe((event: NavigationEnd) => {\n // The document title itself is owned by SneatTitleStrategy; here we only\n // reuse the same derived page title for the analytics pageview event.\n const title = getRouteTitle(this.router.routerState.snapshot);\n this.analyticsService.logEvent('$pageview', {\n page_path: event.urlAfterRedirects,\n title,\n });\n\n this.forgetCurrentSpaceIfLeftSpaces(event.urlAfterRedirects);\n });\n }\n\n // Keep the persisted current space in sync with where an authenticated user is:\n // once they navigate to a non-space page, forget it so login does not bounce them\n // back to a space they had left. Auth routes are ignored so signing out from a\n // space still restores it; the `authenticated` guard preserves the value during\n // the logged-out login flow so the login page can still read it.\n private forgetCurrentSpaceIfLeftSpaces(url: string): void {\n if (this.authStatus !== 'authenticated') {\n return;\n }\n if (\n url.startsWith('/space/') ||\n url.startsWith('/login') ||\n url.startsWith('/signed-out')\n ) {\n return;\n }\n clearCurrentSpace();\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { provideHttpClient } from '@angular/common/http';
|
|
2
2
|
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
|
3
|
-
import { RouteReuseStrategy } from '@angular/router';
|
|
3
|
+
import { RouteReuseStrategy, TitleStrategy } from '@angular/router';
|
|
4
4
|
import { IonicRouteStrategy } from '@ionic/angular/standalone';
|
|
5
5
|
import { provideIonicAngular } from '@ionic/angular/standalone';
|
|
6
6
|
import { DefaultSneatAppApiBaseUrl, SneatApiBaseUrl } from '@sneat/api';
|
|
@@ -11,6 +11,7 @@ import { RANDOM_ID_OPTIONS } from '@sneat/random';
|
|
|
11
11
|
import { AppComponentService } from './app-component.service';
|
|
12
12
|
import { EnvConfigToken } from '@sneat/core';
|
|
13
13
|
import { getAngularFireProviders } from './init-firebase';
|
|
14
|
+
import { SneatTitleStrategy } from './sneat-title.strategy';
|
|
14
15
|
// import { getAngularFireImports } from './init-firebase';
|
|
15
16
|
import { registerPosthog } from './register-posthog';
|
|
16
17
|
export function getStandardSneatProviders(environmentConfig) {
|
|
@@ -32,6 +33,12 @@ export function getStandardSneatProviders(environmentConfig) {
|
|
|
32
33
|
provide: RouteReuseStrategy,
|
|
33
34
|
useClass: IonicRouteStrategy,
|
|
34
35
|
},
|
|
36
|
+
{
|
|
37
|
+
// Owns document.title for every app: composes the route's data.title
|
|
38
|
+
// with APP_INFO.appTitle, and lets pages override via PageTitleService.
|
|
39
|
+
provide: TitleStrategy,
|
|
40
|
+
useClass: SneatTitleStrategy,
|
|
41
|
+
},
|
|
35
42
|
{
|
|
36
43
|
provide: SneatApiBaseUrl,
|
|
37
44
|
useValue: environmentConfig.useNgrok
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-standard-sneat-providers.js","sourceRoot":"","sources":["../../../../../libs/app/src/lib/get-standard-sneat-providers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"get-standard-sneat-providers.js","sourceRoot":"","sources":["../../../../../libs/app/src/lib/get-standard-sneat-providers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAsB,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,2DAA2D;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,UAAU,yBAAyB,CACvC,iBAAqC;IAErC,eAAe;IACf,uDAAuD;IACvD,wDAAwD;IACxD,KAAK;IACL,IAAI,iBAAiB,CAAC,OAAO,EAAE,CAAC;QAC9B,eAAe,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,SAAS,GAAG;QAChB,iBAAiB,EAAE;QACnB,kBAAkB,EAAE;QACpB,mBAAmB,EAAE;QACrB,sBAAsB,EAAE;QACxB,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,EAAE;QACpD,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,iBAAiB,EAAE;QACxD;YACE,OAAO,EAAE,kBAAkB;YAC3B,QAAQ,EAAE,kBAAkB;SAC7B;QACD;YACE,qEAAqE;YACrE,wEAAwE;YACxE,OAAO,EAAE,aAAa;YACtB,QAAQ,EAAE,kBAAkB;SAC7B;QACD;YACE,OAAO,EAAE,eAAe;YACxB,QAAQ,EAAE,iBAAiB,CAAC,QAAQ;gBAClC,CAAC,CAAC,KAAK,QAAQ,CAAC,IAAI,MAAM;gBAC1B,CAAC,CAAC,iBAAiB,CAAC,cAAc,CAAC,QAAQ;oBACzC,CAAC,CAAC,gCAAgC,CAAC,8BAA8B;oBACjE,CAAC,CAAC,yBAAyB;SAChC;QACD;YACE,OAAO,EAAE,iBAAiB;YAC1B,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE;SACrB;QACD,uBAAuB,CAAC,iBAAiB,CAAC,cAAc,CAAC;QACzD,qBAAqB,CAAC,iBAAiB,CAAC;QACxC,mBAAmB,EAAE,+CAA+C;QACpE,cAAc;QACd,mBAAmB;KACpB,CAAC;IACF,IAAI,iBAAiB,CAAC,MAAM,EAAE,CAAC;QAC7B,SAAS,CAAC,IAAI,CAAC,2BAA2B,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import { provideHttpClient } from '@angular/common/http';\nimport { EnvironmentProviders, Provider } from '@angular/core';\nimport { provideAnimationsAsync } from '@angular/platform-browser/animations/async';\nimport { RouteReuseStrategy, TitleStrategy } from '@angular/router';\nimport { IonicRouteStrategy } from '@ionic/angular/standalone';\nimport { provideIonicAngular } from '@ionic/angular/standalone';\nimport { DefaultSneatAppApiBaseUrl, SneatApiBaseUrl } from '@sneat/api';\nimport { TelegramAuthService } from '@sneat/auth-core';\nimport { LOGGER_FACTORY, loggerFactory, TopMenuService } from '@sneat/core';\nimport {\n provideErrorLogger,\n provideSentryAppInitializer,\n provideSneatAnalytics,\n} from '@sneat/logging';\nimport { RANDOM_ID_OPTIONS } from '@sneat/random';\nimport { AppComponentService } from './app-component.service';\nimport { EnvConfigToken, IEnvironmentConfig } from '@sneat/core';\nimport { getAngularFireProviders } from './init-firebase';\nimport { SneatTitleStrategy } from './sneat-title.strategy';\n\n// import { getAngularFireImports } from './init-firebase';\n\nimport { registerPosthog } from './register-posthog';\n\nexport function getStandardSneatProviders(\n environmentConfig: IEnvironmentConfig,\n): readonly (Provider | EnvironmentProviders)[] {\n // console.log(\n // \t'getStandardSneatProviders(), environmentConfig:' +\n // \t\tJSON.stringify(environmentConfig, undefined, '\\t'),\n // );\n if (environmentConfig.posthog) {\n registerPosthog(environmentConfig.posthog);\n }\n\n const providers = [\n provideHttpClient(),\n provideErrorLogger(),\n provideIonicAngular(),\n provideAnimationsAsync(),\n { provide: LOGGER_FACTORY, useValue: loggerFactory },\n { provide: EnvConfigToken, useValue: environmentConfig },\n {\n provide: RouteReuseStrategy,\n useClass: IonicRouteStrategy,\n },\n {\n // Owns document.title for every app: composes the route's data.title\n // with APP_INFO.appTitle, and lets pages override via PageTitleService.\n provide: TitleStrategy,\n useClass: SneatTitleStrategy,\n },\n {\n provide: SneatApiBaseUrl,\n useValue: environmentConfig.useNgrok\n ? `//${location.host}/v0/`\n : environmentConfig.firebaseConfig.emulator\n ? 'https://local-api.sneat.ws/v0/' // 'http://localhost:4300/v0/'\n : DefaultSneatAppApiBaseUrl,\n },\n {\n provide: RANDOM_ID_OPTIONS,\n useValue: { len: 9 },\n },\n getAngularFireProviders(environmentConfig.firebaseConfig),\n provideSneatAnalytics(environmentConfig),\n AppComponentService, // TODO: check if it's used and probably remove\n TopMenuService,\n TelegramAuthService,\n ];\n if (environmentConfig.sentry) {\n providers.push(provideSentryAppInitializer(environmentConfig.sentry));\n }\n return providers;\n}\n"]}
|
|
@@ -5,6 +5,24 @@ import { Capacitor } from '@capacitor/core';
|
|
|
5
5
|
import { provideFirebaseApp, initializeApp, FirebaseApp, } from '@angular/fire/app';
|
|
6
6
|
import { getAuth } from 'firebase/auth';
|
|
7
7
|
import { getFirestore, provideFirestore } from '@angular/fire/firestore';
|
|
8
|
+
import { isLocalhost } from './init-helpers';
|
|
9
|
+
// Defense in depth: a production bundle should never carry emulator config
|
|
10
|
+
// (use appEnvironmentConfig()), but if one slips through, refuse to point real
|
|
11
|
+
// users at 127.0.0.1 — that just yields "refused to connect". Warn loudly so a
|
|
12
|
+
// post-deploy smoke test (and the console) surfaces the misconfiguration.
|
|
13
|
+
function emulatorAllowed(hasEmulator) {
|
|
14
|
+
if (!hasEmulator) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (isLocalhost()) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
console.error('[init-firebase] Emulator config present on a non-localhost host ' +
|
|
21
|
+
`("${typeof location !== 'undefined' ? location.hostname : '?'}") — ` +
|
|
22
|
+
'ignoring it. This is a build/deploy misconfiguration: a production ' +
|
|
23
|
+
'build shipped the emulator environment.');
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
8
26
|
export function provideFireApp(firebaseConfig) {
|
|
9
27
|
return provideFirebaseApp(() => initFirebase(firebaseConfig));
|
|
10
28
|
}
|
|
@@ -16,7 +34,7 @@ export function getAngularFireProviders(firebaseConfig) {
|
|
|
16
34
|
const fbApp = injector.get(FirebaseApp);
|
|
17
35
|
const firestore = getFirestore(fbApp);
|
|
18
36
|
const { emulator } = firebaseConfig;
|
|
19
|
-
if (emulator) {
|
|
37
|
+
if (emulator && emulatorAllowed(!!emulator)) {
|
|
20
38
|
connectFirestoreEmulator(firestore, emulator.firestoreHost || '127.0.0.1', emulator.firestorePort);
|
|
21
39
|
if (emulator.firestorePort === 443) {
|
|
22
40
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
@@ -39,7 +57,7 @@ export function getAngularFireProviders(firebaseConfig) {
|
|
|
39
57
|
auth = getAuth(fbApp);
|
|
40
58
|
}
|
|
41
59
|
const { emulator } = firebaseConfig;
|
|
42
|
-
if (emulator?.authPort) {
|
|
60
|
+
if (emulator?.authPort && emulatorAllowed(!!emulator)) {
|
|
43
61
|
// alert('Using firebase auth emulator');
|
|
44
62
|
const authUrl = `${emulator.authPort === 443 ? 'https' : 'http'}://${emulator.authHost || '127.0.0.1'}:${emulator.authPort}`;
|
|
45
63
|
// console.log('authUrl: ', authUrl);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init-firebase.js","sourceRoot":"","sources":["../../../../../libs/app/src/lib/init-firebase.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAEL,mBAAmB,EACnB,yBAAyB,EACzB,cAAc,EACd,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"init-firebase.js","sourceRoot":"","sources":["../../../../../libs/app/src/lib/init-firebase.ts"],"names":[],"mappings":"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","sourcesContent":["import { EnvironmentProviders, Type } from '@angular/core';\nimport { getAnalytics, provideAnalytics } from '@angular/fire/analytics';\nimport {\n Auth,\n connectAuthEmulator,\n indexedDBLocalPersistence,\n initializeAuth,\n provideAuth,\n} from '@angular/fire/auth';\nimport { connectFirestoreEmulator } from '@angular/fire/firestore';\nimport { Capacitor } from '@capacitor/core';\nimport { IFirebaseConfig } from '@sneat/core';\nimport {\n provideFirebaseApp,\n initializeApp,\n FirebaseApp,\n} from '@angular/fire/app';\nimport { getAuth } from 'firebase/auth';\nimport { getFirestore, provideFirestore } from '@angular/fire/firestore';\nimport { isLocalhost } from './init-helpers';\n\n// Defense in depth: a production bundle should never carry emulator config\n// (use appEnvironmentConfig()), but if one slips through, refuse to point real\n// users at 127.0.0.1 — that just yields \"refused to connect\". Warn loudly so a\n// post-deploy smoke test (and the console) surfaces the misconfiguration.\nfunction emulatorAllowed(hasEmulator: boolean): boolean {\n if (!hasEmulator) {\n return false;\n }\n if (isLocalhost()) {\n return true;\n }\n console.error(\n '[init-firebase] Emulator config present on a non-localhost host ' +\n `(\"${typeof location !== 'undefined' ? location.hostname : '?'}\") — ` +\n 'ignoring it. This is a build/deploy misconfiguration: a production ' +\n 'build shipped the emulator environment.',\n );\n return false;\n}\n\nexport function provideFireApp(firebaseConfig: IFirebaseConfig) {\n return provideFirebaseApp(() => initFirebase(firebaseConfig));\n}\n\nexport function getAngularFireProviders(\n firebaseConfig: IFirebaseConfig,\n): EnvironmentProviders[] {\n const providers = [\n provideFirebaseApp(() => initFirebase(firebaseConfig)),\n provideFirestore((injector) => {\n // console.log('AngularFire: provideFirestore');\n const fbApp = injector.get(FirebaseApp);\n const firestore = getFirestore(fbApp);\n const { emulator } = firebaseConfig;\n if (emulator && emulatorAllowed(!!emulator)) {\n connectFirestoreEmulator(\n firestore,\n emulator.firestoreHost || '127.0.0.1',\n emulator.firestorePort,\n );\n if (emulator.firestorePort === 443) {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-expect-error\n firestore['_settings']['ssl'] = true;\n }\n }\n return firestore;\n }),\n provideAuth((injector) => {\n // console.log('AngularFire: provideAuth');\n const fbApp = injector.get<FirebaseApp>(FirebaseApp as Type<FirebaseApp>);\n let auth: Auth;\n if (Capacitor.isNativePlatform()) {\n auth = initializeAuth(fbApp, {\n persistence: indexedDBLocalPersistence,\n });\n } else {\n auth = getAuth(fbApp);\n }\n const { emulator } = firebaseConfig;\n if (emulator?.authPort && emulatorAllowed(!!emulator)) {\n // alert('Using firebase auth emulator');\n const authUrl = `${emulator.authPort === 443 ? 'https' : 'http'}://${emulator.authHost || '127.0.0.1'}:${emulator.authPort}`;\n // console.log('authUrl: ', authUrl);\n // noinspection HttpUrlsUsage\n connectAuthEmulator(auth, authUrl);\n }\n return auth;\n }),\n ];\n if (firebaseConfig?.measurementId !== 'G-PROVIDE_IF_NEEDED') {\n providers.push(\n provideAnalytics(() => {\n // const fbApp = injector.get<FirebaseApp>(\n // \tFirebaseApp as Type<FirebaseApp>,\n // );\n // const fbAnalytics = getAnalytics(fbApp);\n const fbAnalytics = getAnalytics();\n return fbAnalytics;\n }),\n );\n }\n return providers;\n}\n\nfunction initFirebase(firebaseConfig: IFirebaseConfig): FirebaseApp {\n return initializeApp(firebaseConfig);\n}\n"]}
|
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
import { emulatorEnvironmentConfig } from '../environments/environment.base';
|
|
2
|
+
// True only when the app is actually running on a developer machine. Used to
|
|
3
|
+
// decide, at RUNTIME, whether to use the Firebase emulator — see
|
|
4
|
+
// appEnvironmentConfig() below.
|
|
5
|
+
export function isLocalhost() {
|
|
6
|
+
return (typeof location !== 'undefined' &&
|
|
7
|
+
['localhost', '127.0.0.1', '[::1]', '0.0.0.0'].includes(location.hostname));
|
|
8
|
+
}
|
|
9
|
+
// Fail-safe environment selection. Use the Firebase emulator ONLY when running
|
|
10
|
+
// on localhost; every deployed domain gets the production config passed in.
|
|
11
|
+
//
|
|
12
|
+
// Because the emulator-vs-production decision is made at runtime from the
|
|
13
|
+
// hostname — NOT from a build-time `fileReplacements` swap — a mis-built or
|
|
14
|
+
// mis-deployed bundle can never ship the emulator config to production. This
|
|
15
|
+
// structurally eliminates the recurring "127.0.0.1 refused to connect" class of
|
|
16
|
+
// bug: forgetting `--configuration production`, a stale `dist/`, an nx cache
|
|
17
|
+
// hit, or deploying a dev build can no longer point real users at the emulator.
|
|
18
|
+
//
|
|
19
|
+
// Apps should define a single environment.ts:
|
|
20
|
+
// export const fooEnvironmentConfig = appEnvironmentConfig({ ...prod config });
|
|
21
|
+
// and drop environment.prod.ts + the production fileReplacements entirely.
|
|
22
|
+
export function appEnvironmentConfig(prod) {
|
|
23
|
+
return isLocalhost()
|
|
24
|
+
? appSpecificConfig(emulatorEnvironmentConfig)
|
|
25
|
+
: prod;
|
|
26
|
+
}
|
|
1
27
|
function firebaseApiKey(useEmulators, apiKey) {
|
|
2
28
|
return useEmulators ? 'emulator-does-not-need-api-key' : apiKey;
|
|
3
29
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init-helpers.js","sourceRoot":"","sources":["../../../../../libs/app/src/lib/init-helpers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init-helpers.js","sourceRoot":"","sources":["../../../../../libs/app/src/lib/init-helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAE7E,6EAA6E;AAC7E,iEAAiE;AACjE,gCAAgC;AAChC,MAAM,UAAU,WAAW;IACzB,OAAO,CACL,OAAO,QAAQ,KAAK,WAAW;QAC/B,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAC3E,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,4EAA4E;AAC5E,EAAE;AACF,0EAA0E;AAC1E,4EAA4E;AAC5E,6EAA6E;AAC7E,gFAAgF;AAChF,6EAA6E;AAC7E,gFAAgF;AAChF,EAAE;AACF,8CAA8C;AAC9C,kFAAkF;AAClF,2EAA2E;AAC3E,MAAM,UAAU,oBAAoB,CAClC,IAAwB;IAExB,OAAO,WAAW,EAAE;QAClB,CAAC,CAAC,iBAAiB,CAAC,yBAAyB,CAAC;QAC9C,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED,SAAS,cAAc,CAAC,YAAqB,EAAE,MAAc;IAC3D,OAAO,YAAY,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC,CAAC,MAAM,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,YAAqB,EACrB,SAAiB;IAEjB,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AACxD,CAAC;AAED,8FAA8F;AAC9F,2CAA2C;AAC3C,uBAAuB;AACvB,kBAAkB;AAClB,2CAA2C;AAC3C,IAAI;AAEJ,4BAA4B;AAC5B,MAAM,UAAU,iBAAiB,CAC/B,SAA6B;IAG7B,IAAI,MAAM,GAAuB;QAC/B,GAAG,SAAS;QACZ,cAAc,EAAE;YACd,GAAG,SAAS,CAAC,cAAc;SAC5B;KACF,CAAC;IACF,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAAC;IAClC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC;IACrD,MAAM,SAAS,GAAG,iBAAiB,CAAC,WAAW,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IAC3E,MAAM,GAAG;QACP,GAAG,MAAM;QACT,cAAc,EAAE;YACd,GAAG,cAAc;YACjB,MAAM,EAAE,cAAc,CAAC,WAAW,EAAE,cAAc,CAAC,MAAM,CAAC;YAC1D,SAAS,EAAE,SAAS;SACrB;KACF,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { IEnvironmentConfig } from '@sneat/core';\nimport { emulatorEnvironmentConfig } from '../environments/environment.base';\n\n// True only when the app is actually running on a developer machine. Used to\n// decide, at RUNTIME, whether to use the Firebase emulator — see\n// appEnvironmentConfig() below.\nexport function isLocalhost(): boolean {\n return (\n typeof location !== 'undefined' &&\n ['localhost', '127.0.0.1', '[::1]', '0.0.0.0'].includes(location.hostname)\n );\n}\n\n// Fail-safe environment selection. Use the Firebase emulator ONLY when running\n// on localhost; every deployed domain gets the production config passed in.\n//\n// Because the emulator-vs-production decision is made at runtime from the\n// hostname — NOT from a build-time `fileReplacements` swap — a mis-built or\n// mis-deployed bundle can never ship the emulator config to production. This\n// structurally eliminates the recurring \"127.0.0.1 refused to connect\" class of\n// bug: forgetting `--configuration production`, a stale `dist/`, an nx cache\n// hit, or deploying a dev build can no longer point real users at the emulator.\n//\n// Apps should define a single environment.ts:\n// export const fooEnvironmentConfig = appEnvironmentConfig({ ...prod config });\n// and drop environment.prod.ts + the production fileReplacements entirely.\nexport function appEnvironmentConfig(\n prod: IEnvironmentConfig,\n): IEnvironmentConfig {\n return isLocalhost()\n ? appSpecificConfig(emulatorEnvironmentConfig)\n : prod;\n}\n\nfunction firebaseApiKey(useEmulators: boolean, apiKey: string): string {\n return useEmulators ? 'emulator-does-not-need-api-key' : apiKey;\n}\n\nexport function firebaseProjectId(\n useEmulators: boolean,\n projectId: string,\n): string {\n return useEmulators ? 'demo-' + projectId : projectId;\n}\n\n// function firebaseDatabaseURL(useEmulators: boolean, projectId: string): string | undefined{\n// \t// noinspection SpellCheckingInspection\n// \treturn useEmulators\n// \t\t? undefined :\n// \t\t`https://${projectId}.firebaseio.com`;\n// }\n\n// TODO: document why needed\nexport function appSpecificConfig(\n envConfig: IEnvironmentConfig,\n // appConfig: IAppSpecificConfig,\n): IEnvironmentConfig {\n let config: IEnvironmentConfig = {\n ...envConfig,\n firebaseConfig: {\n ...envConfig.firebaseConfig,\n },\n };\n const { firebaseConfig } = config;\n const useEmulator = !!config.firebaseConfig.emulator;\n const projectId = firebaseProjectId(useEmulator, firebaseConfig.projectId);\n config = {\n ...config,\n firebaseConfig: {\n ...firebaseConfig,\n apiKey: firebaseApiKey(useEmulator, firebaseConfig.apiKey),\n projectId: projectId,\n },\n };\n return config;\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { inject, Injectable } from '@angular/core';
|
|
2
|
+
import { Title } from '@angular/platform-browser';
|
|
3
|
+
import { APP_INFO } from '@sneat/core';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
// Owns the browser/document title. A page can set it imperatively during its
|
|
6
|
+
// lifecycle, e.g. once a record loads:
|
|
7
|
+
// inject(PageTitleService).setPageTitle(space.title)
|
|
8
|
+
//
|
|
9
|
+
// Composes the "bare" page title with the app name (APP_INFO.appTitle, e.g.
|
|
10
|
+
// "Debtus.app") => "Debts @ Debtus.app", or just the app name when no page
|
|
11
|
+
// title is given. SneatTitleStrategy calls this on every navigation with the
|
|
12
|
+
// route's declarative data.title, so a value set by one page does not leak into
|
|
13
|
+
// the next.
|
|
14
|
+
//
|
|
15
|
+
// If the app forgot to register APP_INFO we fall back to the current host name
|
|
16
|
+
// (e.g. "debtus.app") and warn once, so the title is still sensible while
|
|
17
|
+
// surfacing the misconfiguration to the developer.
|
|
18
|
+
export class PageTitleService {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.title = inject(Title);
|
|
21
|
+
this.appInfo = inject(APP_INFO, { optional: true });
|
|
22
|
+
if (this.appInfo?.appTitle) {
|
|
23
|
+
this.appTitle = this.appInfo.appTitle;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.appTitle = location.host;
|
|
27
|
+
console.warn(`[PageTitleService] APP_INFO is not provided (or has no appTitle). ` +
|
|
28
|
+
`Falling back to host "${this.appTitle}" for the document title. ` +
|
|
29
|
+
`Register it via provideAppInfo({ appId, appTitle }).`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
setPageTitle(pageTitle) {
|
|
33
|
+
this.title.setTitle(pageTitle ? `${pageTitle} @ ${this.appTitle}` : this.appTitle);
|
|
34
|
+
}
|
|
35
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: PageTitleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
36
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: PageTitleService, providedIn: 'root' }); }
|
|
37
|
+
}
|
|
38
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: PageTitleService, decorators: [{
|
|
39
|
+
type: Injectable,
|
|
40
|
+
args: [{ providedIn: 'root' }]
|
|
41
|
+
}], ctorParameters: () => [] });
|
|
42
|
+
//# sourceMappingURL=page-title.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-title.service.js","sourceRoot":"","sources":["../../../../../libs/app/src/lib/page-title.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAY,MAAM,aAAa,CAAC;;AAEjD,6EAA6E;AAC7E,uCAAuC;AACvC,uDAAuD;AACvD,EAAE;AACF,4EAA4E;AAC5E,2EAA2E;AAC3E,6EAA6E;AAC7E,gFAAgF;AAChF,YAAY;AACZ,EAAE;AACF,+EAA+E;AAC/E,0EAA0E;AAC1E,mDAAmD;AAEnD,MAAM,OAAO,gBAAgB;IAK3B;QAJiB,UAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACtB,YAAO,GAAG,MAAM,CAAW,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAIxE,IAAI,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;YAC3B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC;YAC9B,OAAO,CAAC,IAAI,CACV,oEAAoE;gBAClE,yBAAyB,IAAI,CAAC,QAAQ,4BAA4B;gBAClE,sDAAsD,CACzD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,YAAY,CAAC,SAA6B;QACxC,IAAI,CAAC,KAAK,CAAC,QAAQ,CACjB,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAC9D,CAAC;IACJ,CAAC;8GAtBU,gBAAgB;kHAAhB,gBAAgB,cADH,MAAM;;2FACnB,gBAAgB;kBAD5B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE","sourcesContent":["import { inject, Injectable } from '@angular/core';\nimport { Title } from '@angular/platform-browser';\nimport { APP_INFO, IAppInfo } from '@sneat/core';\n\n// Owns the browser/document title. A page can set it imperatively during its\n// lifecycle, e.g. once a record loads:\n// inject(PageTitleService).setPageTitle(space.title)\n//\n// Composes the \"bare\" page title with the app name (APP_INFO.appTitle, e.g.\n// \"Debtus.app\") => \"Debts @ Debtus.app\", or just the app name when no page\n// title is given. SneatTitleStrategy calls this on every navigation with the\n// route's declarative data.title, so a value set by one page does not leak into\n// the next.\n//\n// If the app forgot to register APP_INFO we fall back to the current host name\n// (e.g. \"debtus.app\") and warn once, so the title is still sensible while\n// surfacing the misconfiguration to the developer.\n@Injectable({ providedIn: 'root' })\nexport class PageTitleService {\n private readonly title = inject(Title);\n private readonly appInfo = inject<IAppInfo>(APP_INFO, { optional: true });\n private readonly appTitle: string;\n\n constructor() {\n if (this.appInfo?.appTitle) {\n this.appTitle = this.appInfo.appTitle;\n } else {\n this.appTitle = location.host;\n console.warn(\n `[PageTitleService] APP_INFO is not provided (or has no appTitle). ` +\n `Falling back to host \"${this.appTitle}\" for the document title. ` +\n `Register it via provideAppInfo({ appId, appTitle }).`,\n );\n }\n }\n\n setPageTitle(pageTitle: string | undefined): void {\n this.title.setTitle(\n pageTitle ? `${pageTitle} @ ${this.appTitle}` : this.appTitle,\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Derives the page title from route config: walks to the deepest activated
|
|
2
|
+
// route, reads its `data['title']`, and (for space routes) prefixes the space
|
|
3
|
+
// type, e.g. data.title="Debts" on a family space => "Family Debts". This is
|
|
4
|
+
// the single source of the "bare" page title (without the app-name suffix),
|
|
5
|
+
// shared by SneatTitleStrategy (document.title) and BaseAppComponent (analytics)
|
|
6
|
+
// so both stay in sync.
|
|
7
|
+
export function getRouteTitle(snapshot) {
|
|
8
|
+
let route = snapshot.root;
|
|
9
|
+
while (route.firstChild) {
|
|
10
|
+
route = route.firstChild;
|
|
11
|
+
}
|
|
12
|
+
let title = route.data['title'];
|
|
13
|
+
if (title) {
|
|
14
|
+
const spaceType = route.paramMap.get('spaceType');
|
|
15
|
+
if (spaceType) {
|
|
16
|
+
title = `${capitalizeFirstLetter(spaceType)} ${title}`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return title;
|
|
20
|
+
}
|
|
21
|
+
function capitalizeFirstLetter(text) {
|
|
22
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=route-title.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-title.js","sourceRoot":"","sources":["../../../../../libs/app/src/lib/route-title.ts"],"names":[],"mappings":"AAEA,2EAA2E;AAC3E,8EAA8E;AAC9E,6EAA6E;AAC7E,4EAA4E;AAC5E,iFAAiF;AACjF,wBAAwB;AACxB,MAAM,UAAU,aAAa,CAAC,QAA6B;IACzD,IAAI,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC;IAC1B,OAAO,KAAK,CAAC,UAAU,EAAE,CAAC;QACxB,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC;IAC3B,CAAC;IACD,IAAI,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAuB,CAAC;IACtD,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAClD,IAAI,SAAS,EAAE,CAAC;YACd,KAAK,GAAG,GAAG,qBAAqB,CAAC,SAAS,CAAC,IAAI,KAAK,EAAE,CAAC;QACzD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC","sourcesContent":["import { RouterStateSnapshot } from '@angular/router';\n\n// Derives the page title from route config: walks to the deepest activated\n// route, reads its `data['title']`, and (for space routes) prefixes the space\n// type, e.g. data.title=\"Debts\" on a family space => \"Family Debts\". This is\n// the single source of the \"bare\" page title (without the app-name suffix),\n// shared by SneatTitleStrategy (document.title) and BaseAppComponent (analytics)\n// so both stay in sync.\nexport function getRouteTitle(snapshot: RouterStateSnapshot): string | undefined {\n let route = snapshot.root;\n while (route.firstChild) {\n route = route.firstChild;\n }\n let title = route.data['title'] as string | undefined;\n if (title) {\n const spaceType = route.paramMap.get('spaceType');\n if (spaceType) {\n title = `${capitalizeFirstLetter(spaceType)} ${title}`;\n }\n }\n return title;\n}\n\nfunction capitalizeFirstLetter(text: string): string {\n return text.charAt(0).toUpperCase() + text.slice(1);\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { inject, Injectable } from '@angular/core';
|
|
2
|
+
import { TitleStrategy } from '@angular/router';
|
|
3
|
+
import { PageTitleService } from './page-title.service';
|
|
4
|
+
import { getRouteTitle } from './route-title';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
// Single place that owns document.title for every Sneat-based app. On each
|
|
7
|
+
// navigation it reads the route's declarative `data['title']` (via getRouteTitle)
|
|
8
|
+
// and hands it to PageTitleService, which composes it with the app name.
|
|
9
|
+
//
|
|
10
|
+
// Pages can override the title imperatively during their lifecycle by calling
|
|
11
|
+
// PageTitleService.setPageTitle(...) directly; the next navigation resets it.
|
|
12
|
+
//
|
|
13
|
+
// Registered once in getStandardSneatProviders, so every app inherits it.
|
|
14
|
+
export class SneatTitleStrategy extends TitleStrategy {
|
|
15
|
+
constructor() {
|
|
16
|
+
super(...arguments);
|
|
17
|
+
this.pageTitleService = inject(PageTitleService);
|
|
18
|
+
}
|
|
19
|
+
updateTitle(snapshot) {
|
|
20
|
+
this.pageTitleService.setPageTitle(getRouteTitle(snapshot));
|
|
21
|
+
}
|
|
22
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SneatTitleStrategy, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
23
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SneatTitleStrategy }); }
|
|
24
|
+
}
|
|
25
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SneatTitleStrategy, decorators: [{
|
|
26
|
+
type: Injectable
|
|
27
|
+
}] });
|
|
28
|
+
//# sourceMappingURL=sneat-title.strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sneat-title.strategy.js","sourceRoot":"","sources":["../../../../../libs/app/src/lib/sneat-title.strategy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAuB,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;;AAE9C,2EAA2E;AAC3E,kFAAkF;AAClF,yEAAyE;AACzE,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,EAAE;AACF,0EAA0E;AAE1E,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IADrD;;QAEmB,qBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;KAK9D;IAHU,WAAW,CAAC,QAA6B;QAChD,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9D,CAAC;8GALU,kBAAkB;kHAAlB,kBAAkB;;2FAAlB,kBAAkB;kBAD9B,UAAU","sourcesContent":["import { inject, Injectable } from '@angular/core';\nimport { RouterStateSnapshot, TitleStrategy } from '@angular/router';\nimport { PageTitleService } from './page-title.service';\nimport { getRouteTitle } from './route-title';\n\n// Single place that owns document.title for every Sneat-based app. On each\n// navigation it reads the route's declarative `data['title']` (via getRouteTitle)\n// and hands it to PageTitleService, which composes it with the app name.\n//\n// Pages can override the title imperatively during their lifecycle by calling\n// PageTitleService.setPageTitle(...) directly; the next navigation resets it.\n//\n// Registered once in getStandardSneatProviders, so every app inherits it.\n@Injectable()\nexport class SneatTitleStrategy extends TitleStrategy {\n private readonly pageTitleService = inject(PageTitleService);\n\n override updateTitle(snapshot: RouterStateSnapshot): void {\n this.pageTitleService.setPageTitle(getRouteTitle(snapshot));\n }\n}\n"]}
|
package/index.d.ts
CHANGED
|
@@ -10,3 +10,6 @@ export * from './lib/contact-extensions';
|
|
|
10
10
|
export * from './lib/get-standard-sneat-providers';
|
|
11
11
|
export * from './lib/app-specific-providers';
|
|
12
12
|
export * from './lib/capacitator-http.service';
|
|
13
|
+
export * from './lib/page-title.service';
|
|
14
|
+
export * from './lib/sneat-title.strategy';
|
|
15
|
+
export * from './lib/route-title';
|
|
@@ -2,9 +2,7 @@ import { TopMenuService } from '@sneat/core';
|
|
|
2
2
|
export declare class BaseAppComponent {
|
|
3
3
|
private readonly telegramAuthService;
|
|
4
4
|
private readonly router;
|
|
5
|
-
private readonly activatedRoute;
|
|
6
5
|
private readonly analyticsService;
|
|
7
|
-
private readonly titleService;
|
|
8
6
|
private readonly authStateService;
|
|
9
7
|
protected readonly topMenuService: TopMenuService;
|
|
10
8
|
private authStatus?;
|
package/lib/init-helpers.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { IEnvironmentConfig } from '@sneat/core';
|
|
2
|
+
export declare function isLocalhost(): boolean;
|
|
3
|
+
export declare function appEnvironmentConfig(prod: IEnvironmentConfig): IEnvironmentConfig;
|
|
2
4
|
export declare function firebaseProjectId(useEmulators: boolean, projectId: string): string;
|
|
3
5
|
export declare function appSpecificConfig(envConfig: IEnvironmentConfig): IEnvironmentConfig;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
export declare class PageTitleService {
|
|
3
|
+
private readonly title;
|
|
4
|
+
private readonly appInfo;
|
|
5
|
+
private readonly appTitle;
|
|
6
|
+
constructor();
|
|
7
|
+
setPageTitle(pageTitle: string | undefined): void;
|
|
8
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PageTitleService, never>;
|
|
9
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<PageTitleService>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { RouterStateSnapshot, TitleStrategy } from '@angular/router';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export declare class SneatTitleStrategy extends TitleStrategy {
|
|
4
|
+
private readonly pageTitleService;
|
|
5
|
+
updateTitle(snapshot: RouterStateSnapshot): void;
|
|
6
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SneatTitleStrategy, never>;
|
|
7
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<SneatTitleStrategy>;
|
|
8
|
+
}
|