@fluid-topics/ft-app-context 1.3.5 → 1.3.7

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.
@@ -2,6 +2,7 @@ import { FtMessageContext, FtMetadataConfiguration, FtSession, FtUiLocale } from
2
2
  export interface FtAppContextProperties {
3
3
  baseUrl?: string;
4
4
  apiIntegrationIdentifier?: string;
5
+ apiIntegrationAppVersion?: string;
5
6
  uiLocale?: string;
6
7
  availableUiLocales?: Array<FtUiLocale>;
7
8
  editorMode?: boolean;
@@ -14,4 +15,5 @@ export interface FtAppContextProperties {
14
15
  openExternalDocumentInNewTab?: boolean;
15
16
  navigatorOnline?: boolean;
16
17
  forcedOffline?: boolean;
18
+ authenticationRequired?: boolean;
17
19
  }
package/build/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./mixins/withDateFormat";
2
2
  export * from "./redux-stores/FtAppInfoStore";
3
3
  export * from "./redux-stores/FtUserAssetsStore";
4
+ export * from "./redux-stores/selectors/UserAssetSelectors";
4
5
  export * from "./services";
5
6
  export * from "./ft-app-context.styles";
6
7
  export * from "./ft-app-context.properties";
package/build/index.js CHANGED
@@ -3,6 +3,7 @@ import { FtAppContext } from "./ft-app-context";
3
3
  export * from "./mixins/withDateFormat";
4
4
  export * from "./redux-stores/FtAppInfoStore";
5
5
  export * from "./redux-stores/FtUserAssetsStore";
6
+ export * from "./redux-stores/selectors/UserAssetSelectors";
6
7
  export * from "./services";
7
8
  export * from "./ft-app-context.styles";
8
9
  export * from "./ft-app-context.properties";
@@ -5,6 +5,7 @@ export declare const FtAppInfoStoreName = "ft-app-info";
5
5
  export interface FtAppInfoState {
6
6
  baseUrl: Optional<string>;
7
7
  apiIntegrationIdentifier: Optional<string>;
8
+ apiIntegrationAppVersion: Optional<string>;
8
9
  uiLocale: string;
9
10
  availableUiLocales: Array<FtUiLocale>;
10
11
  availableContentLocales: Array<FtSearchLocale>;
@@ -19,6 +20,7 @@ export interface FtAppInfoState {
19
20
  openExternalDocumentInNewTab: boolean;
20
21
  navigatorOnline: boolean;
21
22
  forcedOffline: boolean;
23
+ authenticationRequired: boolean;
22
24
  }
23
25
  export declare class AuthenticationChangeEvent extends CustomEvent<Optional<FtSession>> {
24
26
  static eventName: string;
@@ -21,6 +21,7 @@ export const ftAppInfoStore = FtReduxStore.get({
21
21
  initialState: {
22
22
  baseUrl: undefined,
23
23
  apiIntegrationIdentifier: undefined,
24
+ apiIntegrationAppVersion: undefined,
24
25
  uiLocale: document.documentElement.lang || "en-US",
25
26
  availableUiLocales: [],
26
27
  availableContentLocales: [],
@@ -35,5 +36,6 @@ export const ftAppInfoStore = FtReduxStore.get({
35
36
  openExternalDocumentInNewTab: false,
36
37
  navigatorOnline: true,
37
38
  forcedOffline: false,
39
+ authenticationRequired: false,
38
40
  },
39
41
  });
@@ -1,26 +1,62 @@
1
- import { FtBookmark, FtMySearch } from "@fluid-topics/public-api";
2
- import { EmptyReducers, FtReduxStore, Optional } from "@fluid-topics/ft-wc-utils";
1
+ import { FtBookmark, FtMySearch, FtUserAssetCount, FtUserAssetType } from "@fluid-topics/public-api";
2
+ import { FtReduxStore, Optional } from "@fluid-topics/ft-wc-utils";
3
+ import { UserAssetsCountService } from "../services/user-assets/UserAssetCountService";
3
4
  export declare const FtUserAssetsStoreName = "ft-user-assets";
5
+ export interface AssetCounts {
6
+ allAsset: Record<FtUserAssetType, number | undefined>;
7
+ bookmarkByMap: {
8
+ [mapId: string]: number;
9
+ };
10
+ }
4
11
  export interface FtUserAssetsState {
5
12
  savedSearches: Optional<Array<FtMySearch>>;
6
13
  bookmarks: Optional<Array<FtBookmark>>;
14
+ assetCounts: AssetCounts;
7
15
  }
8
- export type FtUserAssetsStore = FtReduxStore<FtUserAssetsState, EmptyReducers>;
16
+ declare const reducers: {
17
+ setAssetCount: (state: FtUserAssetsState, action: {
18
+ payload: {
19
+ assetCount: FtUserAssetCount;
20
+ };
21
+ type: string;
22
+ }) => void;
23
+ clearAssetCount: (state: FtUserAssetsState) => void;
24
+ setBookmarkCountByMap: (state: FtUserAssetsState, action: {
25
+ payload: {
26
+ count: number;
27
+ mapId: string;
28
+ };
29
+ type: string;
30
+ }) => void;
31
+ clearBookmarkCountByMap: (state: FtUserAssetsState) => void;
32
+ };
33
+ export type FtUserAssetsStateReducers = typeof reducers;
34
+ export type FtUserAssetsStore = FtReduxStore<FtUserAssetsState, FtUserAssetsStateReducers>;
9
35
  export declare const ftUserAssetsStore: FtUserAssetsStore;
10
- declare class UserAssetsActions {
36
+ export declare class UserAssetsActions {
37
+ private assetCountsService;
11
38
  private currentSession;
12
39
  private bookmarksAreUsed;
13
40
  private bookmarksService;
14
41
  private savedSearchesService;
15
- constructor();
42
+ constructor(assetCountsService?: UserAssetsCountService);
16
43
  private reloadWhenUserSessionChanges;
44
+ clearUserAssetCounts(): void;
17
45
  clear(): void;
18
46
  clearMySearches(): void;
19
47
  private clearMyBookmarks;
20
48
  reloadMySearches(): Promise<void>;
21
49
  reloadBookmarks(): Promise<void>;
50
+ loadAssetCount(assetType: FtUserAssetType): Promise<void>;
51
+ loadBookmarkByMapId(mapId: string): Promise<void>;
52
+ reloadAssetCount(assetType: FtUserAssetType): Promise<void>;
22
53
  registerBookmarkComponent(): Promise<void>;
23
54
  private updateBookmarksIfUsed;
24
55
  }
56
+ declare global {
57
+ interface Window {
58
+ FluidTopicsUserAssetsActions: UserAssetsActions;
59
+ }
60
+ }
25
61
  export declare const userAssetsActions: UserAssetsActions;
26
62
  export {};
@@ -1,17 +1,42 @@
1
+ import { FtUserAssetType } from "@fluid-topics/public-api";
1
2
  import { deepEqual, FtReduxStore } from "@fluid-topics/ft-wc-utils";
2
3
  import { SavedSearchesService } from "../services/SavedSearchesService";
3
4
  import { BookmarksService } from "../services/BookmarksService";
4
5
  import { ftAppInfoStore } from "./FtAppInfoStore";
6
+ import { UserAssetsCountService } from "../services/user-assets/UserAssetCountService";
5
7
  export const FtUserAssetsStoreName = "ft-user-assets";
8
+ const reducers = {
9
+ setAssetCount: (state, action) => {
10
+ const { userAssetType, count } = action.payload.assetCount;
11
+ state.assetCounts.allAsset[userAssetType] = count;
12
+ },
13
+ clearAssetCount: (state) => {
14
+ Object.values(FtUserAssetType).forEach(type => {
15
+ state.assetCounts.allAsset[type] = undefined;
16
+ });
17
+ },
18
+ setBookmarkCountByMap: (state, action) => {
19
+ const mapId = action.payload.mapId;
20
+ state.assetCounts.bookmarkByMap[mapId] = action.payload.count;
21
+ },
22
+ clearBookmarkCountByMap: (state) => {
23
+ state.assetCounts.bookmarkByMap = {};
24
+ },
25
+ };
6
26
  export const ftUserAssetsStore = FtReduxStore.get({
7
27
  name: FtUserAssetsStoreName,
28
+ reducers: reducers,
8
29
  initialState: {
9
30
  savedSearches: undefined,
10
31
  bookmarks: undefined,
32
+ assetCounts: {
33
+ allAsset: Object.fromEntries(Object.values(FtUserAssetType).map(key => [key, undefined])), bookmarkByMap: {},
34
+ },
11
35
  },
12
36
  });
13
- class UserAssetsActions {
14
- constructor() {
37
+ export class UserAssetsActions {
38
+ constructor(assetCountsService = new UserAssetsCountService()) {
39
+ this.assetCountsService = assetCountsService;
15
40
  this.currentSession = ftAppInfoStore.getState().session;
16
41
  this.bookmarksAreUsed = false;
17
42
  this.bookmarksService = new BookmarksService();
@@ -25,8 +50,13 @@ class UserAssetsActions {
25
50
  this.currentSession = session;
26
51
  this.clearMySearches(); // The search context automatically reload
27
52
  this.reloadBookmarks();
53
+ this.clearUserAssetCounts();
28
54
  }
29
55
  }
56
+ clearUserAssetCounts() {
57
+ ftUserAssetsStore.actions.clearAssetCount();
58
+ ftUserAssetsStore.actions.clearBookmarkCountByMap();
59
+ }
30
60
  clear() {
31
61
  this.clearMySearches();
32
62
  this.clearMyBookmarks();
@@ -48,6 +78,30 @@ class UserAssetsActions {
48
78
  this.bookmarksService.clearCache();
49
79
  await this.updateBookmarksIfUsed();
50
80
  }
81
+ async loadAssetCount(assetType) {
82
+ const assetCount = await this.assetCountsService.getUserAssetCount(assetType);
83
+ if (assetCount) {
84
+ ftUserAssetsStore.actions.setAssetCount({ assetCount: assetCount });
85
+ }
86
+ }
87
+ async loadBookmarkByMapId(mapId) {
88
+ const bookmarkCount = await this.assetCountsService.getUserBookmarkCountByMap(mapId);
89
+ if (bookmarkCount) {
90
+ ftUserAssetsStore.actions.setBookmarkCountByMap({ count: bookmarkCount.count, mapId: mapId });
91
+ }
92
+ }
93
+ // Used in GWT
94
+ async reloadAssetCount(assetType) {
95
+ this.assetCountsService.clearCache();
96
+ const isBookmarkByMapStored = Object.keys(ftUserAssetsStore.getState().assetCounts.bookmarkByMap).length !== 0;
97
+ if (assetType === FtUserAssetType.BOOKMARKS && isBookmarkByMapStored) {
98
+ ftUserAssetsStore.actions.clearBookmarkCountByMap();
99
+ }
100
+ const isAssetCountStored = ftUserAssetsStore.getState().assetCounts.allAsset[assetType] !== undefined;
101
+ if (isAssetCountStored) {
102
+ await this.loadAssetCount(assetType);
103
+ }
104
+ }
51
105
  async registerBookmarkComponent() {
52
106
  this.bookmarksAreUsed = true;
53
107
  await this.updateBookmarksIfUsed();
@@ -61,3 +115,6 @@ class UserAssetsActions {
61
115
  }
62
116
  }
63
117
  export const userAssetsActions = new UserAssetsActions();
118
+ if (window.FluidTopicsUserAssetsActions == null) {
119
+ window.FluidTopicsUserAssetsActions = userAssetsActions;
120
+ }
@@ -0,0 +1,2 @@
1
+ import { FtUserAssetType } from "@fluid-topics/public-api";
2
+ export declare const selectAssetCount: (type?: FtUserAssetType, mapId?: string) => number | undefined;
@@ -0,0 +1,23 @@
1
+ import { FtUserAssetType } from "@fluid-topics/public-api";
2
+ import { ftUserAssetsStore } from "../FtUserAssetsStore";
3
+ export const selectAssetCount = (type, mapId) => {
4
+ if (type === undefined) {
5
+ return undefined;
6
+ }
7
+ if (type === FtUserAssetType.BOOKMARKS && mapId) {
8
+ return resolveBookmarkByMapCount(mapId);
9
+ }
10
+ return resolveAssetCount(type);
11
+ };
12
+ const resolveBookmarkByMapCount = (mapId) => {
13
+ if (ftUserAssetsStore.getState().assetCounts.bookmarkByMap[mapId] === undefined) {
14
+ window.FluidTopicsUserAssetsActions.loadBookmarkByMapId(mapId);
15
+ }
16
+ return ftUserAssetsStore.getState().assetCounts.bookmarkByMap[mapId];
17
+ };
18
+ const resolveAssetCount = (type) => {
19
+ if (ftUserAssetsStore.getState().assetCounts.allAsset[type] === undefined) {
20
+ window.FluidTopicsUserAssetsActions.loadAssetCount(type);
21
+ }
22
+ return ftUserAssetsStore.getState().assetCounts.allAsset[type];
23
+ };
@@ -1,10 +1,12 @@
1
- import type { FtDocumentSearchEvent, FtDocumentStartDisplayEvent, FtKhubSearchEvent, FtTopicStartDisplayEvent, FtSearchPageSelectEvent, FtSearchResultOpenContextMenuEvent } from "@fluid-topics/public-api";
2
- import { FtService } from "./FtService";
3
- export declare class FtAnalyticsEventsService extends FtService {
4
- sendDocumentStartDisplayEvent(event: FtDocumentStartDisplayEvent): Promise<void>;
5
- sendTopicStartDisplayEvent(event: FtTopicStartDisplayEvent): Promise<void>;
6
- sendKhubSearchEvent(event: FtKhubSearchEvent): Promise<void>;
7
- sendDocumentSearchEvent(event: FtDocumentSearchEvent): Promise<void>;
8
- sendSearchPageSelectEvent(event: FtSearchPageSelectEvent): Promise<void>;
9
- sendSearchResultOpenContextMenuEvent(event: FtSearchResultOpenContextMenuEvent): Promise<void>;
1
+ import type { FtDocumentSearchEvent, FtDocumentStartDisplayEvent, FtKhubSearchEvent, FtSearchPageSelectEvent, FtSearchResultOpenContextMenuEvent, FtTopicStartDisplayEvent } from "@fluid-topics/public-api";
2
+ import { FtServiceWithCache } from "./FtServiceWithCache";
3
+ export declare class FtAnalyticsEventsService extends FtServiceWithCache {
4
+ sendDocumentStartDisplayEvent(event: FtDocumentStartDisplayEvent): Promise<string>;
5
+ sendTopicStartDisplayEvent(event: FtTopicStartDisplayEvent): Promise<string>;
6
+ sendKhubSearchEvent(event: FtKhubSearchEvent): Promise<string>;
7
+ sendDocumentSearchEvent(event: FtDocumentSearchEvent): Promise<string>;
8
+ sendSearchPageSelectEvent(event: FtSearchPageSelectEvent): Promise<string>;
9
+ sendSearchResultOpenContextMenuEvent(event: FtSearchResultOpenContextMenuEvent): Promise<string>;
10
+ private sortEventArrays;
11
+ private sendEvents;
10
12
  }
@@ -1,51 +1,58 @@
1
- import { FtService } from "./FtService";
2
- export class FtAnalyticsEventsService extends FtService {
1
+ import { FtServiceWithCache } from "./FtServiceWithCache";
2
+ export class FtAnalyticsEventsService extends FtServiceWithCache {
3
3
  async sendDocumentStartDisplayEvent(event) {
4
- return (await this.awaitApi)
5
- .sendEvents([event])
6
- .catch(reason => {
7
- console.info("Failed to send document.start_display event");
8
- console.debug(reason);
9
- });
4
+ return this.sendEvents([this.sortEventArrays(event)], "Failed to send document.start_display event");
10
5
  }
11
6
  async sendTopicStartDisplayEvent(event) {
12
- return (await this.awaitApi)
13
- .sendEvents([event])
14
- .catch(reason => {
15
- console.info("Failed to send topic.start_display event");
16
- console.debug(reason);
17
- });
7
+ return this.sendEvents([this.sortEventArrays(event)], "Failed to send topic.start_display event");
18
8
  }
19
9
  async sendKhubSearchEvent(event) {
20
- return (await this.awaitApi)
21
- .sendEvents([event])
22
- .catch(reason => {
23
- console.info("Failed to send khub.search event");
24
- console.debug(reason);
25
- });
10
+ return this.sendEvents([this.sortEventArrays(event)], "Failed to send khub.search event");
26
11
  }
27
12
  async sendDocumentSearchEvent(event) {
28
- return (await this.awaitApi)
29
- .sendEvents([event])
30
- .catch(reason => {
31
- console.info("Failed to send document.search event");
32
- console.debug(reason);
33
- });
13
+ return this.sendEvents([this.sortEventArrays(event)], "Failed to send document.search event");
34
14
  }
35
15
  async sendSearchPageSelectEvent(event) {
36
- return (await this.awaitApi)
37
- .sendEvents([event])
38
- .catch(reason => {
39
- console.info("Failed to send search_page.select event");
40
- console.debug(reason);
41
- });
16
+ return this.sendEvents([this.sortEventArrays(event)], "Failed to send search_page.select event");
42
17
  }
43
18
  async sendSearchResultOpenContextMenuEvent(event) {
44
- return (await this.awaitApi)
45
- .sendEvents([event])
46
- .catch(reason => {
47
- console.info("Failed to send search_result.open_context_menu event");
48
- console.debug(reason);
19
+ return this.sendEvents([this.sortEventArrays(event)], "Failed to send search_result.open_context_menu event");
20
+ }
21
+ sortEventArrays(object) {
22
+ const entries = Object.entries(object).map(([key, value]) => {
23
+ if (Array.isArray(value)) {
24
+ return [key, value.map((val) => typeof val === 'object' && val != null && !Array.isArray(val) ? this.sortEventArrays(val) : val)
25
+ .sort((a, b) => {
26
+ if (typeof a === 'object' && a !== null && 'key' in a) {
27
+ return a.key.localeCompare(b.key);
28
+ }
29
+ else if (typeof a === 'string' && typeof b === 'string') {
30
+ return a.localeCompare(b);
31
+ }
32
+ else {
33
+ return String(a).localeCompare(String(b));
34
+ }
35
+ })];
36
+ }
37
+ else if (typeof value === 'object' && value !== null) {
38
+ return [key, this.sortEventArrays(value)];
39
+ }
40
+ else {
41
+ return [key, value];
42
+ }
49
43
  });
44
+ return Object.fromEntries(entries);
45
+ }
46
+ sendEvents(events, errorMessage) {
47
+ const fakeResponse = "anything not undefined";
48
+ return this.cache.get("analytics-event-" + this.hash(events), async () => {
49
+ return (await this.awaitApi).sendEvents(events)
50
+ .then(() => fakeResponse)
51
+ .catch(reason => {
52
+ console.info(errorMessage);
53
+ console.debug(reason);
54
+ return fakeResponse;
55
+ });
56
+ }, 1000);
50
57
  }
51
58
  }
@@ -0,0 +1 @@
1
+ export declare function getOrDefaultIfMissingRequiredAuthentication<T>(provider: () => Promise<T>, defaultValue: T): Promise<T>;
@@ -0,0 +1,9 @@
1
+ import { ftAppInfoStore } from "../redux-stores/FtAppInfoStore";
2
+ const store = ftAppInfoStore;
3
+ export function getOrDefaultIfMissingRequiredAuthentication(provider, defaultValue) {
4
+ var _a;
5
+ if (store.getState().authenticationRequired && !((_a = store.getState().session) === null || _a === void 0 ? void 0 : _a.sessionAuthenticated)) {
6
+ return Promise.resolve(defaultValue);
7
+ }
8
+ return provider();
9
+ }
@@ -6,4 +6,6 @@ export declare class FtServiceWithCache extends FtService {
6
6
  cache: CacheRegistry;
7
7
  constructor(withCommonCache?: boolean, overrideApi?: FluidTopicsApi);
8
8
  clearCache(): void;
9
+ private sortObjectFields;
10
+ hash(object: any): string;
9
11
  }
@@ -4,6 +4,13 @@ export class FtServiceWithCache extends FtService {
4
4
  constructor(withCommonCache = true, overrideApi) {
5
5
  var _a;
6
6
  super(overrideApi);
7
+ this.sortObjectFields = (_, value) => {
8
+ if (typeof value !== "object" || value == null || Array.isArray(value)) {
9
+ return value;
10
+ }
11
+ return Object.fromEntries(Object.entries(value)
12
+ .sort(([ka], [kb]) => ka.localeCompare(kb)));
13
+ };
7
14
  // Have a different cache for each child class
8
15
  let constructor = this.constructor;
9
16
  constructor.commonCache = (_a = constructor.commonCache) !== null && _a !== void 0 ? _a : new CacheRegistry();
@@ -12,4 +19,8 @@ export class FtServiceWithCache extends FtService {
12
19
  clearCache() {
13
20
  this.cache.clearAll();
14
21
  }
22
+ hash(object) {
23
+ return String(Array.from(JSON.stringify(object, this.sortObjectFields))
24
+ .reduce((hash, char) => 0 | (31 * hash + char.charCodeAt(0)), 0));
25
+ }
15
26
  }
@@ -0,0 +1,8 @@
1
+ import { FtUserAssetCount, FtUserAssetType } from "@fluid-topics/public-api";
2
+ import { FtServiceWithCache } from "../FtServiceWithCache";
3
+ export declare class UserAssetsCountService extends FtServiceWithCache {
4
+ private readonly CACHE_DURATION;
5
+ getUserAssetCount(userAssetType: FtUserAssetType): Promise<FtUserAssetCount | undefined>;
6
+ getUserBookmarkCountByMap(mapId: string): Promise<FtUserAssetCount | undefined>;
7
+ private isAuthenticated;
8
+ }
@@ -0,0 +1,24 @@
1
+ import { ftAppInfoStore } from "../../redux-stores/FtAppInfoStore";
2
+ import { FtServiceWithCache } from "../FtServiceWithCache";
3
+ export class UserAssetsCountService extends FtServiceWithCache {
4
+ constructor() {
5
+ super(...arguments);
6
+ this.CACHE_DURATION = 3 * 60 * 1000;
7
+ }
8
+ async getUserAssetCount(userAssetType) {
9
+ if (!this.isAuthenticated()) {
10
+ return undefined;
11
+ }
12
+ return this.cache.get(`user-asset-count-${userAssetType}`, async () => (await this.awaitApi).get(`/internal/api/webapp/user/assets/count/${userAssetType}`), this.CACHE_DURATION);
13
+ }
14
+ async getUserBookmarkCountByMap(mapId) {
15
+ if (!this.isAuthenticated()) {
16
+ return undefined;
17
+ }
18
+ return this.cache.get(`user-bookmark-count-by-map-${mapId}`, async () => (await this.awaitApi).get(`/internal/api/webapp/user/assets/count/BOOKMARKS/${mapId}`), this.CACHE_DURATION);
19
+ }
20
+ isAuthenticated() {
21
+ const session = ftAppInfoStore.getState().session;
22
+ return Boolean(session === null || session === void 0 ? void 0 : session.sessionAuthenticated);
23
+ }
24
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-topics/ft-app-context",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "description": "Global application context for Fluid Topics integrations",
5
5
  "keywords": [
6
6
  "Lit"
@@ -19,11 +19,11 @@
19
19
  "url": "ssh://git@scm.mrs.antidot.net:2222/fluidtopics/ft-web-components.git"
20
20
  },
21
21
  "dependencies": {
22
- "@fluid-topics/ft-wc-utils": "1.3.5",
22
+ "@fluid-topics/ft-wc-utils": "1.3.7",
23
23
  "lit": "3.1.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@fluid-topics/public-api": "1.0.99"
27
27
  },
28
- "gitHead": "87377b99617257593de238b08c41d2638617f501"
28
+ "gitHead": "83a6f0c292e6669cc067b7aa40fea8a4a0a61b7f"
29
29
  }