replaced style editor in settings with manage styles

This commit is contained in:
booploops 2022-05-04 13:33:55 -07:00
parent 6b59db98fc
commit 0abc06da18
4 changed files with 427 additions and 192 deletions

View file

@ -1,18 +1,18 @@
import {join} from "path"; import { join } from "path";
import {app, BrowserWindow as bw, ipcMain, ShareMenu, shell} from "electron"; import { app, BrowserWindow as bw, ipcMain, ShareMenu, shell } from "electron";
import * as windowStateKeeper from "electron-window-state"; import * as windowStateKeeper from "electron-window-state";
import * as express from "express"; import * as express from "express";
import * as getPort from "get-port"; import * as getPort from "get-port";
import {search} from "youtube-search-without-api-key"; import { search } from "youtube-search-without-api-key";
import {existsSync, rmSync, mkdirSync, readdirSync, readFileSync, writeFileSync, statSync} from "fs"; import { existsSync, rmSync, mkdirSync, readdirSync, readFileSync, writeFileSync, statSync } from "fs";
import {Stream} from "stream"; import { Stream } from "stream";
import {networkInterfaces} from "os"; import { networkInterfaces } from "os";
import * as mm from 'music-metadata'; import * as mm from 'music-metadata';
import fetch from 'electron-fetch' import fetch from 'electron-fetch'
import {wsapi} from "./wsapi"; import { wsapi } from "./wsapi";
import {utils} from './utils'; import { utils } from './utils';
import {Plugins} from "./plugins"; import { Plugins } from "./plugins";
import {watch} from "chokidar"; import { watch } from "chokidar";
import * as os from "os"; import * as os from "os";
import wallpaper from "wallpaper"; import wallpaper from "wallpaper";
import * as AdmZip from "adm-zip"; import * as AdmZip from "adm-zip";
@ -46,6 +46,7 @@ export class BrowserWindow {
"pages/library-artists", "pages/library-artists",
"pages/browse", "pages/browse",
"pages/settings", "pages/settings",
"pages/installed-themes",
"pages/listen_now", "pages/listen_now",
"pages/home", "pages/home",
"pages/artist-feed", "pages/artist-feed",
@ -178,6 +179,10 @@ export class BrowserWindow {
page: "settings", page: "settings",
component: `<cider-settings></cider-settings>`, component: `<cider-settings></cider-settings>`,
condition: `page == 'settings'` condition: `page == 'settings'`
}, {
page: "installed-themes",
component: `<installed-themes></installed-themes>`,
condition: `page == 'installed-themes'`
}, { }, {
page: "search", page: "search",
component: `<cider-search :search="search"></cider-search>`, component: `<cider-search :search="search"></cider-search>`,
@ -241,7 +246,7 @@ export class BrowserWindow {
show: false, show: false,
// backgroundColor: "#1E1E1E", // backgroundColor: "#1E1E1E",
titleBarStyle: 'hidden', titleBarStyle: 'hidden',
trafficLightPosition: {x: 15, y: 20}, trafficLightPosition: { x: 15, y: 20 },
webPreferences: { webPreferences: {
experimentalFeatures: true, experimentalFeatures: true,
nodeIntegration: true, nodeIntegration: true,
@ -301,7 +306,7 @@ export class BrowserWindow {
* @yields {object} Electron browser window * @yields {object} Electron browser window
*/ */
async createWindow(): Promise<Electron.BrowserWindow> { async createWindow(): Promise<Electron.BrowserWindow> {
this.clientPort = await getPort({port: 9000}); this.clientPort = await getPort({ port: 9000 });
BrowserWindow.verifyFiles(); BrowserWindow.verifyFiles();
this.StartWatcher(utils.getPath('themes')); this.StartWatcher(utils.getPath('themes'));
@ -561,7 +566,7 @@ export class BrowserWindow {
remote.use(express.static(join(utils.getPath('srcPath'), "./web-remote/"))) remote.use(express.static(join(utils.getPath('srcPath'), "./web-remote/")))
remote.set("views", join(utils.getPath('srcPath'), "./web-remote/views")); remote.set("views", join(utils.getPath('srcPath'), "./web-remote/views"));
remote.set("view engine", "ejs"); remote.set("view engine", "ejs");
getPort({port: 6942}).then((port: number) => { getPort({ port: 6942 }).then((port: number) => {
this.remotePort = port; this.remotePort = port;
// Start Remote Discovery // Start Remote Discovery
this.broadcastRemote() this.broadcastRemote()
@ -634,7 +639,7 @@ export class BrowserWindow {
'KHTML, like Gecko) Mobile/17D50 UCBrowser/12.8.2.1268 Mobile AliApp(TUnionSDK/0.1.20.3) ' 'KHTML, like Gecko) Mobile/17D50 UCBrowser/12.8.2.1268 Mobile AliApp(TUnionSDK/0.1.20.3) '
details.requestHeaders['Referer'] = "https://y.qq.com/portal/player.html" details.requestHeaders['Referer'] = "https://y.qq.com/portal/player.html"
} }
callback({requestHeaders: details.requestHeaders}); callback({ requestHeaders: details.requestHeaders });
} }
); );
@ -702,7 +707,7 @@ export class BrowserWindow {
// remove WidevineCDM from appdata folder // remove WidevineCDM from appdata folder
const widevineCdmPath = join(app.getPath("userData"), "./WidevineCdm"); const widevineCdmPath = join(app.getPath("userData"), "./WidevineCdm");
if (existsSync(widevineCdmPath)) { if (existsSync(widevineCdmPath)) {
rmSync(widevineCdmPath, {recursive: true, force: true}) rmSync(widevineCdmPath, { recursive: true, force: true })
} }
// reinstall WidevineCDM // reinstall WidevineCDM
app.relaunch() app.relaunch()
@ -978,7 +983,7 @@ export class BrowserWindow {
//Fullscreen //Fullscreen
ipcMain.on('detachDT', (_event, _) => { ipcMain.on('detachDT', (_event, _) => {
BrowserWindow.win.webContents.openDevTools({mode: 'detach'}); BrowserWindow.win.webContents.openDevTools({ mode: 'detach' });
}) })
ipcMain.handle('relaunchApp', (_event, _) => { ipcMain.handle('relaunchApp', (_event, _) => {
@ -1310,10 +1315,10 @@ export class BrowserWindow {
// Set window Handler // Set window Handler
BrowserWindow.win.webContents.setWindowOpenHandler((x: any) => { BrowserWindow.win.webContents.setWindowOpenHandler((x: any) => {
if (x.url.includes("apple") || x.url.includes("localhost")) { if (x.url.includes("apple") || x.url.includes("localhost")) {
return {action: "allow"}; return { action: "allow" };
} }
shell.openExternal(x.url).catch(console.error); shell.openExternal(x.url).catch(console.error);
return {action: "deny"}; return { action: "deny" };
}); });
} }
@ -1369,7 +1374,7 @@ export class BrowserWindow {
"CtlN": "Cider", "CtlN": "Cider",
"iV": "196623" "iV": "196623"
}; };
let server2 = mdns.createAdvertisement(x, `${await getPort({port: 3839})}`, { let server2 = mdns.createAdvertisement(x, `${await getPort({ port: 3839 })}`, {
name: encoded, name: encoded,
txt: txt_record txt: txt_record
}); });

View file

@ -493,69 +493,74 @@
//background: linear-gradient(180deg, var(--bgColor) 32px, var(--bgColor) 18px, transparent 60px, transparent 100%); //background: linear-gradient(180deg, var(--bgColor) 32px, var(--bgColor) 18px, transparent 60px, transparent 100%);
top : 0; top : 0;
padding-top : var(--navigationBarHeight); padding-top : var(--navigationBarHeight);
display:flex; display : flex;
flex-direction: column; flex-direction: column;
height: 100%; height : 100%;
overflow: hidden; overflow : hidden;
.cd-mediaitem-list-item{
&:hover{ .cd-mediaitem-list-item {
.heart-icon{ &:hover {
display:flex; .heart-icon {
display: flex;
} }
} }
.heart-icon{
.heart-icon {
left: -25px; left: -25px;
} }
} }
.editTracksBtn { .editTracksBtn {
position: absolute; position: absolute;
top: 20px; top : 20px;
right: 20px; right : 20px;
z-index: 1; z-index : 1;
>span { >span {
display: flex; display: flex;
gap: 8px; gap : 8px;
} }
} }
.mediaContainer { .mediaContainer {
transition: width 0.5s ease-in-out, height 0.5s ease-in-out; transition: width 0.5s ease-in-out, height 0.5s ease-in-out;
width: 260px;height:260px; width : 260px;
height : 260px;
} }
.playlist-body { .playlist-body {
padding : 32px; padding : 32px;
// margin-top: -75px; // margin-top: -75px;
overflow-y:overlay; overflow-y : overlay;
height:100%; height : 100%;
padding:0px; padding : 0px;
background-color: var(--color1); background-color: var(--color1);
&.scrollbody { &.scrollbody {
.tabs { .tabs {
display: flex; display : flex;
flex-flow: column; flex-flow: column;
height: 100%; height : 100%;
.nav-link { .nav-link {
text-transform:capitalize; text-transform: capitalize;
} }
.tab-content { .tab-content {
height: 100%; height : 100%;
overflow: hidden; overflow: hidden;
margin:0px; margin : 0px;
.tab-pane { .tab-pane {
height: 100%; height : 100%;
overflow-y: overlay; overflow-y : overlay;
overflow-x:hidden; overflow-x : hidden;
padding: var(--contentInnerPadding); padding : var(--contentInnerPadding);
padding-inline: 40px; padding-inline : 40px;
-webkit-mask-image: linear-gradient(180deg, transparent, white 20px); -webkit-mask-image: linear-gradient(180deg, transparent, white 20px);
.well { .well {
margin:0px; margin: 0px;
} }
} }
} }
@ -574,7 +579,7 @@
background : rgba(0, 0, 0, 0.25); background : rgba(0, 0, 0, 0.25);
top : var(--navigationBarHeight); top : var(--navigationBarHeight);
transition : opacity 0.1s var(--appleEase); transition : opacity 0.1s var(--appleEase);
display: none; display : none;
} }
.playlist-display { .playlist-display {
@ -660,7 +665,7 @@
} }
.playlist-desc { .playlist-desc {
transition: height .2s ease-in-out, opacity .2s ease-in-out; transition : height .2s ease-in-out, opacity .2s ease-in-out;
box-sizing : border-box; box-sizing : border-box;
font-size : 14px; font-size : 14px;
flex-shrink : unset; flex-shrink : unset;
@ -761,11 +766,11 @@
} }
.playlist-time { .playlist-time {
font-size: 0.9em; font-size : 0.9em;
margin : 6px; margin : 6px;
opacity : 0.7; opacity : 0.7;
transition: height .2s ease-in-out, opacity .2s ease-in-out; transition: height .2s ease-in-out, opacity .2s ease-in-out;
height: 0.9em; height : 0.9em;
} }
&.inline-playlist { &.inline-playlist {
@ -813,8 +818,8 @@
.pilldim { .pilldim {
.nav-pills { .nav-pills {
width: max-content; width : max-content;
margin: 0 auto; margin : 0 auto;
margin-top: 16px; margin-top: 16px;
} }
} }
@ -824,26 +829,24 @@
transition: min-height 0.5s ease-in-out; transition: min-height 0.5s ease-in-out;
min-height: 200px; min-height: 200px;
.playlistInfo { .playlistInfo {}
}
.mediaContainer { .mediaContainer {
transition: width 0.5s ease-in-out, height 0.5s ease-in-out; transition: width 0.5s ease-in-out, height 0.5s ease-in-out;
width: 128px!important; width : 128px !important;
height: 128px!important; height : 128px !important;
} }
.playlist-time { .playlist-time {
transition: height .2s ease-in-out, opacity .2s ease-in-out; transition: height .2s ease-in-out, opacity .2s ease-in-out;
height: 0px; height : 0px;
opacity: 0; opacity : 0;
} }
.playlist-desc { .playlist-desc {
transition: height .2s ease-in-out, opacity .2s ease-in-out; transition: height .2s ease-in-out, opacity .2s ease-in-out;
height: 0px!important; height : 0px !important;
opacity: 0; opacity : 0;
} }
} }
} }
@ -1123,9 +1126,46 @@
/* Artist Page End */ /* Artist Page End */
// Settings page
.settings-page { .installed-themes-page {
padding: 0px;
.themeContextMenu {
background: transparent;
color : var(--keyColor);
border : 0px;
}
.repo-header {
font-size : 16px;
position : sticky;
top : 0;
left : 0;
right : 0;
width : 100%;
height : 50px;
z-index : 1;
background : rgba(36, 36, 36, 0.5);
display : flex;
justify-content: center;
align-items : center;
backdrop-filter: var(--glassFilter);
overflow : hidden;
border-bottom : 1px solid rgb(0 0 0 / 18%);
border-top : 1px solid rgb(135 135 135 / 18%);
}
.style-editor-container {
height : 100%;
flex : 1;
background: var(--color2);
padding : 0px;
overflow-y: overlay;
.list-group-item {
border-radius: 0px;
}
}
.stylestack-editor { .stylestack-editor {
width: 100%; width: 100%;
@ -1136,17 +1176,17 @@
} }
.themeLabel { .themeLabel {
display:flex; display : flex;
align-items: center; align-items: center;
} }
.removeItem { .removeItem {
border: 0px; border : 0px;
background: transparent; background : transparent;
height: 32px; height : 32px;
font-weight: bold; font-weight: bold;
color: var(--textColor); color : var(--textColor);
cursor: pointer; cursor : pointer;
} }
.stylesDropdown { .stylesDropdown {
@ -1156,7 +1196,11 @@
} }
} }
} }
}
// Settings page
.settings-page {
padding: 0px;
.nav { .nav {
width : 90%; width : 90%;
@ -1174,8 +1218,9 @@
.settings-option-body-webview { .settings-option-body-webview {
height: 100%; height: 100%;
width: 100%; width : 100%;
} }
.settings-option-body { .settings-option-body {
margin: 16px; margin: 16px;
} }

View file

@ -0,0 +1,291 @@
<script type="text/x-template" id="installed-themes">
<div class="content-inner github-themes-page installed-themes-page">
<div class="gh-header">
<div class="row">
<div class="col nopadding">
<h1 class="header-text">
Manage Styles
</h1>
</div>
<div class="col-auto flex-center">
<button class="md-btn md-btn-small md-btn-block" @click="openThemesFolder()">
{{$root.getLz('settings.option.visual.theme.github.openfolder')}}
</button>
</div>
<div class="col-auto nopadding flex-center">
<button class="md-btn md-btn-small md-btn-block" @click="installThemeURL()">
{{$root.getLz('settings.option.visual.theme.github.download')}}
</button>
</div>
</div>
</div>
<div class="gh-content">
<div class="repos-list">
<div class="repo-header">
<h4>Available</h4>
</div>
<ul class="list-group list-group-flush">
<li @click="addStyle(theme.file)"
@contextmenu="contextMenu"
class="list-group-item list-group-item-dark"
v-for="theme in themes" :value="theme.file"
v-if="!$root.cfg.visual.styles.includes(theme.file)">
<b-row>
<b-col class="themeLabel">{{theme.name}}</b-col>
<b-col sm="auto">
<button @click.stop="contextMenu" class="themeContextMenu codicon codicon-list-unordered"></button>
</b-col>
</b-row>
</li>
</ul>
</div>
<div class="style-editor-container">
<div class="repo-header">
<h4>Applied</h4>
</div>
<stylestack-editor ref="stackEditor" v-if="themes.length != 0" :themes="themes"/>
</div>
</transition>
</div>
</div>
</script>
<script>
// do not translate
Vue.component('stylestack-editor', {
/*html*/
template: `
<div class="stylestack-editor" >
<draggable class="list-group" v-model="$root.cfg.visual.styles" @end="$root.reloadStyles()">
<b-list-group-item variant="dark" v-for="theme in $root.cfg.visual.styles" :key="theme">
<b-row>
<b-col class="themeLabel">{{getThemeName(theme)}}</b-col>
<b-col sm="auto">
<button class="removeItem codicon codicon-close" @click="remove(theme)"></button>
</b-col>
</b-row>
</b-list-group-item>
</draggable>
</div>
`,
props: {
themes: {
type: Array,
default: [],
required: true
}
},
data: function () {
return {
selected: null,
newTheme: null,
themeList: []
}
},
mounted() {
console.log(this.themes)
this.themeList = [...this.themes]
this.themeList.unshift({
name: "Acrylic Grain",
file: "grain.less"
})
this.themeList.unshift({
name: "Sweetener",
file: "sweetener.less"
})
this.themeList.unshift({
name: "Reduce Visuals",
file: "reduce_visuals.less"
})
this.themeList.unshift({
name: "Inline Drawer",
file: "inline_drawer.less"
})
},
methods: {
gitHubExplore() {
this.$root.appRoute("themes-github")
},
getThemeName(filename) {
try {
return this.themeList.find(theme => theme.file === filename).name;
} catch (e) {
return filename;
}
},
moveUp() {
const styles = this.$root.cfg.visual.styles
const index = styles.indexOf(this.selected)
if (index > 0) {
styles.splice(index, 1)
styles.splice(index - 1, 0, this.selected)
}
this.$root.reloadStyles()
},
moveDown() {
const styles = this.$root.cfg.visual.styles
const index = styles.indexOf(this.selected)
if (index < styles.length - 1) {
styles.splice(index, 1)
styles.splice(index + 1, 0, this.selected)
}
this.$root.reloadStyles()
},
remove(style) {
const styles = this.$root.cfg.visual.styles
const index = styles.indexOf(style)
styles.splice(index, 1)
this.$root.reloadStyles()
},
addStyle(style) {
const styles = this.$root.cfg.visual.styles
styles.push(style)
this.$root.reloadStyles()
}
}
})
</script>
<script>
Vue.component('installed-themes', {
template: "#installed-themes",
props: [],
data: function () {
return {
repos: [],
openRepo: {
id: -1,
name: '',
description: '',
html_url: '',
stargazers_count: 0,
owner: {
avatar_url: ''
},
readme: ""
},
themesInstalled: [],
themes: []
}
},
mounted() {
this.themes = ipcRenderer.sendSync("get-themes")
// this.getRepos();
this.getInstalledThemes();
// app.checkForThemeUpdates()
},
methods: {
contextMenu(event) {
let menu = {
items: [{
name: "Uninstall",
action: () => {
}
},
{
name: "View Info",
action: () => {
}
}
]
}
this.$root.showMenuPanel(menu, event)
},
openThemesFolder() {
ipcRenderer.invoke("open-path", "themes")
},
getInstalledThemes() {
let self = this
const themes = ipcRenderer.sendSync("get-themes")
// for each theme, get the github_repo property and push it to the themesInstalled array, if not blank
themes.forEach(theme => {
if (theme.github_repo !== "" && typeof theme.commit != "") {
self.themesInstalled.push(theme.github_repo.toLowerCase())
}
})
},
addStyle(filename) {
this.$refs.stackEditor.addStyle(filename)
},
showRepo(repo) {
const self = this
const readmeUrl = `https://raw.githubusercontent.com/${repo.full_name}/main/README.md`;
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
fetch(readmeUrl, requestOptions)
.then(response => response.text())
.then(result => {
self.openRepo = repo
self.openRepo.readme = self.convertReadMe(result);
})
.catch(error => {
self.openRepo = repo
self.openRepo.readme = `This repository doesn't have a README.md file.`;
console.log('error', error)
});
},
convertReadMe(text) {
return marked.parse(text)
},
installThemeRepo(repo) {
let self = this
let msg = app.stringTemplateParser(app.getLz('settings.option.visual.theme.github.install.confirm'), {
repo: repo.full_name
});
bootbox.confirm(msg, (res) => {
if (res) {
ipcRenderer.once("theme-installed", (event, arg) => {
if (arg.success) {
self.themes = ipcRenderer.sendSync("get-themes")
self.getInstalledThemes()
notyf.success(app.getLz('settings.notyf.visual.theme.install.success'));
} else {
notyf.error(app.getLz('settings.notyf.visual.theme.install.error'));
}
});
ipcRenderer.invoke("get-github-theme", repo.html_url)
}
})
},
installThemeURL() {
let self = this
bootbox.prompt(app.getLz('settings.prompt.visual.theme.github.URL'), (result) => {
if (result) {
ipcRenderer.once("theme-installed", (event, arg) => {
if (arg.success) {
self.themes = ipcRenderer.sendSync("get-themes")
notyf.success(app.getLz('settings.notyf.visual.theme.install.success'));
} else {
notyf.error(app.getLz('settings.notyf.visual.theme.install.error'));
}
});
ipcRenderer.invoke("get-github-theme", result)
}
});
},
getRepos() {
let self = this
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
fetch("https://api.github.com/search/repositories?q=topic:cidermusictheme fork:true", requestOptions)
.then(response => response.text())
.then(result => {
let items = JSON.parse(result).items
self.repos = items
})
.catch(error => console.log('error', error));
}
}
})
</script>

View file

@ -1224,8 +1224,8 @@
<small>Mix and match various theme components to get Cider looking exactly how you <small>Mix and match various theme components to get Cider looking exactly how you
want.</small> want.</small>
</div> </div>
<div class="md-option-segment"> <div class="md-option-segment md-option-segment_auto">
<stylestack-editor :themes="themes"/> <button class="md-btn" @click="app.appRoute('installed-themes')">Manage Styles</button>
</div> </div>
</div> </div>
@ -1436,112 +1436,6 @@
</div> </div>
</script> </script>
<script>
// do not translate
Vue.component('stylestack-editor', {
/*html*/
template: `
<div class="stylestack-editor">
<draggable class="list-group" v-model="$root.cfg.visual.styles" @end="$root.reloadStyles()">
<b-list-group-item variant="dark" v-for="theme in $root.cfg.visual.styles" :key="theme">
<b-row>
<b-col class="themeLabel">{{getThemeName(theme)}}</b-col>
<b-col sm="auto">
<button class="removeItem codicon codicon-close" @click="remove(theme)"></button>
</b-col>
</b-row>
</b-list-group-item>
<b-list-group-item slot="footer" style="-webkit-user-drag: none" variant="dark">
<b-row>
<b-col>
<b-dropdown class="stylesDropdown" variant="primary" text="Add Style...">
<b-dropdown-item v-for="theme in themeList" @click="addStyle(theme.file)">{{theme.name}}</b-dropdown-item>
</b-dropdown>
</b-col>
<b-col>
<b-btn @click="gitHubExplore()">{{$root.getLz('settings.option.visual.theme.github.explore')}}</b-btn>
</b-col>
</b-row>
</b-list-group-item>
</draggable>
</div>
`,
props: {
themes: {
type: Array,
default: []
}
},
data: function () {
return {
selected: null,
newTheme: null,
themeList: []
}
},
mounted() {
this.themeList = [...this.themes]
this.themeList.unshift({
name: "Acrylic Grain",
file: "grain.less"
})
this.themeList.unshift({
name: "Sweetener",
file: "sweetener.less"
})
this.themeList.unshift({
name: "Reduce Visuals",
file: "reduce_visuals.less"
})
this.themeList.unshift({
name: "Inline Drawer",
file: "inline_drawer.less"
})
},
methods: {
gitHubExplore() {
this.$root.appRoute("themes-github")
},
getThemeName(filename) {
try {
return this.themeList.find(theme => theme.file === filename).name;
} catch (e) {
return filename;
}
},
moveUp() {
const styles = this.$root.cfg.visual.styles
const index = styles.indexOf(this.selected)
if (index > 0) {
styles.splice(index, 1)
styles.splice(index - 1, 0, this.selected)
}
this.$root.reloadStyles()
},
moveDown() {
const styles = this.$root.cfg.visual.styles
const index = styles.indexOf(this.selected)
if (index < styles.length - 1) {
styles.splice(index, 1)
styles.splice(index + 1, 0, this.selected)
}
this.$root.reloadStyles()
},
remove(style) {
const styles = this.$root.cfg.visual.styles
const index = styles.indexOf(style)
styles.splice(index, 1)
this.$root.reloadStyles()
},
addStyle(style) {
const styles = this.$root.cfg.visual.styles
styles.push(style)
this.$root.reloadStyles()
}
}
})
</script>
<script> <script>
Vue.component('cider-settings', { Vue.component('cider-settings', {
template: "#cider-settings", template: "#cider-settings",