| 1 |
import apiFetch from '@wordpress/api-fetch'; |
| 2 |
import { useEffect, useState } from '@wordpress/element'; |
| 3 |
import { __ } from '@wordpress/i18n'; |
| 4 |
import { create } from 'zustand'; |
| 5 |
import { createJSONStorage, devtools, persist } from 'zustand/middleware'; |
| 6 |
import type { Attributes } from '../types'; |
| 7 |
|
| 8 |
type ThemeType = { |
| 9 |
previousTheme: string; |
| 10 |
previousLineHeight: string; |
| 11 |
previousFontFamily?: string; |
| 12 |
previousFontSize: string; |
| 13 |
previousHeaderType: string; |
| 14 |
previousFooterType?: string; |
| 15 |
previousClampFonts?: boolean; |
| 16 |
previousDisablePadding?: boolean; |
| 17 |
previousLineNumbers?: boolean; |
| 18 |
previousHighlightingHover?: boolean; |
| 19 |
previousCopyButton: boolean; |
| 20 |
previousCopyButtonType: string; |
| 21 |
previousCopyButtonString?: string; |
| 22 |
previousCopyButtonStringCopied?: string; |
| 23 |
previousCopyButtonUseTextarea?: boolean; |
| 24 |
previousTabSize: number; |
| 25 |
previousUseTabs?: boolean; |
| 26 |
// previousEnableMaxHeight?: boolean; |
| 27 |
// previousSeeMoreAfterLine?: string; |
| 28 |
previousSeeMoreType?: string; |
| 29 |
previousSeeMoreString?: string; |
| 30 |
previousSeeMoreTransition?: boolean; |
| 31 |
previousSeeMoreCollapse?: boolean; |
| 32 |
previousSeeMoreCollapseString?: string; |
| 33 |
updateThemeHistory: (settings: Partial<Attributes>) => void; |
| 34 |
}; |
| 35 |
const path = '/code-block-pro/v1/settings'; |
| 36 |
const getSettings = async (name: string) => { |
| 37 |
const allSettings = await apiFetch({ path }); |
| 38 |
// @ts-expect-error-next-line |
| 39 |
return allSettings?.[name]; |
| 40 |
}; |
| 41 |
const defaultSettings = { |
| 42 |
previousTheme: 'nord', |
| 43 |
previousLineHeight: '1.25rem', |
| 44 |
previousFontFamily: 'Code-Pro-JetBrains-Mono', |
| 45 |
previousFontSize: '.875rem', |
| 46 |
previousHeaderType: 'headlights', |
| 47 |
previousFooterType: 'none', |
| 48 |
previousClampFonts: false, |
| 49 |
previousDisablePadding: false, |
| 50 |
previousLineNumbers: false, |
| 51 |
previousHighlightingHover: false, |
| 52 |
previousCopyButton: true, |
| 53 |
previousCopyButtonType: 'heroicons', |
| 54 |
previousCopyButtonString: __('Copy', 'code-block-pro'), |
| 55 |
previusCopyButtonStringCopied: __('Copied', 'code-block-pro'), |
| 56 |
previousCopyButtonUseTextarea: true, |
| 57 |
previousTabSize: 2, |
| 58 |
previousUseTabs: false, |
| 59 |
// TODO: maybe impliment these with an extra UI to make them optional |
| 60 |
// previousEnableMaxHeight: undefined, |
| 61 |
// previousSeeMoreAfterLine: undefined, |
| 62 |
previousSeeMoreType: '', |
| 63 |
previousSeeMoreString: '', |
| 64 |
previousSeeMoreTransition: false, |
| 65 |
previousSeeMoreCollapse: false, |
| 66 |
previousSeeMoreCollapseString: '', |
| 67 |
}; |
| 68 |
const storage = { |
| 69 |
getItem: async (name: string) => { |
| 70 |
const settings = await getSettings(name); |
| 71 |
return JSON.stringify({ |
| 72 |
version: settings?.version ?? 0, |
| 73 |
state: settings || defaultSettings, |
| 74 |
}); |
| 75 |
}, |
| 76 |
setItem: async (name: string, value: string) => { |
| 77 |
const { state, version } = JSON.parse(value); |
| 78 |
const data = { |
| 79 |
[name]: Object.assign( |
| 80 |
(await getSettings(name)) || defaultSettings, |
| 81 |
state, |
| 82 |
version, |
| 83 |
), |
| 84 |
}; |
| 85 |
await apiFetch({ path, method: 'POST', data }); |
| 86 |
}, |
| 87 |
removeItem: async (name: string) => { |
| 88 |
const data = { [name]: null }; |
| 89 |
await apiFetch({ path, method: 'POST', data }); |
| 90 |
return undefined; |
| 91 |
}, |
| 92 |
}; |
| 93 |
|
| 94 |
export const useThemeStore = create<ThemeType>()( |
| 95 |
persist( |
| 96 |
devtools( |
| 97 |
(set) => ({ |
| 98 |
...defaultSettings, |
| 99 |
updateThemeHistory(attributes: Partial<Attributes>) { |
| 100 |
set((state) => { |
| 101 |
return Object.keys(attributes).reduce<Partial<ThemeType>>( |
| 102 |
(acc, attr) => { |
| 103 |
const att = attr as keyof Attributes; |
| 104 |
const fmted = `previous${ |
| 105 |
attr.charAt(0).toUpperCase() + attr.slice(1) |
| 106 |
}` as keyof ThemeType; |
| 107 |
const keyExists = Object.keys(state).includes(fmted); |
| 108 |
return keyExists ? { ...acc, [fmted]: attributes[att] } : acc; |
| 109 |
}, |
| 110 |
{ ...state }, |
| 111 |
); |
| 112 |
}); |
| 113 |
}, |
| 114 |
}), |
| 115 |
{ name: 'Code Block Pro Theme Settings' }, |
| 116 |
), |
| 117 |
{ |
| 118 |
name: 'code_block_pro_settings', |
| 119 |
storage: createJSONStorage(() => storage), |
| 120 |
}, |
| 121 |
), |
| 122 |
); |
| 123 |
|
| 124 |
// const useThemeStoreReady = |
| 125 |
/* Hook useful for when you need to wait on the async state to hydrate */ |
| 126 |
export const useThemeStoreReady = () => { |
| 127 |
const [hydrated, setHydrated] = useState(useThemeStore.persist.hasHydrated); |
| 128 |
useEffect(() => { |
| 129 |
const unsubFinishHydration = useThemeStore.persist.onFinishHydration(() => |
| 130 |
setHydrated(true), |
| 131 |
); |
| 132 |
return () => { |
| 133 |
unsubFinishHydration(); |
| 134 |
}; |
| 135 |
}, []); |
| 136 |
return hydrated; |
| 137 |
}; |
| 138 |
|