diff --git a/dev/dev-notes.md b/dev/dev-notes.md index 2c5d87f3a64e..121368e1194c 100644 --- a/dev/dev-notes.md +++ b/dev/dev-notes.md @@ -46,3 +46,5 @@ General - Improved documentation - Fixed hot-reloading development environment - [grssmnn](https://github.com/grssmnn) - Added Confirmation component to deleting recipes - [zackbcom](https://github.com/zackbcom) +- Added Persistent storage to vuex - [zackbcom](https://github.com/zackbcom) +- Updated Theme backend - [zackbcom](https://github.com/zackbcom) diff --git a/frontend/package.json b/frontend/package.json index 17dfefb37958..cd9550c3be2d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,7 +17,8 @@ "vue-html-to-paper": "^1.3.1", "vue-router": "^3.4.9", "vuetify": "^2.4.1", - "vuex": "^3.6.0" + "vuex": "^3.6.0", + "vuex-persistedstate": "^4.0.0-beta.2" }, "devDependencies": { "@vue/cli-plugin-babel": "~4.5.0", diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 11b321b41192..533c394c7c67 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -37,6 +37,7 @@ import Menu from "./components/UI/Menu"; import SearchHeader from "./components/UI/SearchHeader"; import AddRecipe from "./components/AddRecipe"; import SnackBar from "./components/UI/SnackBar"; +import Vuetify from "./plugins/vuetify"; export default { name: "App", @@ -54,8 +55,9 @@ export default { }, mounted() { - this.$store.dispatch("initCookies"); + this.$store.dispatch("initTheme"); this.$store.dispatch("requestRecentRecipes"); + this.darkModeSystemCheck(); this.darkModeAddEventListener(); }, @@ -63,16 +65,25 @@ export default { search: false }), methods: { + /** + * Checks if 'system' is set for dark mode and then sets the corrisponding value for vuetify + */ + darkModeSystemCheck() { + if (this.$store.getters.getDarkMode === "system") + Vuetify.framework.theme.dark = window.matchMedia( + "(prefers-color-scheme: dark)" + ).matches; + }, /** * This will monitor the OS level darkmode and call to update dark mode. */ darkModeAddEventListener() { const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); - darkMediaQuery.addEventListener("change", () => { - this.$store.commit("setDarkMode", "system"); + this.darkModeSystemCheck(); }); }, + toggleSearch() { if (this.search === true) { this.search = false; diff --git a/frontend/src/components/Admin/Theme.vue b/frontend/src/components/Admin/Theme.vue index 28f441bbcc8a..057d80195c8d 100644 --- a/frontend/src/components/Admin/Theme.vue +++ b/frontend/src/components/Admin/Theme.vue @@ -11,10 +11,10 @@ Default to system @@ -48,7 +48,7 @@ :items="availableThemes" item-text="name" return-object - v-model="activeTheme" + v-model="selectedTheme" @change="themeSelected" :rules="[v => !!v || 'Theme is required']" required @@ -73,42 +73,45 @@ - + - + - + @@ -118,7 +121,9 @@ - Save Theme + + Save Colors and Apply Theme + @@ -139,62 +144,80 @@ export default { }, data() { return { - activeTheme: {}, - darkMode: "system", + selectedTheme: {}, + selectedDarkMode: "system", availableThemes: [] }; }, async mounted() { this.availableThemes = await api.themes.requestAll(); - this.activeTheme = this.$store.getters.getActiveTheme; - this.darkMode = this.$store.getters.getDarkMode; + this.selectedTheme = this.$store.getters.getActiveTheme; + this.selectedDarkMode = this.$store.getters.getDarkMode; }, methods: { + /** + * Open the delete confirmation. + */ deleteSelectedThemeValidation() { if (this.$refs.form.validate()) { - if (this.activeTheme.name === "default") { + if (this.selectedTheme.name === "default") { // Notify User Can't Delete Default - } else if (this.activeTheme !== {}) { + } else if (this.selectedTheme !== {}) { this.$refs.deleteThemeConfirm.open(); } } }, + /** + * Delete the selected Theme + */ async deleteSelectedTheme() { - api.themes.delete(this.activeTheme.name); - this.availableThemes = await api.themes.requestAll(); - //Change to default if deleting current theme. + //Delete Theme from DB + await api.themes.delete(this.selectedTheme.name); + //Get the new list of available from DB + this.availableThemes = await api.themes.requestAll(); + console.log("themes", this.availableThemes); + + //Change to default if deleting current theme. if ( !this.availableThemes.some( - theme => theme.name === this.activeTheme.name + theme => theme.name === this.selectedTheme.name ) ) { - this.$store.commit("setActiveTheme", null); - this.activeTheme = this.$store.getters.getActiveTheme; + console.log("hit"); + await this.$store.dispatch("resetTheme"); + this.selectedTheme = this.$store.getters.getActiveTheme; } }, + /** + * Create the new Theme and select it. + */ async appendTheme(newTheme) { - api.themes.create(newTheme); + await api.themes.create(newTheme); this.availableThemes.push(newTheme); - this.activeTheme = newTheme; - }, - themeSelected() { - console.log("this.activeTheme", this.activeTheme); + this.selectedTheme = newTheme; }, - setDarkMode() { - this.$store.commit("setDarkMode", this.darkMode); + themeSelected() { + //TODO Revamp Theme selection. + //console.log("this.activeTheme", this.selectedTheme); + }, + + setStoresDarkMode() { + this.$store.commit("setDarkMode", this.selectedDarkMode); }, /** * This will save the current colors and make the selected theme live. */ async saveThemes() { if (this.$refs.form.validate()) { - this.$store.commit("setActiveTheme", this.activeTheme); - this.$store.dispatch("initCookies"); - api.themes.update(this.activeTheme.name, this.activeTheme); - } else; + this.$store.commit("setTheme", this.selectedTheme); + await api.themes.update( + this.selectedTheme.name, + this.selectedTheme.colors + ); + } } } }; diff --git a/frontend/src/store/modules/userSettings.js b/frontend/src/store/modules/userSettings.js new file mode 100644 index 000000000000..005e403f369c --- /dev/null +++ b/frontend/src/store/modules/userSettings.js @@ -0,0 +1,70 @@ + +import api from "../../api"; +import Vuetify from "../../plugins/vuetify"; + +const state = { + activeTheme: {}, + darkMode: 'system' + +}; + +const mutations = { + setTheme(state, payload) { + Vuetify.framework.theme.themes.dark = payload.colors; + Vuetify.framework.theme.themes.light = payload.colors; + state.activeTheme = payload; + }, + setDarkMode(state, payload) { + let isDark; + + if (payload === 'system') { + //Get System Preference from browser + const darkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + isDark = darkMediaQuery.matches; + } + else if (payload === 'dark') + isDark = true; + else if (payload === 'light') + isDark = false; + + if (isDark !== null) { + Vuetify.framework.theme.dark = isDark; + state.darkMode = payload; + } + }, +}; + +const actions = { + async resetTheme({ commit }) { + const defaultTheme = await api.themes.requestByName("default"); + if (defaultTheme.colors) { + Vuetify.framework.theme.themes.dark = defaultTheme.colors; + Vuetify.framework.theme.themes.light = defaultTheme.colors; + commit('setTheme', defaultTheme) + } + }, + + async initTheme({ dispatch, getters }) { + //If theme is empty resetTheme + if (Object.keys(getters.getActiveTheme).length === 0) { + await dispatch('resetTheme') + } + else { + Vuetify.framework.theme.themes.dark = getters.getActiveTheme.colors; + Vuetify.framework.theme.themes.light = getters.getActiveTheme.colors; + } + }, + +} + +const getters = { + getActiveTheme: (state) => state.activeTheme, + getDarkMode: (state) => state.darkMode +} + +export default { + state, + mutations, + actions, + getters +} \ No newline at end of file diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index ae4d6aadc1b2..ca1a5efe61ea 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -1,11 +1,16 @@ import Vue from "vue"; import Vuex from "vuex"; import api from "../api"; -import Vuetify from "../plugins/vuetify"; +import createPersistedState from "vuex-persistedstate"; +import userSettings from "./modules/userSettings"; Vue.use(Vuex); const store = new Vuex.Store({ + plugins: [createPersistedState()], + modules: { + userSettings + }, state: { // Snackbar snackActive: false, @@ -15,41 +20,6 @@ const store = new Vuex.Store({ // All Recipe Data Store recentRecipes: [], allRecipes: [], - - // Site Settings - darkMode: 'system', - activeTheme: { - name: 'default', - colors: { - primary: "#E58325", - accent: "#00457A", - secondary: "#973542", - success: "#5AB1BB", - info: "#4990BA", - warning: "#FF4081", - error: "#EF5350", - } - }, - themes: { - light: { - primary: "#E58325", - accent: "#00457A", - secondary: "#973542", - success: "#5AB1BB", - info: "#4990BA", - warning: "#FF4081", - error: "#EF5350", - }, - dark: { - primary: "#E58325", - accent: "#00457A", - secondary: "#973542", - success: "#5AB1BB", - info: "#4990BA", - warning: "#FF4081", - error: "#EF5350", - }, - }, }, mutations: { @@ -65,65 +35,9 @@ const store = new Vuex.Store({ setRecentRecipes(state, payload) { state.recentRecipes = payload; }, - - setDarkMode(state, payload) { - let isDark; - state.darkMode = payload; - Vue.$cookies.set("darkMode", payload); - - - if (payload === 'system') { - //Get System Preference from browser - const darkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); - isDark = darkMediaQuery.matches; - } - else if (payload === 'dark') - isDark = true; - else - isDark = false; - - Vuetify.framework.theme.dark = isDark; - }, - - setActiveTheme(state, payload) { - state.activeTheme = payload; - Vue.$cookies.set("activeTheme", payload); - - const themes = payload ? { dark: payload.colors, light: payload.colors } : null - console.log("themes", themes) - state.themes = themes; - Vue.$cookies.set("themes", themes); - Vuetify.framework.theme.themes = themes; - }, }, actions: { - async initCookies() { - //TODO if has no value set to default. - if (!Vue.$cookies.isKey("themes") || !Vue.$cookies.isKey("activeTheme")) { - const DEFAULT_THEME = await api.themes.requestByName("default"); - Vue.$cookies.set("themes", { - light: DEFAULT_THEME.colors, - dark: DEFAULT_THEME.colors, - }); - Vue.$cookies.set("activeTheme", { - name: DEFAULT_THEME.name, - colors: DEFAULT_THEME.colors - }); - } - - this.commit("setActiveTheme", Vue.$cookies.get("activeTheme")); - - //https://csabaszabo.dev/blog/dark-mode-for-website-with-nuxtjs-and-vuetify/ - //https://github.com/settings/appearance - - - // Dark Mode - if (!Vue.$cookies.isKey("darkMode")) { - Vue.$cookies.set("darkMode", 'system'); - } - this.commit("setDarkMode", Vue.$cookies.get("darkMode")); - }, async requestRecentRecipes() { const keys = [ @@ -147,11 +61,6 @@ const store = new Vuex.Store({ getSnackType: (state) => state.snackType, getRecentRecipes: (state) => state.recentRecipes, - - // Site Settings - getDarkMode: (state) => state.darkMode, - getThemes: (state) => state.themes, - getActiveTheme: (state) => state.activeTheme }, });