@data-fair/lib-vue 1.13.12 → 1.15.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 +21 -4
  3. package/session.js +42 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@data-fair/lib-vue",
3
- "version": "1.13.12",
3
+ "version": "1.15.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,26 @@ 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
+ hcDark: boolean;
55
+ hcDarkColors?: Colors;
56
+ };
57
+ }
45
58
  export interface SiteInfo {
46
59
  main?: boolean;
47
60
  logo?: string;
61
+ dark?: boolean;
48
62
  colors: Colors;
49
63
  }
64
+ type Theme = 'default' | 'dark' | 'hc' | 'hc-dark';
50
65
  export interface Session {
51
66
  state: SessionState;
52
67
  user: ComputedRef<SessionState['user']>;
@@ -54,8 +69,9 @@ export interface Session {
54
69
  account: ComputedRef<SessionState['account']>;
55
70
  accountRole: ComputedRef<SessionState['accountRole']>;
56
71
  lang: ComputedRef<SessionState['lang']>;
57
- dark: ComputedRef<SessionState['dark']>;
72
+ theme: Ref<null | Theme>;
58
73
  site: Ref<SiteInfo | null>;
74
+ fullSite: Ref<FullSiteInfo | null>;
59
75
  loginUrl: (redirect?: string, extraParams?: Record<string, string>, immediateRedirect?: true) => string;
60
76
  login: (redirect?: string, extraParams?: Record<string, string>, immediateRedirect?: true) => void;
61
77
  logout: (redirect?: string) => Promise<void>;
@@ -65,7 +81,7 @@ export interface Session {
65
81
  cancelDeletion: () => Promise<void>;
66
82
  keepalive: () => Promise<void>;
67
83
  refreshSiteInfo: () => Promise<void>;
68
- switchDark: (value: boolean) => void;
84
+ switchTheme: (value: Theme) => void;
69
85
  switchLang: (value: string) => void;
70
86
  topLocation: Ref<Location | undefined>;
71
87
  options: SessionOptions;
@@ -86,8 +102,9 @@ export declare function createSession(initOptions: Partial<SessionOptions>): Pro
86
102
  account: ComputedRef<SessionState["account"]>;
87
103
  accountRole: ComputedRef<SessionState["accountRole"]>;
88
104
  lang: ComputedRef<SessionState["lang"]>;
89
- dark: ComputedRef<SessionState["dark"]>;
105
+ theme: Ref<null | Theme>;
90
106
  site: Ref<SiteInfo | null>;
107
+ fullSite: Ref<FullSiteInfo | null>;
91
108
  loginUrl: (redirect?: string, extraParams?: Record<string, string>, immediateRedirect?: true) => string;
92
109
  login: (redirect?: string, extraParams?: Record<string, string>, immediateRedirect?: true) => void;
93
110
  logout: (redirect?: string) => Promise<void>;
@@ -97,7 +114,7 @@ export declare function createSession(initOptions: Partial<SessionOptions>): Pro
97
114
  cancelDeletion: () => Promise<void>;
98
115
  keepalive: () => Promise<void>;
99
116
  refreshSiteInfo: () => Promise<void>;
100
- switchDark: (value: boolean) => void;
117
+ switchTheme: (value: Theme) => void;
101
118
  switchLang: (value: string) => void;
102
119
  topLocation: Ref<Location | undefined>;
103
120
  options: SessionOptions;
package/session.js CHANGED
@@ -9,6 +9,15 @@ 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
+ const preferDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
15
+ const preferHC = window.matchMedia && window.matchMedia('(forced-colors: active)').matches
16
+ if (site.theme.hcDark && preferDark && preferHC) { return 'hc-dark' }
17
+ if (site.theme.hc && preferHC) { return 'hc' }
18
+ if (site.theme.dark && preferDark) { return 'dark' }
19
+ return 'default'
20
+ }
12
21
  function jwtDecodeAlive (jwt) {
13
22
  if (!jwt) { return }
14
23
  const decoded = jwtDecode(jwt)
@@ -57,12 +66,13 @@ export async function getSession (initOptions) {
57
66
  })
58
67
  // the core state of the session that is filled by reading cookies
59
68
  const state = reactive({})
69
+ const fullSite = ref(null)
60
70
  const site = ref(null)
71
+ const theme = ref(null)
61
72
  // cookies are the source of truth and this information is transformed into the state reactive object
62
73
  const cookies = initOptions?.cookies ?? new Cookies(options.req?.headers.cookie)
63
74
  const readState = () => {
64
- const darkCookie = cookies.get('theme_dark')
65
- state.dark = darkCookie === '1' || darkCookie === 'true'
75
+ theme.value = cookies.get('theme') ?? null
66
76
  const langCookie = cookies.get('i18n_lang')
67
77
  state.lang = langCookie ?? options.defaultLang
68
78
  const idToken = cookies.get('id_token')
@@ -198,15 +208,15 @@ export async function getSession (initOptions) {
198
208
  await customFetch(`${options.directoryUrl}/api/users/${state.user.id}`, { method: 'PATCH', body: ({ plannedDeletion: null }) })
199
209
  readState()
200
210
  }
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
211
  const switchLang = (value) => {
207
212
  const maxAge = 60 * 60 * 24 * 365 // 1 year
208
213
  cookies.set('i18n_lang', value, { maxAge, path: cookiesPath })
209
- readState()
214
+ goTo(null)
215
+ }
216
+ const switchTheme = (value) => {
217
+ const maxAge = 60 * 60 * 24 * 365 // 1 year
218
+ cookies.set('theme', value, { maxAge, path: cookiesPath })
219
+ goTo(null)
210
220
  }
211
221
  const keepalive = async () => {
212
222
  if (state.user == null) { return }
@@ -226,7 +236,27 @@ export async function getSession (initOptions) {
226
236
  }
227
237
  const refreshSiteInfo = async () => {
228
238
  const siteInfo = await customFetch(`${options.directoryUrl}/api/sites/_public`) ?? null
229
- site.value = siteInfo
239
+ if (siteInfo.theme) {
240
+ fullSite.value = siteInfo
241
+ const partialSite = {
242
+ main: siteInfo.main,
243
+ logo: siteInfo.theme.logo,
244
+ colors: siteInfo.theme.colors
245
+ }
246
+ if (theme.value == null) { theme.value = getDefaultTheme(siteInfo) }
247
+ if (theme.value === 'hc') { partialSite.colors = siteInfo.theme.hcColors }
248
+ if (theme.value === 'dark') {
249
+ partialSite.colors = siteInfo.theme.darkColors
250
+ partialSite.dark = true
251
+ }
252
+ if (theme.value === 'hc-dark') {
253
+ partialSite.colors = siteInfo.theme.hcDarkColors
254
+ partialSite.dark = true
255
+ }
256
+ site.value = partialSite
257
+ } else {
258
+ site.value = siteInfo
259
+ }
230
260
  }
231
261
  if (options.siteInfo) { await refreshSiteInfo() }
232
262
  // immediately performs a keepalive, but only on top windows (not iframes or popups)
@@ -252,9 +282,10 @@ export async function getSession (initOptions) {
252
282
  user: computed(() => state.user),
253
283
  account: computed(() => state.account),
254
284
  accountRole: computed(() => state.accountRole),
255
- dark: computed(() => state.dark),
256
285
  lang: computed(() => state.lang),
286
+ theme,
257
287
  site,
288
+ fullSite,
258
289
  loginUrl,
259
290
  login,
260
291
  logout,
@@ -264,7 +295,7 @@ export async function getSession (initOptions) {
264
295
  cancelDeletion,
265
296
  keepalive,
266
297
  refreshSiteInfo,
267
- switchDark,
298
+ switchTheme,
268
299
  switchLang,
269
300
  topLocation,
270
301
  options