@data-fair/lib-vue 1.13.11 → 1.14.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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/session.d.ts +19 -4
  3. package/session.js +39 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@data-fair/lib-vue",
3
- "version": "1.13.11",
3
+ "version": "1.14.0",
4
4
  "description": "Composables and other utilities for Vue applications in the data-fair stack.",
5
5
  "main": "index.js",
6
6
  "files": [
package/session.d.ts CHANGED
@@ -42,11 +42,24 @@ export interface Colors {
42
42
  admin: string;
43
43
  'on-admin': string;
44
44
  }
45
+ interface FullSiteInfo {
46
+ main?: boolean;
47
+ theme: {
48
+ logo?: string;
49
+ colors: Colors;
50
+ dark: boolean;
51
+ darkColors?: Colors;
52
+ hc: boolean;
53
+ hcColors?: Colors;
54
+ };
55
+ }
45
56
  export interface SiteInfo {
46
57
  main?: boolean;
47
58
  logo?: string;
59
+ dark?: boolean;
48
60
  colors: Colors;
49
61
  }
62
+ type Theme = 'default' | 'dark' | 'hc';
50
63
  export interface Session {
51
64
  state: SessionState;
52
65
  user: ComputedRef<SessionState['user']>;
@@ -54,8 +67,9 @@ export interface Session {
54
67
  account: ComputedRef<SessionState['account']>;
55
68
  accountRole: ComputedRef<SessionState['accountRole']>;
56
69
  lang: ComputedRef<SessionState['lang']>;
57
- dark: ComputedRef<SessionState['dark']>;
70
+ theme: Ref<null | Theme>;
58
71
  site: Ref<SiteInfo | null>;
72
+ fullSite: Ref<FullSiteInfo | null>;
59
73
  loginUrl: (redirect?: string, extraParams?: Record<string, string>, immediateRedirect?: true) => string;
60
74
  login: (redirect?: string, extraParams?: Record<string, string>, immediateRedirect?: true) => void;
61
75
  logout: (redirect?: string) => Promise<void>;
@@ -65,7 +79,7 @@ export interface Session {
65
79
  cancelDeletion: () => Promise<void>;
66
80
  keepalive: () => Promise<void>;
67
81
  refreshSiteInfo: () => Promise<void>;
68
- switchDark: (value: boolean) => void;
82
+ switchTheme: (value: Theme) => void;
69
83
  switchLang: (value: string) => void;
70
84
  topLocation: Ref<Location | undefined>;
71
85
  options: SessionOptions;
@@ -86,8 +100,9 @@ export declare function createSession(initOptions: Partial<SessionOptions>): Pro
86
100
  account: ComputedRef<SessionState["account"]>;
87
101
  accountRole: ComputedRef<SessionState["accountRole"]>;
88
102
  lang: ComputedRef<SessionState["lang"]>;
89
- dark: ComputedRef<SessionState["dark"]>;
103
+ theme: Ref<null | Theme>;
90
104
  site: Ref<SiteInfo | null>;
105
+ fullSite: Ref<FullSiteInfo | null>;
91
106
  loginUrl: (redirect?: string, extraParams?: Record<string, string>, immediateRedirect?: true) => string;
92
107
  login: (redirect?: string, extraParams?: Record<string, string>, immediateRedirect?: true) => void;
93
108
  logout: (redirect?: string) => Promise<void>;
@@ -97,7 +112,7 @@ export declare function createSession(initOptions: Partial<SessionOptions>): Pro
97
112
  cancelDeletion: () => Promise<void>;
98
113
  keepalive: () => Promise<void>;
99
114
  refreshSiteInfo: () => Promise<void>;
100
- switchDark: (value: boolean) => void;
115
+ switchTheme: (value: Theme) => void;
101
116
  switchLang: (value: string) => void;
102
117
  topLocation: Ref<Location | undefined>;
103
118
  options: SessionOptions;
package/session.js CHANGED
@@ -9,6 +9,16 @@ export * from '@data-fair/lib-common-types/session/index.js'
9
9
  const Cookies = cookiesModule
10
10
  const debug = Debug('session')
11
11
  debug.log = console.log.bind(console)
12
+ function getDefaultTheme (site) {
13
+ // see https://www.scottohara.me/blog/2021/10/01/detect-high-contrast-and-dark-modes.html
14
+ if (site.theme.hc) {
15
+ if (window.matchMedia && window.matchMedia('(forced-colors: active)').matches) { return 'hc' }
16
+ }
17
+ if (site.theme.dark) {
18
+ if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { return 'dark' }
19
+ }
20
+ return 'default'
21
+ }
12
22
  function jwtDecodeAlive (jwt) {
13
23
  if (!jwt) { return }
14
24
  const decoded = jwtDecode(jwt)
@@ -57,12 +67,13 @@ export async function getSession (initOptions) {
57
67
  })
58
68
  // the core state of the session that is filled by reading cookies
59
69
  const state = reactive({})
70
+ const fullSite = ref(null)
60
71
  const site = ref(null)
72
+ const theme = ref(null)
61
73
  // cookies are the source of truth and this information is transformed into the state reactive object
62
74
  const cookies = initOptions?.cookies ?? new Cookies(options.req?.headers.cookie)
63
75
  const readState = () => {
64
- const darkCookie = cookies.get('theme_dark')
65
- state.dark = darkCookie === '1' || darkCookie === 'true'
76
+ theme.value = cookies.get('theme') ?? 'default'
66
77
  const langCookie = cookies.get('i18n_lang')
67
78
  state.lang = langCookie ?? options.defaultLang
68
79
  const idToken = cookies.get('id_token')
@@ -198,15 +209,15 @@ export async function getSession (initOptions) {
198
209
  await customFetch(`${options.directoryUrl}/api/users/${state.user.id}`, { method: 'PATCH', body: ({ plannedDeletion: null }) })
199
210
  readState()
200
211
  }
201
- const switchDark = (value) => {
202
- const maxAge = 60 * 60 * 24 * 365 // 1 year
203
- cookies.set('theme_dark', `${value}`, { maxAge, path: cookiesPath })
204
- readState()
205
- }
206
212
  const switchLang = (value) => {
207
213
  const maxAge = 60 * 60 * 24 * 365 // 1 year
208
214
  cookies.set('i18n_lang', value, { maxAge, path: cookiesPath })
209
- readState()
215
+ goTo(null)
216
+ }
217
+ const switchTheme = (value) => {
218
+ const maxAge = 60 * 60 * 24 * 365 // 1 year
219
+ cookies.set('theme', value, { maxAge, path: cookiesPath })
220
+ goTo(null)
210
221
  }
211
222
  const keepalive = async () => {
212
223
  if (state.user == null) { return }
@@ -226,7 +237,23 @@ export async function getSession (initOptions) {
226
237
  }
227
238
  const refreshSiteInfo = async () => {
228
239
  const siteInfo = await customFetch(`${options.directoryUrl}/api/sites/_public`) ?? null
229
- site.value = siteInfo
240
+ if (siteInfo.theme) {
241
+ fullSite.value = siteInfo
242
+ const partialSite = {
243
+ main: siteInfo.main,
244
+ logo: siteInfo.theme.logo,
245
+ colors: siteInfo.theme.colors
246
+ }
247
+ if (theme.value == null) { theme.value = getDefaultTheme(siteInfo) }
248
+ if (theme.value === 'hc') { partialSite.colors = siteInfo.theme.hcColors }
249
+ if (theme.value === 'dark') {
250
+ partialSite.colors = siteInfo.theme.darkColors
251
+ partialSite.dark = true
252
+ }
253
+ site.value = partialSite
254
+ } else {
255
+ site.value = siteInfo
256
+ }
230
257
  }
231
258
  if (options.siteInfo) { await refreshSiteInfo() }
232
259
  // immediately performs a keepalive, but only on top windows (not iframes or popups)
@@ -252,9 +279,10 @@ export async function getSession (initOptions) {
252
279
  user: computed(() => state.user),
253
280
  account: computed(() => state.account),
254
281
  accountRole: computed(() => state.accountRole),
255
- dark: computed(() => state.dark),
256
282
  lang: computed(() => state.lang),
283
+ theme,
257
284
  site,
285
+ fullSite,
258
286
  loginUrl,
259
287
  login,
260
288
  logout,
@@ -264,7 +292,7 @@ export async function getSession (initOptions) {
264
292
  cancelDeletion,
265
293
  keepalive,
266
294
  refreshSiteInfo,
267
- switchDark,
295
+ switchTheme,
268
296
  switchLang,
269
297
  topLocation,
270
298
  options