+export const ns = ['status']
+
+/*
+ * Timeout in seconds before the loading status dissapears
+ */
+const timeout = 2
+
+const LoadingStatus = ({ loadingStatus }) => {
+ const { t } = useTranslation(ns)
+
+ const [fade, setFade] = useState('opacity-100')
+ const [timer, setTimer] = useState(false)
+
+ useEffect(() => {
+ if (loadingStatus[2]) {
+ if (timer) clearTimeout(timer)
+ setTimer(
+ window.setTimeout(() => {
+ setFade('opacity-0')
+ }, timeout * 1000 - 350)
+ )
+ }
+ }, [loadingStatus[2]])
+
+ if (!loadingStatus[0]) return null
+
+ let color = 'secondary'
+ let icon =
+ if (loadingStatus[2]) {
+ color = loadingStatus[3] ? 'success' : 'error'
+ icon = loadingStatus[3] ? (
+
+ ) : (
+
+ )
+ }
+
+ return (
+
- {loadingStatus[1]}
+ {icon}
+ {t(loadingStatus[1])}
- ) : null
+ )
+}
export const useLoadingStatus = () => {
+ /*
+ * LoadingStatus should hold an array with 1 to 4 elements:
+ * 0 => Show loading status or not (true or false)
+ * 1 => Message to show
+ * 2 => Set this to true to make the loadingStatus dissapear after 2 seconds
+ * 3 => Set this to true to show success, false to show error (only when 2 is true)
+ */
const [loadingStatus, setLoadingStatus] = useState([false])
+ const [timer, setTimer] = useState(false)
+
+ useEffect(() => {
+ if (loadingStatus[2]) {
+ if (timer) clearTimeout(timer)
+ setTimer(
+ window.setTimeout(() => {
+ setLoadingStatus([false])
+ }, timeout * 1000)
+ )
+ }
+ }, [loadingStatus[2]])
return {
setLoadingStatus,
diff --git a/sites/shared/hooks/useGist.mjs b/sites/shared/hooks/useGist.mjs
deleted file mode 100644
index 39d1de47b4b..00000000000
--- a/sites/shared/hooks/useGist.mjs
+++ /dev/null
@@ -1,100 +0,0 @@
-import { useState, useMemo, useCallback } from 'react'
-import set from 'lodash.set'
-import unset from 'lodash.unset'
-import cloneDeep from 'lodash.clonedeep'
-import { useLocalStorage } from './useLocalStorage'
-import { defaultGist as baseGist } from 'shared/components/workbench/gist.mjs'
-
-// Generates a default design gist to start from
-export const defaultGist = (design, locale = 'en') => {
- const gist = {
- design,
- ...baseGist,
- _state: { view: 'draft' },
- }
- if (locale) gist.locale = locale
-
- return gist
-}
-
-// generate the gist state and its handlers
-export function useGist(design, locale) {
- // memoize the initial gist for this design so that it doesn't change between renders and cause an infinite loop
- const initialGist = useMemo(() => defaultGist(design, locale), [design, locale])
-
- // get the localstorage state and setter
- const [gist, _setGist, gistReady] = useLocalStorage(`${design}_gist`, initialGist)
- const [gistHistory, setGistHistory] = useState([])
- const [gistFuture, setGistFuture] = useState([])
-
- const setGist = useCallback(
- (newGist, addToHistory = true) => {
- let oldGist
- _setGist((gistState) => {
- // have to clone it or nested objects will be referenced instead of copied, which defeats the purpose
- if (addToHistory) oldGist = cloneDeep(gistState)
-
- return typeof newGist === 'function' ? newGist(cloneDeep(gistState)) : newGist
- })
-
- if (addToHistory) {
- setGistHistory((history) => {
- return [...history, oldGist]
- })
- setGistFuture([])
- }
- },
- [_setGist, setGistFuture, setGistHistory]
- )
-
- /** update a single gist value */
- const updateGist = useCallback(
- (path, value, addToHistory = true) => {
- setGist((gistState) => {
- const newGist = { ...gistState }
- set(newGist, path, value)
- return newGist
- }, addToHistory)
- },
- [setGist]
- )
-
- /** unset a single gist value */
- const unsetGist = useCallback(
- (path, addToHistory = true) => {
- setGist((gistState) => {
- const newGist = { ...gistState }
- unset(newGist, path)
- return newGist
- }, addToHistory)
- },
- [setGist]
- )
-
- const undoGist = useCallback(() => {
- _setGist((gistState) => {
- let prevGist
- setGistHistory((history) => {
- const newHistory = [...history]
- prevGist = newHistory.pop() || defaultGist(design, locale)
- return newHistory
- })
- setGistFuture((future) => [gistState, ...future])
-
- return { ...prevGist }
- })
- }, [_setGist, setGistFuture, setGistHistory])
-
- const redoGist = useCallback(() => {
- const newHistory = [...gistHistory, gist]
- const newFuture = [...gistFuture]
- const newGist = newFuture.shift()
- setGistHistory(newHistory)
- setGistFuture(newFuture)
- _setGist(newGist)
- }, [_setGist, setGistFuture, setGistHistory])
-
- const resetGist = useCallback(() => setGist(defaultGist(design, locale)), [setGist])
-
- return { gist, setGist, unsetGist, gistReady, updateGist, undoGist, redoGist, resetGist }
-}
diff --git a/sites/shared/hooks/useLocalStorage.mjs b/sites/shared/hooks/useLocalStorage.mjs
deleted file mode 100644
index 1730c2e33d4..00000000000
--- a/sites/shared/hooks/useLocalStorage.mjs
+++ /dev/null
@@ -1,38 +0,0 @@
-import { useState, useEffect } from 'react'
-
-const prefix = 'fs_'
-
-// See: https://usehooks.com/useLocalStorage/
-export function useLocalStorage(key, initialValue) {
- // use this to track whether it's mounted. useful for doing other effects outside this hook
- // and for making sure we don't write the initial value over the current value
- const [ready, setReady] = useState(false)
-
- // State to store our value
- const [storedValue, setValue] = useState(initialValue)
-
- // set to localstorage every time the storedValue changes
- // we do it this way instead of a callback because
- // getting the current state inside `useCallback` didn't seem to be working
- useEffect(() => {
- if (ready) {
- window.localStorage.setItem(prefix + key, JSON.stringify(storedValue))
- }
- }, [storedValue, key, ready])
-
- // read from local storage on mount
- useEffect(() => {
- try {
- // Get from local storage by key
- const item = window.localStorage.getItem(prefix + key)
- // Parse stored json or if none return initialValue
- const valToSet = item ? JSON.parse(item) : initialValue
- setValue(valToSet)
- setReady(true)
- } catch (error) {
- console.log(error)
- }
- }, [setReady, setValue, key, initialValue])
-
- return [storedValue, setValue, ready]
-}
diff --git a/sites/shared/i18n/toast/de.yaml b/sites/shared/i18n/status/de.yaml
similarity index 100%
rename from sites/shared/i18n/toast/de.yaml
rename to sites/shared/i18n/status/de.yaml
diff --git a/sites/shared/i18n/toast/en.yaml b/sites/shared/i18n/status/en.yaml
similarity index 60%
rename from sites/shared/i18n/toast/en.yaml
rename to sites/shared/i18n/status/en.yaml
index 9546e0ac319..b5853b4ca5c 100644
--- a/sites/shared/i18n/toast/en.yaml
+++ b/sites/shared/i18n/status/en.yaml
@@ -1,3 +1,5 @@
+updatingSettings: Updating settings
settingsSaved: Settings saved
backendError: Backend returned an error
copiedToClipboard: Copied to clipboard
+processingUpdate: Processing update
diff --git a/sites/shared/i18n/toast/es.yaml b/sites/shared/i18n/status/es.yaml
similarity index 100%
rename from sites/shared/i18n/toast/es.yaml
rename to sites/shared/i18n/status/es.yaml
diff --git a/sites/shared/i18n/toast/fr.yaml b/sites/shared/i18n/status/fr.yaml
similarity index 100%
rename from sites/shared/i18n/toast/fr.yaml
rename to sites/shared/i18n/status/fr.yaml
diff --git a/sites/shared/i18n/toast/nl.yaml b/sites/shared/i18n/status/nl.yaml
similarity index 100%
rename from sites/shared/i18n/toast/nl.yaml
rename to sites/shared/i18n/status/nl.yaml
diff --git a/sites/shared/i18n/toast/uk.yaml b/sites/shared/i18n/status/uk.yaml
similarity index 100%
rename from sites/shared/i18n/toast/uk.yaml
rename to sites/shared/i18n/status/uk.yaml
diff --git a/sites/shared/prebuild/sitenav-org.mjs b/sites/shared/prebuild/sitenav-org.mjs
index 5c6023cba31..927ced11037 100644
--- a/sites/shared/prebuild/sitenav-org.mjs
+++ b/sites/shared/prebuild/sitenav-org.mjs
@@ -39,7 +39,7 @@ export const extendSiteNav = async (siteNav, lang) => {
h: 1,
t: t('sections:new'),
apikey: {
- c: conf.account.fields.developer.apikeys,
+ c: conf.account.fields.security.apikeys,
s: 'new/apikey',
t: t('newApikey'),
o: 30,