1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-08-02 20:15:24 +02:00

Merge branch 'dev' of https://github.com/hay-kot/mealie into api-docs

This commit is contained in:
Hayden 2021-01-07 20:29:52 -09:00
commit e6b423cde0
10 changed files with 269 additions and 161 deletions

View file

@ -13,11 +13,11 @@
"qs": "^6.9.4",
"v-jsoneditor": "^1.4.2",
"vue": "^2.6.11",
"vue-cookies": "^1.7.4",
"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",

View file

@ -2,7 +2,7 @@
<v-app>
<v-app-bar dense app color="primary" dark class="d-print-none">
<v-btn @click="$router.push('/')" icon class="d-flex align-center">
<v-icon size="40" >
<v-icon size="40">
mdi-silverware-variant
</v-icon>
</v-btn>
@ -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",
@ -44,32 +45,53 @@ export default {
Menu,
AddRecipe,
SearchHeader,
SnackBar,
SnackBar
},
watch: {
$route() {
this.search = false;
},
}
},
mounted() {
this.$store.dispatch("initCookies");
this.$store.dispatch("initTheme");
this.$store.dispatch("requestRecentRecipes");
this.darkModeSystemCheck();
this.darkModeAddEventListener();
},
data: () => ({
search: false,
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.darkModeSystemCheck();
});
},
toggleSearch() {
if (this.search === true) {
this.search = false;
} else {
this.search = true;
}
},
},
}
}
};
</script>

View file

@ -19,7 +19,6 @@ export default {
async requestByName(name) {
let response = await apiReq.get(settingsURLs.specificTheme(name));
console.log(response);
return response.data;
},

View file

@ -1,69 +1,117 @@
<template>
<v-card>
<v-card-title class="secondary white--text"> Theme Settings </v-card-title>
<v-card-text>
<h2 class="mt-4 mb-1">Dark Mode</h2>
<p>
Choose how Mealie looks to you. Set your theme preference to follow your
system settings, or choose to use the light or dark theme.
</p>
<v-row dense align="center">
<v-col cols="12">
<v-btn-toggle
v-model="selectedDarkMode"
color="primary "
mandatory
@change="setStoresDarkMode"
>
<v-btn value="system">
Default to system
</v-btn>
<v-btn value="light">
Light
</v-btn>
<v-btn value="dark">
Dark
</v-btn>
</v-btn-toggle>
</v-col>
</v-row></v-card-text
>
<v-divider class=""></v-divider>
<v-card-text>
<h2 class="mt-1 mb-1">Theme</h2>
<p>
Select a theme from the dropdown or create a new theme. Note that the
default theme will be served to all users who have not set a theme
preference.
</p>
<v-row dense align="center">
<v-col cols="12" md="2" sm="5">
<v-switch
v-model="darkMode"
inset
label="Dark Mode"
class="my-n3"
@change="toggleDarkMode"
></v-switch>
</v-col>
<v-col cols="12" md="4" sm="3">
<v-form ref="form" lazy-validation>
<v-form ref="form" lazy-validation>
<v-row dense align="center">
<v-col cols="12" md="4" sm="3">
<v-select
label="Saved Color Schemes"
label="Saved Color Theme"
:items="availableThemes"
item-text="name"
item-value="colors"
return-object
v-model="selectedScheme"
v-model="selectedTheme"
@change="themeSelected"
:rules="[(v) => !!v || 'Theme is required']"
:rules="[v => !!v || 'Theme is required']"
required
>
</v-select>
</v-form>
</v-col>
<v-col cols="12" sm="1">
<NewTheme @new-theme="appendTheme" />
</v-col>
<v-col cols="12" sm="1">
<v-btn text color="error" @click="deleteSelected"> Delete </v-btn>
</v-col>
</v-row>
<v-row dense align-content="center" v-if="activeTheme">
</v-col>
<v-col cols="12" sm="1">
<NewTheme @new-theme="appendTheme" />
</v-col>
<v-col cols="12" sm="1">
<v-btn text color="error" @click="deleteSelectedThemeValidation">
Delete
</v-btn>
<Confirmation
title="Delete Theme"
message="Are you sure you want to delete this theme?"
color="error"
icon="mdi-alert-circle"
ref="deleteThemeConfirm"
v-on:confirm="deleteSelectedTheme()"
/>
</v-col>
</v-row>
</v-form>
<v-row dense align-content="center" v-if="selectedTheme.colors">
<v-col>
<ColorPicker button-text="Primary" v-model="activeTheme.primary" />
<ColorPicker
button-text="Primary"
v-model="selectedTheme.colors.primary"
/>
</v-col>
<v-col>
<ColorPicker
button-text="Secondary"
v-model="activeTheme.secondary"
v-model="selectedTheme.colors.secondary"
/>
</v-col>
<v-col>
<ColorPicker button-text="Accent" v-model="activeTheme.accent" />
<ColorPicker
button-text="Accent"
v-model="selectedTheme.colors.accent"
/>
</v-col>
<v-col>
<ColorPicker button-text="Success" v-model="activeTheme.success" />
<ColorPicker
button-text="Success"
v-model="selectedTheme.colors.success"
/>
</v-col>
<v-col>
<ColorPicker button-text="Info" v-model="activeTheme.info" />
<ColorPicker button-text="Info" v-model="selectedTheme.colors.info" />
</v-col>
<v-col>
<ColorPicker button-text="Warning" v-model="activeTheme.warning" />
<ColorPicker
button-text="Warning"
v-model="selectedTheme.colors.warning"
/>
</v-col>
<v-col>
<ColorPicker button-text="Error" v-model="activeTheme.error" />
<ColorPicker
button-text="Error"
v-model="selectedTheme.colors.error"
/>
</v-col>
</v-row>
</v-card-text>
@ -73,7 +121,9 @@
<v-col> </v-col>
<v-col></v-col>
<v-col align="end">
<v-btn text color="success" @click="saveThemes"> Save Theme </v-btn>
<v-btn text color="success" @click="saveThemes">
Save Colors and Apply Theme
</v-btn>
</v-col>
</v-row>
</v-card-actions>
@ -84,73 +134,90 @@
import api from "../../api";
import ColorPicker from "./ThemeUI/ColorPicker";
import NewTheme from "./ThemeUI/NewTheme";
import Confirmation from "../UI/Confirmation";
export default {
components: {
ColorPicker,
NewTheme,
Confirmation,
NewTheme
},
data() {
return {
themes: null,
activeTheme: {},
darkMode: false,
availableThemes: [],
selectedScheme: "",
selectedLight: "",
selectedTheme: {},
selectedDarkMode: "system",
availableThemes: []
};
},
async mounted() {
this.availableThemes = await api.themes.requestAll();
this.darkMode = this.$store.getters.getDarkMode;
this.themes = this.$store.getters.getThemes;
this.setThemeEditor();
this.selectedTheme = this.$store.getters.getActiveTheme;
this.selectedDarkMode = this.$store.getters.getDarkMode;
},
methods: {
async deleteSelected() {
/**
* Open the delete confirmation.
*/
deleteSelectedThemeValidation() {
if (this.$refs.form.validate()) {
if (this.selectedScheme === "default") {
if (this.selectedTheme.name === "default") {
// Notify User Can't Delete Default
} else if (this.selectedScheme !== "") {
api.themes.delete(this.selectedScheme.name);
} else if (this.selectedTheme !== {}) {
this.$refs.deleteThemeConfirm.open();
}
this.availableThemes = await api.themes.requestAll();
}
},
async appendTheme(newTheme) {
api.themes.create(newTheme);
this.availableThemes.push(newTheme);
},
themeSelected() {
this.activeTheme = this.selectedScheme.colors;
},
setThemeEditor() {
if (this.darkMode) {
this.activeTheme = this.themes.dark;
} else {
this.activeTheme = this.themes.light;
}
},
toggleDarkMode() {
this.$store.commit("setDarkMode", this.darkMode);
this.selectedScheme = "";
/**
* Delete the selected Theme
*/
async deleteSelectedTheme() {
//Delete Theme from DB
await api.themes.delete(this.selectedTheme.name);
this.setThemeEditor();
//Get the new list of available from DB
this.availableThemes = await api.themes.requestAll();
//Change to default if deleting current theme.
if (
!this.availableThemes.some(
theme => theme.name === this.selectedTheme.name
)
) {
await this.$store.dispatch("resetTheme");
this.selectedTheme = this.$store.getters.getActiveTheme;
}
},
saveThemes() {
/**
* Create the new Theme and select it.
*/
async appendTheme(newTheme) {
await api.themes.create(newTheme);
this.availableThemes.push(newTheme);
this.selectedTheme = newTheme;
},
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()) {
if (this.darkMode) {
this.themes.dark = this.activeTheme;
} else {
this.themes.light = this.activeTheme;
}
this.$store.commit("setThemes", this.themes);
this.$store.dispatch("initCookies");
api.themes.update(this.selectedScheme.name, this.activeTheme);
} else;
},
},
this.$store.commit("setTheme", this.selectedTheme);
await api.themes.update(
this.selectedTheme.name,
this.selectedTheme.colors
);
}
}
}
};
</script>

View file

@ -56,7 +56,7 @@ export default {
VJsoneditor,
ViewRecipe,
EditRecipe,
ButtonRow,
ButtonRow
},
data() {
return {
@ -66,7 +66,7 @@ export default {
jsonEditorOptions: {
mode: "code",
search: false,
mainMenuBar: false,
mainMenuBar: false
},
// Recipe Details //
recipeDetails: {
@ -83,9 +83,9 @@ export default {
categories: [],
dateAdded: "",
notes: [],
rating: 0,
rating: 0
},
imageKey: 1,
imageKey: 1
};
},
mounted() {
@ -93,9 +93,9 @@ export default {
},
watch: {
$route: function () {
$route: function() {
this.getRecipeDetails();
},
}
},
computed: {
@ -111,7 +111,7 @@ export default {
} else {
return false;
}
},
}
},
methods: {
getImageFile(fileObject) {
@ -130,7 +130,6 @@ export default {
api.recipes.delete(this.recipeDetails.slug);
},
async saveRecipe() {
console.log(this.recipeDetails);
await api.recipes.update(this.recipeDetails);
if (this.fileObject) {
@ -143,8 +142,8 @@ export default {
showForm() {
this.form = true;
this.jsonEditor = false;
},
},
}
}
};
</script>

View file

@ -4,11 +4,9 @@ import vuetify from "./plugins/vuetify";
import store from "./store/store";
import VueRouter from "vue-router";
import { routes } from "./routes";
import VueCookies from "vue-cookies";
Vue.config.productionTip = false;
Vue.use(VueRouter);
Vue.use(VueCookies);
const router = new VueRouter({
routes,
@ -23,7 +21,7 @@ new Vue({
}).$mount("#app");
// Truncate
let filter = function(text, length, clamp) {
let filter = function (text, length, clamp) {
clamp = clamp || "...";
let node = document.createElement("div");
node.innerHTML = text;

View file

@ -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
}

View file

@ -1,11 +1,18 @@
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({
paths: ['userSettings']
})],
modules: {
userSettings
},
state: {
// Snackbar
snackActive: false,
@ -15,29 +22,6 @@ const store = new Vuex.Store({
// All Recipe Data Store
recentRecipes: [],
allRecipes: [],
// Site Settings
darkMode: false,
themes: {
light: {
primary: "#E58325",
accent: "#00457A",
secondary: "#973542",
success: "#43A047",
info: "#FFFD99",
warning: "#FF4081",
error: "#EF5350",
},
dark: {
primary: "#4527A0",
accent: "#FF4081",
secondary: "#26C6DA",
success: "#43A047",
info: "#2196F3",
warning: "#FB8C00",
error: "#FF5252",
},
},
},
mutations: {
@ -53,38 +37,9 @@ const store = new Vuex.Store({
setRecentRecipes(state, payload) {
state.recentRecipes = payload;
},
setDarkMode(state, payload) {
state.darkMode = payload;
Vue.$cookies.set("darkMode", payload);
Vuetify.framework.theme.dark = payload;
},
setThemes(state, payload) {
state.themes = payload;
Vue.$cookies.set("themes", payload);
Vuetify.framework.theme.themes = payload;
},
},
actions: {
async initCookies() {
if (!Vue.$cookies.isKey("themes")) {
const DEFAULT_THEME = await api.themes.requestByName("default");
Vue.$cookies.set("themes", {
light: DEFAULT_THEME.colors,
dark: DEFAULT_THEME.colors,
});
}
this.commit("setThemes", Vue.$cookies.get("themes"));
// Dark Mode
if (!Vue.$cookies.isKey("darkMode")) {
Vue.$cookies.set("darkMode", false);
}
this.commit("setDarkMode", JSON.parse(Vue.$cookies.get("darkMode")));
},
async requestRecentRecipes() {
const keys = [
@ -108,10 +63,6 @@ const store = new Vuex.Store({
getSnackType: (state) => state.snackType,
getRecentRecipes: (state) => state.recentRecipes,
// Site Settings
getDarkMode: (state) => state.darkMode,
getThemes: (state) => state.themes,
},
});