@c-rex/contexts 0.1.9 → 0.1.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c-rex/contexts",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src"
@@ -16,6 +16,10 @@
16
16
  "./search": {
17
17
  "types": "./src/search.tsx",
18
18
  "import": "./src/search.tsx"
19
+ },
20
+ "./highlight-provider": {
21
+ "types": "./src/highlight-provider.tsx",
22
+ "import": "./src/highlight-provider.tsx"
19
23
  }
20
24
  },
21
25
  "scripts": {
@@ -34,8 +38,8 @@
34
38
  },
35
39
  "dependencies": {
36
40
  "next": "^14",
37
- "react": "^18",
38
- "react-dom": "^18",
41
+ "react": "^18.3.1",
42
+ "react-dom": "^18.3.1",
39
43
  "@c-rex/core": "*",
40
44
  "@c-rex/constants": "*",
41
45
  "@c-rex/services": "*",
@@ -1,23 +1,19 @@
1
1
  "use client"
2
2
 
3
- import { CONTENT_LANG_KEY, CREX_TOKEN_HEADER_KEY, UI_LANG_KEY, UI_LANG_OPTIONS } from '@c-rex/constants'
3
+ import { CREX_TOKEN_HEADER_KEY, UI_LANG_OPTIONS } from '@c-rex/constants'
4
4
  import { ConfigInterface, LanguageAndCountries, AvailableVersionsInterface } from '@c-rex/interfaces'
5
- import { call, getCookie, getFromCookieString, setCookie } from '@c-rex/utils'
5
+ import { call } from '@c-rex/utils'
6
6
  import { getConfigs } from '@c-rex/utils/next-cookies'
7
7
  import React, { createContext, useContext, useEffect, useState } from 'react'
8
+ import { useLanguageStore } from '@c-rex/components/language-store'
8
9
 
9
10
  type AppConfigContextType = {
10
11
  error: Error | null
11
- uiLang: string | null
12
12
  configs: ConfigInterface
13
13
  loading: boolean
14
14
  packageID: string | null
15
- contentLang: string | null
16
15
  articleLang: string | null
17
16
  availableVersions: AvailableVersionsInterface[] | null
18
- availableLanguagesAndCountries: LanguageAndCountries[]
19
- setUiLang: (lang: string | null) => void
20
- setContentLang: (lang: string | null) => void
21
17
  setAvailableVersions: (versions: AvailableVersionsInterface[] | null) => void
22
18
  setPackageID: (id: string | null) => void
23
19
  setArticleLang: (lang: string | null) => void
@@ -27,31 +23,34 @@ const AppConfigContext = createContext<AppConfigContextType | undefined>(undefin
27
23
 
28
24
  export const AppConfigProvider = ({ children }: { children: React.ReactNode }) => {
29
25
  const [error, setError] = useState<Error | null>(null)
30
- const [uiLang, setUiLang] = useState<string | null>(null)
31
26
  const [configs, setConfigs] = useState<ConfigInterface | null>(null)
32
27
  const [loading, setLoading] = useState(true)
33
28
  const [packageID, setPackageID] = useState<string | null>(null)
34
29
  const [articleLang, setArticleLang] = useState<string | null>(null)
35
- const [contentLang, setContentLang] = useState<string | null>(null)
36
30
  const [availableVersions, setAvailableVersions] = useState<AvailableVersionsInterface[] | null>(null)
37
- const [availableLanguagesAndCountries, setAvailableLanguagesAndCountries] = useState<LanguageAndCountries[] | null>(null)
31
+
38
32
 
39
33
  const manageUILanguage = async (configs: ConfigInterface): Promise<void> => {
40
- let locale = getFromCookieString(document.cookie, UI_LANG_KEY)
34
+ const uiLang = useLanguageStore.getState().uiLang;
35
+ const setUiLang = useLanguageStore.getState().setUiLang;
36
+ let locale = uiLang
37
+
41
38
  if (locale.length === 0) {
42
39
  const browserLang = navigator.language;
43
40
 
44
41
  locale = UI_LANG_OPTIONS.includes(browserLang.toLowerCase())
45
42
  ? browserLang
46
43
  : configs.languageSwitcher.default;
47
-
48
- await setCookie(UI_LANG_KEY, locale);
49
44
  }
50
45
  setUiLang(locale)
51
46
  }
52
47
 
53
- const manageContentLanguage = async (configs: ConfigInterface, availableLanguages: LanguageAndCountries[]): Promise<void> => {
54
- let locale = getFromCookieString(document.cookie, CONTENT_LANG_KEY)
48
+ const manageContentLanguage = async (configs: ConfigInterface): Promise<void> => {
49
+ const contentLang = useLanguageStore.getState().contentLang;
50
+ const setContentLang = useLanguageStore.getState().setContentLang;
51
+ const availableLanguages = useLanguageStore.getState().availableLanguages;
52
+
53
+ let locale = contentLang
55
54
  if (locale.length === 0) {
56
55
  const browserLang = navigator.language;
57
56
  const hasLang = availableLanguages.some((item) => item.value === browserLang);
@@ -59,22 +58,25 @@ export const AppConfigProvider = ({ children }: { children: React.ReactNode }) =
59
58
  locale = hasLang
60
59
  ? browserLang
61
60
  : configs.languageSwitcher.default;
62
-
63
- await setCookie(CONTENT_LANG_KEY, locale);
64
61
  }
65
62
 
66
63
  setContentLang(locale)
67
64
  }
68
65
 
69
- const manageToken = async (): Promise<void> => {
66
+ const manageToken = async (configs: ConfigInterface): Promise<void> => {
70
67
  try {
71
- const hasToken = await getCookie(CREX_TOKEN_HEADER_KEY);
68
+ const url = configs.publicNextApiUrl
69
+ const res = await fetch(`${url}/api/cookies?key=${CREX_TOKEN_HEADER_KEY}`);
70
+
71
+ if (!res.ok) {
72
+ await requestToken(configs);
73
+ return
74
+ }
75
+
76
+ const hasToken = await res.json();
72
77
 
73
78
  if (hasToken.value === null) {
74
- await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/token`, {
75
- method: 'POST',
76
- credentials: 'include',
77
- });
79
+ await requestToken(configs);
78
80
  }
79
81
  } catch (error) {
80
82
  call("CrexLogger.log", {
@@ -84,40 +86,62 @@ export const AppConfigProvider = ({ children }: { children: React.ReactNode }) =
84
86
  }
85
87
  }
86
88
 
87
- async function interval(delayMs: number) {
89
+ const requestToken = async (configs: ConfigInterface): Promise<void> => {
90
+ try {
91
+ await fetch(`${configs.publicNextApiUrl}/api/token`, {
92
+ method: 'POST',
93
+ credentials: 'include',
94
+ });
95
+ } catch (error) {
96
+ call("CrexLogger.log", {
97
+ level: "error",
98
+ message: `config-provider.requestToken error: ${error}`
99
+ });
100
+ }
101
+ }
102
+
103
+ const interval = async (delayMs: number, configs: ConfigInterface) => {
88
104
  while (true) {
89
105
  await new Promise(resolve => setTimeout(resolve, delayMs));
90
- await manageToken()
106
+ await manageToken(configs);
91
107
  }
92
108
  }
93
109
 
110
+ const updateLanguages = async () => {
111
+ const setAvailableLanguages = useLanguageStore.getState().setAvailableLanguages;
112
+
113
+ const langs = await call<LanguageAndCountries[]>("LanguageService.getLanguagesAndCountries")
114
+ setAvailableLanguages(langs)
115
+ }
116
+
94
117
  useEffect(() => {
118
+ useLanguageStore.getState().hydrate();
119
+
95
120
  const load = async () => {
96
121
  try {
97
122
  setLoading(true)
98
123
 
99
- await manageToken()
100
-
101
- const [configsResult, langs] = await Promise.all([
102
- getConfigs(),
103
- call<LanguageAndCountries[]>("LanguageService.getLanguagesAndCountries")
104
- ])
105
-
124
+ const configsResult = await getConfigs()
106
125
  if (!configsResult) {
107
126
  throw new Error("Config cookie not available");
108
127
  }
109
128
 
129
+ await manageToken(configsResult)
130
+
110
131
  await Promise.all([
111
132
  manageUILanguage(configsResult),
112
- manageContentLanguage(configsResult, langs)
133
+ manageContentLanguage(configsResult),
134
+ updateLanguages()
113
135
  ])
114
136
 
115
- setAvailableLanguagesAndCountries(langs)
116
- setConfigs(configsResult)
117
137
  setError(null)
118
-
119
- interval(1000 * 60 * 9);
138
+ setConfigs(configsResult)
139
+ interval(1000 * 60 * 9, configsResult);
120
140
  } catch (err) {
141
+ call("CrexLogger.log", {
142
+ level: "error",
143
+ message: `config-provider.load error: ${err}`
144
+ });
121
145
  setError(err as Error)
122
146
  } finally {
123
147
  setLoading(false)
@@ -137,17 +161,12 @@ export const AppConfigProvider = ({ children }: { children: React.ReactNode }) =
137
161
  <AppConfigContext.Provider
138
162
  value={{
139
163
  configs: configs!,
140
- availableLanguagesAndCountries: availableLanguagesAndCountries!,
141
164
  loading,
142
165
  error,
143
- uiLang,
144
- contentLang,
145
166
  availableVersions,
146
167
  packageID,
147
168
  articleLang,
148
169
  setArticleLang,
149
- setUiLang,
150
- setContentLang,
151
170
  setAvailableVersions,
152
171
  setPackageID
153
172
  }}
@@ -0,0 +1,77 @@
1
+ "use client";
2
+
3
+ import {
4
+ createContext,
5
+ useCallback,
6
+ useContext,
7
+ useEffect,
8
+ useRef,
9
+ useState,
10
+ } from "react";
11
+
12
+ type HighlightContextType = {
13
+ currentIndex: number;
14
+ total: number;
15
+ next: () => void;
16
+ prev: () => void;
17
+ registerContainer: (el: HTMLElement | null) => void;
18
+ };
19
+
20
+ const HighlightContext = createContext<HighlightContextType | null>(null);
21
+
22
+ export const HighlightProvider = ({ children }: { children: React.ReactNode }) => {
23
+
24
+ const containerRef = useRef<HTMLElement | null>(null);
25
+ const [marks, setMarks] = useState<NodeListOf<HTMLElement> | null>(null);
26
+ const [currentIndex, setCurrentIndex] = useState(-1);
27
+
28
+ const registerContainer = useCallback((el: HTMLElement | null) => {
29
+ containerRef.current = el;
30
+ if (el) {
31
+ const found = el.querySelectorAll("mark");
32
+ setMarks(found);
33
+ setCurrentIndex(found.length > 0 ? 0 : -1);
34
+ } else {
35
+ setMarks(null);
36
+ setCurrentIndex(-1);
37
+ }
38
+ }, []);
39
+
40
+ useEffect(() => {
41
+ if (marks && currentIndex >= 0 && marks[currentIndex]) {
42
+ marks.forEach((m) => m.classList.remove("bg-yellow-200", "bg-orange-300"));
43
+ marks[currentIndex].classList.add("bg-orange-300");
44
+ marks[currentIndex].scrollIntoView({ behavior: "smooth", block: "center" });
45
+ }
46
+ }, [currentIndex, marks]);
47
+
48
+ const next = () => {
49
+ if (!marks || marks.length === 0) return;
50
+ setCurrentIndex((i) => (i + 1) % marks.length);
51
+ };
52
+
53
+ const prev = () => {
54
+ if (!marks || marks.length === 0) return;
55
+ setCurrentIndex((i) => (i - 1 + marks.length) % marks.length);
56
+ };
57
+
58
+ return (
59
+ <HighlightContext.Provider
60
+ value={{
61
+ currentIndex,
62
+ total: marks?.length ?? 0,
63
+ next,
64
+ prev,
65
+ registerContainer,
66
+ }}
67
+ >
68
+ {children}
69
+ </HighlightContext.Provider>
70
+ );
71
+ };
72
+
73
+ export const useHighlight = () => {
74
+ const ctx = useContext(HighlightContext);
75
+ if (!ctx) throw new Error("useHighlight must be used inside HighlightProvider");
76
+ return ctx;
77
+ };