commit
ead51ae676
|
@ -19,6 +19,54 @@ const ENDPOINTS = {
|
|||
'statistics': `${APIBASE}/statistics`
|
||||
};
|
||||
|
||||
const dummyTags = ['tag1', 'tag2', 'tag3', 'tag4', 'tag5'];
|
||||
const dummyRepo = {
|
||||
name: 'ExampleRepository',
|
||||
baseUri: 'https://github.com/Jiiks/ExampleRepository',
|
||||
rawUri: 'https://github.com/Jiiks/ExampleRepository/raw/master'
|
||||
};
|
||||
const dummyVersion = () => `${Math.round(Math.random() * 3)}.${Math.round(Math.random() * 10)}.${Math.round(Math.random() * 10)}`;
|
||||
const dummyFiles = {
|
||||
readme: 'Example/readme.md',
|
||||
previews: [{
|
||||
large: 'Example/preview1-big.png',
|
||||
thumb: 'Example/preview1-small.png'
|
||||
}]
|
||||
};
|
||||
const dummyAuthor = 'DummyAuthor';
|
||||
const dummyTimestamp = () => `2018-${Math.floor((Math.random() * 12) + 1).toString().padStart(2, '0')}-${Math.floor((Math.random() * 30) + 1).toString().padStart(2, '0')}T14:51:32.057Z`;
|
||||
|
||||
async function dummyThemes() {
|
||||
// Simulate get
|
||||
await new Promise(r => setTimeout(r, Math.random() * 3000));
|
||||
const dummies = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
dummies.push({
|
||||
id: `theme${i}${btoa(Math.random()).substring(3, 9)}`,
|
||||
name: `Dummy ${i}`,
|
||||
tags: dummyTags,
|
||||
installs: Math.floor(Math.random() * 10000),
|
||||
updated: dummyTimestamp(),
|
||||
rating: Math.floor(Math.random() * 1000),
|
||||
activeUsers: Math.floor(Math.random() * 1000),
|
||||
rated: Math.random() > .5,
|
||||
version: dummyVersion(),
|
||||
repository: dummyRepo,
|
||||
files: dummyFiles,
|
||||
author: dummyAuthor
|
||||
});
|
||||
}
|
||||
return {
|
||||
docs: dummies,
|
||||
pagination: {
|
||||
total: 25,
|
||||
pages: 3,
|
||||
limit: 9,
|
||||
page: 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default class BdWebApi {
|
||||
|
||||
static get themes() {
|
||||
|
@ -41,11 +89,14 @@ export default class BdWebApi {
|
|||
}
|
||||
|
||||
static getThemes(args) {
|
||||
return dummyThemes();
|
||||
/*
|
||||
if (!args) return request.get(ENDPOINTS.themes);
|
||||
const { id } = args;
|
||||
if (id) return request.get(ENDPOINTS.theme(id));
|
||||
|
||||
return request.get(ENDPOINTS.themes);
|
||||
*/
|
||||
}
|
||||
|
||||
static getUsers(args) {
|
||||
|
|
|
@ -12,6 +12,7 @@ import asar from 'asar';
|
|||
import path, { dirname } from 'path';
|
||||
import rimraf from 'rimraf';
|
||||
|
||||
import { remote } from 'electron';
|
||||
import Content from './content';
|
||||
import Globals from './globals';
|
||||
import Database from './database';
|
||||
|
@ -71,6 +72,28 @@ export default class {
|
|||
return Globals.getPath(this.pathId);
|
||||
}
|
||||
|
||||
static async packContent(path, contentPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
remote.dialog.showSaveDialog({
|
||||
title: 'Save Package',
|
||||
defaultPath: path,
|
||||
filters: [
|
||||
{
|
||||
name: 'BetterDiscord Package',
|
||||
extensions: ['bd']
|
||||
}
|
||||
]
|
||||
}, filepath => {
|
||||
if (!filepath) return;
|
||||
|
||||
asar.uncache(filepath);
|
||||
asar.createPackage(contentPath, filepath, () => {
|
||||
resolve(filepath);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all locally stored content.
|
||||
* @param {bool} suppressErrors Suppress any errors that occur during loading of content
|
||||
|
|
|
@ -13,6 +13,7 @@ import Security from './security';
|
|||
import { ReactComponents } from './reactcomponents';
|
||||
import Reflection from './reflection';
|
||||
import DiscordApi from './discordapi';
|
||||
import ThemeManager from './thememanager';
|
||||
|
||||
export default class PackageInstaller {
|
||||
|
||||
|
@ -64,47 +65,39 @@ export default class PackageInstaller {
|
|||
/**
|
||||
* Installs or updates defined package
|
||||
* @param {Byte[]|String} bytesOrPath byte array of binary or path to local file
|
||||
* @param {String} name Package name
|
||||
* @param {String} nameOrId Package name
|
||||
* @param {Boolean} update Does an older version already exist
|
||||
*/
|
||||
static async installPackage(bytesOrPath, id, update = false) {
|
||||
static async installPackage(bytesOrPath, nameOrId, contentType, update = false) {
|
||||
let outputPath = null;
|
||||
try {
|
||||
const bytes = typeof bytesOrPath === 'string' ? fs.readFileSync(bytesOrPath) : bytesOrPath;
|
||||
|
||||
const outputName = `${id}.bd`;
|
||||
outputPath = path.join(Globals.getPath('plugins'), outputName);
|
||||
const bytes = typeof bytesOrPath === 'string' ? fs.readFileSync(bytesOrPath) : bytesOrPath;
|
||||
const outputName = `${nameOrId}.bd`;
|
||||
|
||||
outputPath = path.join(Globals.getPath(`${contentType}s`), outputName);
|
||||
fs.writeFileSync(outputPath, bytes);
|
||||
|
||||
if (!update) return PluginManager.preloadPackedContent(outputName);
|
||||
const manager = contentType === 'plugin' ? PluginManager : ThemeManager;
|
||||
|
||||
const oldContent = PluginManager.getPluginById(id);
|
||||
if (!update) return manager.preloadPackedContent(outputName);
|
||||
|
||||
if (update && oldContent.packed && oldContent.packed.packageName !== id) {
|
||||
await oldContent.unload(true);
|
||||
const oldContent = manager.findContent(nameOrId);
|
||||
|
||||
await oldContent.unload(true);
|
||||
|
||||
if (oldContent.packed && oldContent.packed.packageName !== nameOrId) {
|
||||
rimraf(oldContent.packed.packagePath, err => {
|
||||
if(err) console.log(err);
|
||||
if (err) throw err;
|
||||
});
|
||||
|
||||
return PluginManager.preloadPackedContent(outputName);
|
||||
}
|
||||
|
||||
if (update && !oldContent.packed) {
|
||||
await oldContent.unload(true);
|
||||
} else {
|
||||
rimraf(oldContent.contentPath, err => {
|
||||
if (err) console.log(err);
|
||||
if (err) throw err;
|
||||
});
|
||||
|
||||
return PluginManager.preloadPackedContent(outputName);
|
||||
}
|
||||
|
||||
return PluginManager.reloadContent(oldContent);
|
||||
return manager.preloadPackedContent(outputName);
|
||||
} catch (err) {
|
||||
if (outputPath) {
|
||||
rimraf(outputPath, err => {
|
||||
if (err) console.log(err);
|
||||
});
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,56 @@
|
|||
.bd-pluginsview,
|
||||
.bd-themesview {
|
||||
.bd-onlinePh {
|
||||
.bd-localPh {
|
||||
.bd-scroller {
|
||||
padding: 0 20px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.bd-onlinePh,
|
||||
.bd-localPh {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 10% 0;
|
||||
margin: 10px 0;
|
||||
|
||||
.bd-spinnerContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bd-onlinePhHeader {
|
||||
display: flex;
|
||||
padding: 0 20px 0 10px;
|
||||
min-height: 80px;
|
||||
|
||||
.bd-flexRow {
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.bd-searchHint {
|
||||
flex-grow: 1;
|
||||
line-height: 40px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.bd-searchSort {
|
||||
span {
|
||||
color: #fff;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bd-onlinePhBody {
|
||||
margin-top: 10px;
|
||||
|
||||
.bd-spinnerContainer {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.bd-scroller {
|
||||
padding: 0 20px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #fff;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
.bd-remoteCard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 10px;
|
||||
padding: 10px 0;
|
||||
padding: 10px;
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid rgba(114, 118, 126, .3);
|
||||
|
||||
|
@ -52,17 +51,19 @@
|
|||
.bd-remoteCardTags {
|
||||
color: #828a97;
|
||||
font-size: 10px;
|
||||
line-height: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.bd-buttonGroup {
|
||||
align-self: flex-end;
|
||||
justify-content: flex-end;
|
||||
flex-grow: 1;
|
||||
max-height: 20px;
|
||||
max-height: 30px;
|
||||
|
||||
.bd-button {
|
||||
font-size: 12px;
|
||||
font-size: 16px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,11 +164,11 @@
|
|||
.bd-fancySearch {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
transform: translateY(80px) translateX(-140px);
|
||||
transition: all .5s ease-in-out;
|
||||
// transform: translateY(80px) translateX(-140px);
|
||||
// transition: all .5s ease-in-out;
|
||||
|
||||
&::before {
|
||||
content: 'Search by name, description or tag...';
|
||||
// content: 'Search by name, description or tag...';
|
||||
color: #f6f6f7;
|
||||
position: relative;
|
||||
top: -20px;
|
||||
|
@ -184,6 +184,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.bd-disabled {
|
||||
.bd-textInput {
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
|
||||
.bd-textInput {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
|
|
|
@ -120,8 +120,14 @@
|
|||
}
|
||||
|
||||
.bd-settingswrapContents {
|
||||
padding: 0 20px;
|
||||
margin-bottom: 84px;
|
||||
padding: 0 0 0 20px;
|
||||
}
|
||||
|
||||
.bd-scroller {
|
||||
.bd-settingswrapContents {
|
||||
margin-bottom: 84px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,8 @@
|
|||
|
||||
<script>
|
||||
// Imports
|
||||
import asar from 'asar';
|
||||
import electron from 'electron';
|
||||
import fs from 'fs';
|
||||
import { Toasts } from 'ui';
|
||||
import { Settings } from 'modules';
|
||||
import { Settings, PluginManager } from 'modules';
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
import { shell } from 'electron';
|
||||
import Card from './Card.vue';
|
||||
|
@ -45,24 +42,13 @@
|
|||
MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension, MiBoxDownload
|
||||
},
|
||||
methods: {
|
||||
package() {
|
||||
electron.remote.dialog.showSaveDialog({
|
||||
title: 'Save Plugin Package',
|
||||
defaultPath: this.plugin.name,
|
||||
filters: [
|
||||
{
|
||||
name: 'BetterDiscord Package',
|
||||
extensions: ['bd']
|
||||
}
|
||||
]
|
||||
}, filepath => {
|
||||
if (!filepath) return;
|
||||
|
||||
asar.uncache(filepath);
|
||||
asar.createPackage(this.plugin.contentPath, filepath, () => {
|
||||
Toasts.success('Plugin Packaged!');
|
||||
});
|
||||
});
|
||||
async package() {
|
||||
try {
|
||||
const packagePath = await PluginManager.packContent(this.plugin.name, this.plugin.contentPath);
|
||||
Toasts.success(`Plugin Packaged: ${packagePath}`);
|
||||
} catch (err) {
|
||||
Logger.log('PluginCard', err);
|
||||
}
|
||||
},
|
||||
editPlugin() {
|
||||
try {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*/
|
||||
|
||||
<template>
|
||||
<SettingsWrapper headertext="Plugins">
|
||||
<SettingsWrapper headertext="Plugins" :noscroller="true">
|
||||
<div class="bd-tabbar" slot="header">
|
||||
<div class="bd-button" :class="{'bd-active': local}" @click="showLocal">
|
||||
<h3>Installed</h3>
|
||||
|
@ -22,12 +22,26 @@
|
|||
</div>
|
||||
|
||||
<div class="bd-flex bd-flexCol bd-pluginsview">
|
||||
<div v-if="local" class="bd-flex bd-flexGrow bd-flexCol bd-pluginsContainer bd-localPlugins">
|
||||
<PluginCard v-for="plugin in localPlugins" :plugin="plugin" :key="plugin.id" :data-plugin-id="plugin.id" @toggle-plugin="togglePlugin(plugin)" @reload-plugin="reloadPlugin(plugin)" @delete-plugin="unload => deletePlugin(plugin, unload)" @show-settings="dont_clone => showSettings(plugin, dont_clone)" />
|
||||
<div v-if="local" class="bd-flex bd-flexGrow bd-flexCol bd-pluginsContainer bd-localPlugins bd-localPh">
|
||||
<ScrollerWrap>
|
||||
<PluginCard v-for="plugin in localPlugins" :plugin="plugin" :key="plugin.id" :data-plugin-id="plugin.id" @toggle-plugin="togglePlugin(plugin)" @reload-plugin="reloadPlugin(plugin)" @delete-plugin="unload => deletePlugin(plugin, unload)" @show-settings="dont_clone => showSettings(plugin, dont_clone)" />
|
||||
</ScrollerWrap>
|
||||
</div>
|
||||
<div v-if="!local" class="bd-onlinePh">
|
||||
<h3>Coming Soon</h3>
|
||||
<a href="https://v2.betterdiscord.net/plugins" target="_new">Website Browser</a>
|
||||
<div v-else class="bd-onlinePh">
|
||||
<div class="bd-onlinePhHeader">
|
||||
<div class="bd-fancySearch" :class="{'bd-disabled': loadingOnline, 'bd-active': loadingOnline || (onlinePlugins && onlinePlugins.docs)}">
|
||||
<input type="text" class="bd-textInput" placeholder="Search" @keydown.enter="searchInput" @keyup.stop />
|
||||
</div>
|
||||
<div v-if="loadingOnline" class="bd-spinnerContainer">
|
||||
<div class="bd-spinner7" />
|
||||
</div>
|
||||
</div>
|
||||
<ScrollerWrap class="bd-onlinePhBody" v-if="!loadingOnline && onlinePlugins" :scrollend="scrollend">
|
||||
<RemoteCard v-if="onlinePlugins && onlinePlugins.docs" v-for="plugin in onlinePlugins.docs" :key="plugin.id" :item="plugin" />
|
||||
<div v-if="loadingMore" class="bd-spinnerContainer">
|
||||
<div class="bd-spinner7" />
|
||||
</div>
|
||||
</ScrollerWrap>
|
||||
</div>
|
||||
</div>
|
||||
</SettingsWrapper>
|
||||
|
@ -38,7 +52,7 @@
|
|||
import { PluginManager } from 'modules';
|
||||
import { Modals } from 'ui';
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
import { MiRefresh } from '../common';
|
||||
import { MiRefresh, ScrollerWrap } from '../common';
|
||||
import SettingsWrapper from './SettingsWrapper.vue';
|
||||
import PluginCard from './PluginCard.vue';
|
||||
import RefreshBtn from '../common/RefreshBtn.vue';
|
||||
|
@ -48,12 +62,15 @@
|
|||
return {
|
||||
PluginManager,
|
||||
local: true,
|
||||
localPlugins: PluginManager.localPlugins
|
||||
localPlugins: PluginManager.localPlugins,
|
||||
onlinePlugins: null,
|
||||
loadingOnline: false,
|
||||
loadingMore: false
|
||||
};
|
||||
},
|
||||
components: {
|
||||
SettingsWrapper, PluginCard,
|
||||
MiRefresh,
|
||||
MiRefresh, ScrollerWrap,
|
||||
RefreshBtn
|
||||
},
|
||||
methods: {
|
||||
|
@ -96,6 +113,24 @@
|
|||
return Modals.contentSettings(plugin, null, {
|
||||
dont_clone
|
||||
});
|
||||
},
|
||||
searchInput(e) {
|
||||
if (this.loadingOnline || this.loadingMore) return;
|
||||
this.refreshOnline();
|
||||
},
|
||||
async scrollend(e) {
|
||||
// TODO
|
||||
return;
|
||||
if (this.loadingOnline || this.loadingMore) return;
|
||||
this.loadingMore = true;
|
||||
try {
|
||||
const getPlugins = await BdWebApi.plugins.get();
|
||||
this.onlinePlugins.docs = [...this.onlinePlugins.docs, ...getPlugins.docs];
|
||||
} catch (err) {
|
||||
Logger.err('PluginsView', err);
|
||||
} finally {
|
||||
this.loadingMore = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<div class="bd-remoteCardInfoBox bd-flex bd-flexGrow bd-flexCol">
|
||||
<div class="bd-remoteCardInfo">{{item.installs}} Installs</div>
|
||||
<div class="bd-remoteCardInfo">{{item.activeUsers}} Active Users</div>
|
||||
<div class="bd-remoteCardInfo">Updated: Some time ago</div>
|
||||
<div class="bd-remoteCardInfo">Updated {{fromNow()}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -29,13 +29,16 @@
|
|||
<div class="bd-buttonGroup">
|
||||
<div class="bd-button">Install</div>
|
||||
<div class="bd-button">Preview</div>
|
||||
<div class="bd-button">Source</div>
|
||||
<div class="bd-button" @click="openSourceUrl">Source</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Reflection } from 'modules';
|
||||
import { shell } from 'electron';
|
||||
|
||||
export default {
|
||||
props: ['item'],
|
||||
data() {
|
||||
|
@ -44,6 +47,15 @@
|
|||
methods: {
|
||||
resolveThumb() {
|
||||
return `${this.item.repository.rawUri}/${this.item.files.previews[0].thumb}`;
|
||||
},
|
||||
fromNow() {
|
||||
const { Moment } = Reflection.modules;
|
||||
return Moment(this.item.updated).fromNow();
|
||||
},
|
||||
openSourceUrl() {
|
||||
if (!this.item.repository || !this.item.repository.baseUri) return;
|
||||
if (Object.assign(document.createElement('a'), { href: this.item.repository.baseUri }).hostname !== 'github.com') return;
|
||||
shell.openExternal(this.item.repository.baseUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,16 @@
|
|||
|
||||
<template>
|
||||
<div class="bd-settingswrap">
|
||||
<ScrollerWrap>
|
||||
<div v-if="noscroller" class="bd-flex bd-flexCol">
|
||||
<div class="bd-settingswrapHeader">
|
||||
<span class="bd-settingswrapHeaderText">{{ headertext }}</span>
|
||||
<slot name="header" />
|
||||
</div>
|
||||
<div class="bd-settingswrapContents bd-flex bd-flexGrow bd-flexCol">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<ScrollerWrap v-else :scrollend="scrollend">
|
||||
<div class="bd-settingswrapHeader">
|
||||
<span class="bd-settingswrapHeaderText">{{ headertext }}</span>
|
||||
<slot name="header" />
|
||||
|
@ -27,7 +36,7 @@
|
|||
import { ScrollerWrap } from '../common';
|
||||
|
||||
export default {
|
||||
props: ['headertext'],
|
||||
props: ['headertext', 'scrollend', 'noscroller'],
|
||||
components: {
|
||||
ScrollerWrap
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<Card :item="theme">
|
||||
<SettingSwitch slot="toggle" :value="theme.enabled" @input="$emit('toggle-theme')" />
|
||||
<ButtonGroup slot="controls" v-if="!online">
|
||||
<Button v-if="devmode && !theme.packed" v-tooltip="'Package Theme'" @click="package"><MiBoxDownload size="18" /></Button>
|
||||
<Button v-tooltip="'Settings (shift + click to open settings without cloning the set)'" v-if="theme.hasSettings" @click="$emit('show-settings', $event.shiftKey)"><MiSettings size="18" /></Button>
|
||||
<Button v-tooltip="'Recompile (shift + click to reload)'" @click="$emit('reload-theme', $event.shiftKey)"><MiRefresh size="18" /></Button>
|
||||
<Button v-tooltip="'Edit'" @click="editTheme"><MiPencil size="18" /></Button>
|
||||
|
@ -22,17 +23,33 @@
|
|||
|
||||
<script>
|
||||
// Imports
|
||||
import { Toasts } from 'ui';
|
||||
import { Settings, ThemeManager } from 'modules';
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
import { shell } from 'electron';
|
||||
import Card from './Card.vue';
|
||||
import { Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension } from '../common';
|
||||
import { Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension, MiBoxDownload } from '../common';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
devmode: Settings.getSetting('core', 'advanced', 'developer-mode').value
|
||||
}
|
||||
},
|
||||
props: ['theme', 'online'],
|
||||
components: {
|
||||
Card, Button, ButtonGroup, SettingSwitch,
|
||||
MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension
|
||||
MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension, MiBoxDownload
|
||||
},
|
||||
methods: {
|
||||
async package() {
|
||||
try {
|
||||
const packagePath = await ThemeManager.packContent(this.theme.name, this.theme.contentPath);
|
||||
Toasts.success(`Theme Packaged: ${packagePath}`);
|
||||
} catch (err) {
|
||||
Logger.log('ThemeCard', err);
|
||||
}
|
||||
},
|
||||
editTheme() {
|
||||
try {
|
||||
shell.openItem(this.theme.contentPath);
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*/
|
||||
|
||||
<template>
|
||||
<SettingsWrapper headertext="Themes">
|
||||
<SettingsWrapper headertext="Themes" :noscroller="true">
|
||||
<div class="bd-tabbar" slot="header">
|
||||
<div class="bd-button" :class="{'bd-active': local}" @click="showLocal">
|
||||
<h3>Installed</h3>
|
||||
|
@ -22,15 +22,38 @@
|
|||
</div>
|
||||
|
||||
<div class="bd-flex bd-flexCol bd-themesview">
|
||||
<div v-if="local" class="bd-flex bd-flexGrow bd-flexCol bd-themesContainer bd-localThemes">
|
||||
<ThemeCard v-for="theme in localThemes" :theme="theme" :key="theme.id" :data-theme-id="theme.id" @toggle-theme="toggleTheme(theme)" @reload-theme="reload => reloadTheme(theme, reload)" @show-settings="dont_clone => showSettings(theme, dont_clone)" @delete-theme="unload => deleteTheme(theme, unload)" />
|
||||
<div v-if="local" class="bd-flex bd-flexGrow bd-flexCol bd-themesContainer bd-localPh">
|
||||
<ScrollerWrap>
|
||||
<ThemeCard v-for="theme in localThemes" :theme="theme" :key="theme.id" :data-theme-id="theme.id" @toggle-theme="toggleTheme(theme)" @reload-theme="reload => reloadTheme(theme, reload)" @show-settings="dont_clone => showSettings(theme, dont_clone)" @delete-theme="unload => deleteTheme(theme, unload)" />
|
||||
</ScrollerWrap>
|
||||
</div>
|
||||
<div v-if="!local" class="bd-onlinePh">
|
||||
<div class="bd-fancySearch" :class="{'bd-active': loadingOnline || (onlineThemes && onlineThemes.docs)}">
|
||||
<input type="text" class="bd-textInput" @keydown.enter="searchInput" @keyup.stop/>
|
||||
<div v-else class="bd-onlinePh">
|
||||
<div class="bd-onlinePhHeader bd-flexCol">
|
||||
<div class="bd-flex bd-flexRow">
|
||||
<div v-if="loadingOnline" class="bd-spinnerContainer">
|
||||
<div class="bd-spinner7" />
|
||||
</div>
|
||||
<div class="bd-searchHint">{{searchHint}}</div>
|
||||
<div class="bd-fancySearch" :class="{'bd-disabled': loadingOnline, 'bd-active': loadingOnline || (onlineThemes && onlineThemes.docs)}">
|
||||
<input type="text" class="bd-textInput" placeholder="Search" @keydown.enter="searchInput" @keyup.stop />
|
||||
</div>
|
||||
</div>
|
||||
<div class="bd-flex bd-flexRow" v-if="onlineThemes && onlineThemes.docs && onlineThemes.docs.length">
|
||||
<div class="bd-searchSort bd-flex bd-flexGrow">
|
||||
<span class="bd-flexGrow">Sort by:</span>
|
||||
<div class="bd-sort">Name</div>
|
||||
<div class="bd-sort">Updated</div>
|
||||
<div class="bd-sort">Installs</div>
|
||||
<div class="bd-sort">Users</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 v-if="loadingOnline">Loading</h2>
|
||||
<RemoteCard v-else-if="onlineThemes && onlineThemes.docs" v-for="theme in onlineThemes.docs" :key="theme.id" :item="theme"/>
|
||||
<ScrollerWrap class="bd-onlinePhBody" v-if="!loadingOnline && onlineThemes" :scrollend="scrollend">
|
||||
<RemoteCard v-if="onlineThemes && onlineThemes.docs" v-for="theme in onlineThemes.docs" :key="theme.id" :item="theme" />
|
||||
<div v-if="loadingMore" class="bd-spinnerContainer">
|
||||
<div class="bd-spinner7"/>
|
||||
</div>
|
||||
</ScrollerWrap>
|
||||
</div>
|
||||
</div>
|
||||
</SettingsWrapper>
|
||||
|
@ -41,7 +64,7 @@
|
|||
import { ThemeManager, BdWebApi } from 'modules';
|
||||
import { Modals } from 'ui';
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
import { MiRefresh } from '../common';
|
||||
import { MiRefresh, ScrollerWrap } from '../common';
|
||||
import SettingsWrapper from './SettingsWrapper.vue';
|
||||
import ThemeCard from './ThemeCard.vue';
|
||||
import RemoteCard from './RemoteCard.vue';
|
||||
|
@ -54,12 +77,14 @@
|
|||
local: true,
|
||||
localThemes: ThemeManager.localThemes,
|
||||
onlineThemes: null,
|
||||
loadingOnline: false
|
||||
loadingOnline: false,
|
||||
loadingMore: false,
|
||||
searchHint: ''
|
||||
};
|
||||
},
|
||||
components: {
|
||||
SettingsWrapper, ThemeCard, RemoteCard,
|
||||
MiRefresh,
|
||||
MiRefresh, ScrollerWrap,
|
||||
RefreshBtn
|
||||
},
|
||||
methods: {
|
||||
|
@ -74,38 +99,14 @@
|
|||
await this.ThemeManager.refreshThemes();
|
||||
},
|
||||
async refreshOnline() {
|
||||
this.searchHint = '';
|
||||
if (this.loadingOnline || this.loadingMore) return;
|
||||
this.loadingOnline = true;
|
||||
try {
|
||||
// const getThemes = await BdWebApi.themes.get();
|
||||
// this.onlineThemes = JSON.parse(getThemes);
|
||||
const dummies = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
dummies.push({
|
||||
id: `theme${i}`,
|
||||
name: `Dummy ${i}`,
|
||||
tags: ['tag1', 'tag2', 'tag3', 'tag4', 'tag5'],
|
||||
installs: Math.floor(Math.random() * 10000),
|
||||
updated: '2018-07-21T14:51:32.057Z',
|
||||
rating: Math.floor(Math.random() * 1000),
|
||||
activeUsers: Math.floor(Math.random() * 1000),
|
||||
rated: Math.random() > .5,
|
||||
version: '1.0.0',
|
||||
repository: {
|
||||
name: 'ExampleRepository',
|
||||
baseUri: 'https://github.com/Jiiks/ExampleRepository',
|
||||
rawUri: 'https://github.com/Jiiks/ExampleRepository/raw/master'
|
||||
},
|
||||
files: {
|
||||
readme: 'Example/readme.md',
|
||||
previews: [{
|
||||
large: 'Example/preview1-big.png',
|
||||
thumb: 'Example/preview1-small.png'
|
||||
}]
|
||||
},
|
||||
author: 'Jiiks'
|
||||
});
|
||||
}
|
||||
this.onlineThemes = { docs: dummies };
|
||||
const getThemes = await BdWebApi.themes.get();
|
||||
this.onlineThemes = getThemes;
|
||||
if (!this.onlineThemes.docs) return;
|
||||
this.searchHint = `${this.onlineThemes.pagination.total} Results`;
|
||||
} catch (err) {
|
||||
Logger.err('ThemesView', err);
|
||||
} finally {
|
||||
|
@ -140,8 +141,20 @@
|
|||
});
|
||||
},
|
||||
searchInput(e) {
|
||||
this.loadingOnline = true;
|
||||
setTimeout(this.refreshOnline, 1000);
|
||||
if (this.loadingOnline || this.loadingMore) return;
|
||||
this.refreshOnline();
|
||||
},
|
||||
async scrollend(e) {
|
||||
if (this.loadingOnline || this.loadingMore) return;
|
||||
this.loadingMore = true;
|
||||
try {
|
||||
const getThemes = await BdWebApi.themes.get();
|
||||
this.onlineThemes.docs = [...this.onlineThemes.docs, ...getThemes.docs];
|
||||
} catch (err) {
|
||||
Logger.err('ThemesView', err);
|
||||
} finally {
|
||||
this.loadingMore = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
},
|
||||
async install() {
|
||||
try {
|
||||
const installed = await PackageInstaller.installPackage(this.modal.filePath, this.modal.config.info.id, this.alreadyInstalled);
|
||||
const installed = await PackageInstaller.installPackage(this.modal.filePath, this.modal.config.info.id || this.modal.config.info.name, this.modal.contentType, this.alreadyInstalled);
|
||||
this.installed = installed;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<template>
|
||||
<div class="bd-scrollerWrap" :class="{'bd-dark': dark}">
|
||||
<div class="bd-scroller">
|
||||
<div class="bd-scroller" @scroll="onscroll">
|
||||
<slot/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,6 +18,13 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
props: ['dark']
|
||||
props: ['dark', 'scrollend'],
|
||||
methods: {
|
||||
onscroll(e) {
|
||||
if (!this.scrollend) return;
|
||||
const { offsetHeight, scrollTop, scrollHeight } = e.target;
|
||||
if (offsetHeight + scrollTop >= scrollHeight) this.scrollend(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue