This commit is contained in:
supertiger1234 2019-11-29 10:09:55 +00:00
parent b98d77c17a
commit a3d5c1ba9d
7 changed files with 404 additions and 2 deletions

19
package-lock.json generated
View file

@ -2826,6 +2826,11 @@
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true
},
"codemirror": {
"version": "5.49.2",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.49.2.tgz",
"integrity": "sha512-dwJ2HRPHm8w51WB5YTF9J7m6Z5dtkqbU9ntMZ1dqXyFB9IpjoUFDj80ahRVEoVanfIp6pfASJbOlbWdEf8FOzQ=="
},
"collection-visit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@ -3793,6 +3798,11 @@
"integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
"dev": true
},
"diff-match-patch": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.4.tgz",
"integrity": "sha512-Uv3SW8bmH9nAtHKaKSanOQmj2DnlH65fUpcrMdfdaOxUG02QQ4YGZ8AE7kKOMisF7UqvOlGKVYWRvezdncW9lg=="
},
"diffie-hellman": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
@ -11736,6 +11746,15 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz",
"integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ=="
},
"vue-codemirror": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/vue-codemirror/-/vue-codemirror-4.0.6.tgz",
"integrity": "sha512-ilU7Uf0mqBNSSV3KT7FNEeRIxH4s1fmpG4TfHlzvXn0QiQAbkXS9lLfwuZpaBVEnpP5CSE62iGJjoliTuA8poQ==",
"requires": {
"codemirror": "^5.41.0",
"diff-match-patch": "^1.0.0"
}
},
"vue-eslint-parser": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz",

View file

@ -23,6 +23,7 @@
"v-clipboard": "^2.2.2",
"validator": "^11.1.0",
"vue": "^2.5.17",
"vue-codemirror": "^4.0.6",
"vue-headful": "^2.0.1",
"vue-mq": "^1.0.1",
"vue-recaptcha": "^1.1.1",

View file

@ -38,6 +38,7 @@ import isElectron from '@/utils/ElectronJS/isElectron';
const MyProfile = () => import("./SettingsPanels/MyProfile.vue");
const ManageEmojis = () => import("./SettingsPanels/ManageEmojis.vue");
const MessageDesign = () => import("./SettingsPanels/MessageDesign.vue");
const MyThemes = () => import("./SettingsPanels/MyThemes.vue");
const Notifications = () => import("./SettingsPanels/Notifications.vue");
const AppSettings = () => import("./SettingsPanels/appSettings");
@ -47,6 +48,7 @@ export default {
MyProfile,
ManageEmojis,
MessageDesign,
MyThemes,
Notifications,
AppSettings
},
@ -66,6 +68,12 @@ export default {
icon: "palette",
component: "message-design"
},
{
name: "My Themes",
tabName: "My Themes BETA",
icon: "palette",
component: "my-themes"
},
{
name: "Manage Emojis",
tabName: "Manage Emojis",

View file

@ -0,0 +1,330 @@
<template>
<div class="container">
<div class="editing" v-if="editing">
<div class="bar">
<input
class="theme-name"
v-model="name"
type="text"
placeholder="Theme name"
/>
<div class="button" @click="saveButton">
<div class="material-icons">save</div>
Save
</div>
<div class="button" @click="closeButton">
<div class="material-icons">clear</div>
Close
</div>
<div class="button end" @click="applyButton">Apply</div>
</div>
<!-- <prism-editor class="editor" v-model="code" language="css"></prism-editor> -->
<codemirror
class="editor"
v-model="code"
:options="cmOptions"
@ready="onCmReady"
/>
<div class="notice">
<div class="material-icons">warning</div>
<div class="notice-message">Warning: Pasting someone elses code could be dangerous.</div>
</div>
</div>
<div class="managing" v-else>
<div class="bar">
<div class="button">
<div class="material-icons">explore</div>
Explore
</div>
<div class="button" @click="createButton">
<div class="material-icons">add</div>
Create
</div>
</div>
<spinner v-if="!themes" />
<div v-if="themes" class="themes-list">
<div class="theme" v-for="theme in themes" :key="theme.id">
<div
class="name"
:class="{ selected: selectedThemeID === theme.id }"
@click="themeClick(theme.id)"
>
{{ theme.name }}
</div>
<div class="context" v-if="theme.id == selectedThemeID">
<div class="button" @click="applyButton(theme.id)">
<div class="material-icons">check</div>
<div class="btn-name">Apply</div>
</div>
<div class="button" @click="editButton(theme.id)">
<div class="material-icons">edit</div>
<div class="btn-name">Edit</div>
</div>
<div class="button delete-button" @click="deleteButton(theme.id)">
<div class="material-icons">delete</div>
<div class="btn-name">Delete</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
// import "prismjs";
// import "prismjs/themes/prism-twilight.css";
// const PrismEditor = () => import("vue-prism-editor");
// language js
import "codemirror/mode/css/css.js";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/hint/show-hint.js";
import "codemirror/addon/hint/css-hint";
// theme css
import "codemirror/theme/base16-dark.css";
import "codemirror/lib/codemirror.css";
import { codemirror } from "vue-codemirror";
import Spinner from "@/components/Spinner";
import config from "@/config.js";
import ThemeService from "@/services/ThemeService";
export default {
components: {
codemirror,
Spinner
},
data() {
return {
selectedThemeID: null,
themes: null,
editing: null,
name: "",
code: `/* Start writing your styles*/\n`,
cmOptions: {
// codemirror options
tabSize: 2,
mode: "text/css",
theme: "base16-dark",
lineNumbers: true,
line: true
}
};
},
methods: {
async fetchThemes() {
this.themes = null;
// fetch themes
const {ok, result, error} = await ThemeService.getThemes();
if (ok) {
this.themes = result.data;
}
},
createButton() {
this.name = "Untitled"
this.code = `/* Start writing your styles*/\n`;
this.editing = true;
},
closeButton() {
this.editing = false;
this.fetchThemes();
},
themeClick(id) {
if (this.selectedThemeID === id) {
this.selectedThemeID = null;
} else {
this.selectedThemeID = id;
}
},
onCmReady(cm) {
cm.on("keypress", () => {
cm.showHint({ completeSingle: false });
});
},
async saveButton() {
const css = this.code;
const name = this.name;
if (typeof this.editing === 'string'){
const response = await ThemeService.update({ name, css }, this.editing);
} else {
const response = await ThemeService.save({ name, css });
}
},
async applyButton(id) {
if (id) {
const {ok, result, error} = await ThemeService.getTheme(id);
if (ok) {
this.code = result.data.css;
}
// save to local storage.
localStorage.setItem('appliedThemeId', id);
}
const currentStyle = document.getElementById("theme");
if (currentStyle) {
currentStyle.innerHTML = this.code;
} else {
const styleEl = document.createElement("style");
styleEl.id = "theme";
styleEl.innerHTML = this.code;
document.head.innerHTML += styleEl.outerHTML;
}
},
async editButton(id) {
// fetch theme
const {ok, result, error} = await ThemeService.getTheme(id);
if (ok) {
const {name, css} = result.data;
this.name = name;
this.code = css;
this.editing = id;
}
},
async deleteButton (id) {
const {ok, result, error} = await ThemeService.delete(id);
if (ok) {
this.themes = this.themes.filter(t => t.id !== id);
}
},
},
async mounted() {
this.fetchThemes();
},
computed: {
user() {
return this.$store.getters.user;
}
}
};
</script>
<style scoped lang="scss">
.container {
display: flex;
width: 100%;
height: 100%;
flex-direction: column;
}
.bar {
display: flex;
width: 100%;
background: rgba(0, 0, 0, 0.2);
height: 40px;
flex-shrink: 0;
.button {
display: flex;
flex-shrink: 0;
align-content: center;
align-items: center;
height: 100%;
user-select: none;
cursor: pointer;
padding-left: 5px;
padding-right: 5px;
&.end {
margin: auto;
margin-right: 0;
}
&:hover {
background: rgba(0, 0, 0, 0.2);
}
.material-icons {
margin-right: 5px;
}
}
}
.editing {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
.theme-name {
height: 20px;
margin: 0;
}
.editor {
height: 100%;
}
.notice {
display: flex;
align-items: center;
align-content: center;
background: rgb(255, 54, 54);
height: 40px;
flex-shrink: 0;
padding-left: 5px;
.notice-message {
margin-left: 5px;
}
}
}
.themes-list {
display: flex;
flex-direction: column;
.theme {
display: flex;
flex-direction: column;
widows: 100%;
min-height: 30px;
flex-shrink: 0;
cursor: pointer;
user-select: none;
color: rgb(230, 230, 230);
transition: 0.2s;
.name {
flex-shrink: 0;
display: flex;
height: 100%;
min-height: 30px;
padding-left: 5px;
flex: 1;
align-items: center;
transition: 0.2s;
&:hover {
color: white;
background: rgba(0, 0, 0, 0.3);
}
&.selected {
background: rgba(0, 0, 0, 0.5);
color: white;
}
}
}
.context {
display: flex;
background: rgba(0, 0, 0, 0.6);
.button {
display: flex;
margin: 5px;
align-content: center;
align-items: center;
opacity: 0.8;
transition: 0.2s;
&:hover {
opacity: 1;
}
}
.delete-button {
margin: auto;
margin-right: 10px;
}
}
}
</style>
<style>
.CodeMirror-hints {
background-color: #33332f !important;
border-color: #232321 !important;
}
.CodeMirror-hint {
color: #666 !important;
}
.CodeMirror-hint-active {
background-color: transparent !important;
color: #6cb5d9 !important;
}
.CodeMirror {
height: 100% !important;
}
</style>

View file

@ -0,0 +1,19 @@
import {instance, wrapper} from './Api';
export default {
getTheme (id) {
return wrapper(instance().get(`themes/${id}`));
},
getThemes () {
return wrapper(instance().get('themes/'));
},
save (data) {
return wrapper(instance().post(`themes/`, data));
},
update (data, id) {
return wrapper(instance().patch(`themes/${id}`, data));
},
delete (id) {
return wrapper(instance().delete(`themes/${id}`));
},
}

View file

@ -9,12 +9,22 @@ const prototype = {
};
const config = [
{
version: 8.8,
title: "Themes!",
shortTitle: "",
date: "29/11/2019",
headColor: "#007792",
new: [
"You can now create your own themes in the settings popout using css! (You cannot share themes for now, that feature is coming soon though!)",
],
},
{
version: 8.7,
title: "12 hour clock mode",
shortTitle: "",
date: "24/11/2019",
headColor: "#007792",
new: [
"Added 12 hour clock mode."
],

View file

@ -31,6 +31,7 @@ import changelog from "@/utils/changelog.js";
import ConnectingScreen from "./../components/app/ConnectingScreen.vue";
import Spinner from "./../components/Spinner.vue";
import MainNav from "./../components/app/MainNav.vue";
import ThemeService from '../services/ThemeService';
const ElectronFrameButtons = () =>
import("@/components/ElectronJS/FrameButtons.vue");
@ -121,6 +122,18 @@ export default {
const height = dimensions.height;
this.$refs.app.style.height = height + "px";
this.$refs.app.style.width = width + "px";
},
async setTheme() {
const themeAppliedID = localStorage.getItem('appliedThemeId');
if (!themeAppliedID) {return;}
const {ok, result, error} = await ThemeService.getTheme(themeAppliedID);
if (!ok) { return; }
const styleEl = document.createElement('style');
styleEl.id = "theme"
styleEl.innerHTML = result.data.css
document.head.innerHTML += styleEl.outerHTML;
}
},
watch: {
@ -128,7 +141,7 @@ export default {
this.resizeEvent(dimensions);
}
},
mounted() {
async mounted() {
const currentTab = localStorage.getItem("currentTab");
if (currentTab) {
this.$store.dispatch("setCurrentTab", parseInt(currentTab));
@ -143,6 +156,8 @@ export default {
bus.$on("title:change", title => {
this.title = title;
});
this.setTheme();
},
computed: {