Merge pull request #202 from JsSucks/bdapi

Online theme browser
This commit is contained in:
Alexei Stukov 2018-08-07 07:21:24 +03:00 committed by GitHub
commit 9f00aed4fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 303 additions and 29 deletions

View File

@ -13,23 +13,54 @@ import { request } from 'vendor';
const APIBASE = 'ifyouareinwebtestthenyouknowwhatthisshouldbe'; // Do not push
const ENDPOINTS = {
'themes': `${APIBASE}/themes`,
'theme': id => `${APIBASE}/theme/${id}`,
'users': `${APIBASE}/users`,
'user': id => `${APIBASE}/user/${id}`,
'statistics': `${APIBASE}/statistics`
};
export default class BdWebApi {
static async getThemes() {
const get = await request.get(ENDPOINTS.themes);
return JSON.parse(get);
static get themes() {
return {
get: this.getThemes
};
}
static async getUsers() {
const get = await request.get(ENDPOINTS.users);
return get;
static get users() {
return {
get: this.getUsers
};
}
static async patchStatistics(json) {
return await request({ method: 'PATCH', url: ENDPOINTS.statistics, json });
static get statistics() {
return {
get: this.getStatistics,
patch: this.patchStatistics
};
}
static getThemes(args) {
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) {
if (!args) return request.get(ENDPOINTS.users);
const { id } = args;
if (id) return request.get(ENDPOINTS.user(id));
return request.get(ENDPOINTS.users);
}
static getStatistics() {
return request.get(ENDPOINTS.statistics);
}
static patchStatistics(json) {
return request({ method: 'PATCH', url: ENDPOINTS.statistics, json });
}
}

View File

@ -11,20 +11,14 @@
import BdWebApi from './bdwebapi';
import { ClientLogger as Logger } from 'common';
const APIBASE = 'ifyouareinwebtestthenyouknowwhatthisshouldbe'; // Do not push
const ENDPOINTS = {
'themes': `${APIBASE}/themes`,
'users': `${APIBASE}/users`
};
export default class Connectivity {
static start() {
Logger.info('Connectivity', `Patching anonymous statistics`);
BdWebApi.patchStatistics({ themes: [], plugins: [] });
BdWebApi.statistics.patch({ themes: [], plugins: [] });
setInterval(() => {
Logger.info('Connectivity', `Patching anonymous statistics`);
BdWebApi.patchStatistics({ themes: [], plugins: [] });
BdWebApi.statistics.patch({ themes: [], plugins: [] });
}, 15*60*1000);
}

View File

@ -2,6 +2,7 @@
@import './sidebarview.scss';
@import './contentview.scss';
@import './card.scss';
@import './remotecard.scss';
@import './tooltips.scss';
@import './settings-schemes.scss';
@import './updater.scss';

View File

@ -0,0 +1,72 @@
.bd-remoteCard {
display: flex;
flex-direction: column;
// background: rgba(0,0,0,.1);
margin-top: 10px;
padding: 10px 0;
border-radius: 0;
border-bottom: 1px solid rgba(114, 118, 126, 0.3);
&:hover {
// background: rgba(0,0,0,.2);
transform: scale(1.005);
// box-shadow: 0px 0px 5px rgba(0,0,0,.5);
}
.bd-remoteCard-title {
color: #b9bbbe;
font-weight: 700;
}
.bd-remoteCard-likes {
color: red;
font-size: 12px;
font-weight: 600;
}
.bd-remoteCard-infoContainer {
margin-left: 10px;
.bd-remoteCard-infoBox {
color: #b3b5b8;
font-size: 12px;
font-weight: 700;
.bd-remoteCard-info {
display: flex;
font-size: 12px;
line-height: 16px;
.bd-material-design-icon {
display: flex;
fill: $colbdgreen;
}
}
}
}
.bd-remoteCard-thumb {
height: 100px;
width: 180px;
background: rgba(0,0,0,.1);
border-radius: 3px;
}
.bd-remoteCard-tags {
color: #828a97;
font-size: 10px;
line-height: 20px;
}
.bd-button-group {
align-self: flex-end;
justify-content: flex-end;
flex-grow: 1;
max-height: 20px;
.bd-button {
font-size: 12px;
padding: 5px 10px;
}
}
}

View File

@ -139,3 +139,48 @@
}
}
}
.bd-textInput {
background-color: rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.3);
border-radius: 3px;
box-sizing: border-box;
color: #f6f6f7;
font-size: 14px;
transition: border .5s ease;
&:focus {
border: 1px solid #3ecc9c;
}
}
.bd-fancySearch {
display: flex;
justify-content: flex-end;
transform: translateY(80px) translateX(-140px);
transition: all .5s ease-in-out;
&::before {
content: "Search by name, description or tag...";
color: #f6f6f7;
position: relative;
top: -20px;
left: 245px;
transition: opacity .5s ease-in-out;
}
&.bd-active {
transform: none;
&::before {
opacity: 0;
}
}
.bd-textInput {
padding: 10px;
display: flex;
min-height: 40px;
width: 250px;
}
}

View File

@ -2,19 +2,23 @@
display: flex;
}
.bd-flex-row {
.bd-flex-row,
.bd-flexRow {
flex-direction: row;
}
.bd-flex-col {
.bd-flex-col,
.bd-flexCol {
flex-direction: column;
}
.bd-flex-grow {
.bd-flex-grow,
.bd-flexGrow {
flex-grow: 1;
}
.bd-flex-spacer {
.bd-flex-spacer,
.bd-flexSpacer {
flex-grow: 1;
}

View File

@ -0,0 +1,52 @@
/**
* BetterDiscord Remote Card Component
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
<template>
<div class="bd-remoteCard">
<div class="bd-flexRow bd-flex">
<div class="bd-remoteCard-title bd-flexGrow">{{item.name}} v{{item.version}} by {{item.author}}</div>
<div class="bd-remoteCard-likes">{{item.rating}}</div>
</div>
<div class="bd-flexRow bd-flex" :style="{marginTop: '10px'}">
<div class="bd-remoteCard-thumb" :style="{backgroundImage: `url(${resolveThumb()})`}"></div>
<div class="bd-remoteCard-infoContainer bd-flex bd-flexCol bd-flexGrow">
<div class="bd-remoteCard-infoBox bd-flex bd-flexGrow bd-flexCol">
<div class="bd-remoteCard-info">{{item.installs}} Installs</div>
<div class="bd-remoteCard-info">{{item.activeUsers}} Active Users</div>
<div class="bd-remoteCard-info">Updated: Some time ago</div>
</div>
</div>
</div>
<div class="bd-flexRow bd-flex bd-flexGrow">
<div class="bd-flexGrow bd-remoteCard-tags">{{item.tags.join(', ')}}</div>
<div class="bd-button-group">
<div class="bd-button">Install</div>
<div class="bd-button">Preview</div>
<div class="bd-button">Source</div>
</div>
</div>
</div>
</template>
<script>
import { WebpackModules } from 'modules';
export default {
props: ['item'],
data() {
return {}
},
methods: {
resolveThumb() {
window.momentTest = WebpackModules;
return `${this.item.repository.rawUri}/${this.item.files.previews[0].thumb}`;
}
}
}
</script>

View File

@ -11,7 +11,7 @@
<template>
<Card :item="theme">
<SettingSwitch slot="toggle" :value="theme.enabled" @input="$emit('toggle-theme')" />
<ButtonGroup slot="controls">
<ButtonGroup slot="controls" v-if="!online">
<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>
@ -27,7 +27,7 @@
import { Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension } from '../common';
export default {
props: ['theme'],
props: ['theme', 'online'],
components: {
Card, Button, ButtonGroup, SettingSwitch,
MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension

View File

@ -26,8 +26,11 @@
<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>
<div v-if="!local" class="bd-online-ph">
<h3>Coming Soon</h3>
<a href="https://v2.betterdiscord.net/themes" target="_new">Website Browser</a>
<div class="bd-fancySearch" :class="{'bd-active': loadingOnline || (onlineThemes && onlineThemes.docs)}">
<input type="text" class="bd-textInput" @keydown.enter="searchInput" @keyup.stop/>
</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"/>
</div>
</div>
</SettingsWrapper>
@ -35,12 +38,13 @@
<script>
// Imports
import { ThemeManager } from 'modules';
import { ThemeManager, BdWebApi } from 'modules';
import { Modals } from 'ui';
import { ClientLogger as Logger } from 'common';
import { MiRefresh } from '../common';
import SettingsWrapper from './SettingsWrapper.vue';
import ThemeCard from './ThemeCard.vue';
import RemoteCard from './RemoteCard.vue';
import RefreshBtn from '../common/RefreshBtn.vue';
export default {
@ -48,11 +52,13 @@
return {
ThemeManager,
local: true,
localThemes: ThemeManager.localThemes
localThemes: ThemeManager.localThemes,
onlineThemes: null,
loadingOnline: false
};
},
components: {
SettingsWrapper, ThemeCard,
SettingsWrapper, ThemeCard, RemoteCard,
MiRefresh,
RefreshBtn
},
@ -60,14 +66,51 @@
showLocal() {
this.local = true;
},
showOnline() {
async showOnline() {
this.local = false;
if (this.loadingOnline || this.onlineThemes) return;
},
async refreshLocal() {
await this.ThemeManager.refreshThemes();
},
async refreshOnline() {
// TODO
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 };
} catch (err) {
Logger.err('ThemesView', err);
} finally {
this.loadingOnline = false;
}
},
async toggleTheme(theme) {
// TODO: display error if theme fails to enable/disable
@ -95,6 +138,10 @@
return Modals.contentSettings(theme, null, {
dont_clone
});
},
searchInput(e) {
this.loadingOnline = true;
setTimeout(this.refreshOnline, 1000);
}
}
}

View File

@ -17,3 +17,4 @@ export { default as MiStar } from './materialicons/Star.vue';
export { default as MiInfo } from './materialicons/Info.vue';
export { default as MiWarning } from './materialicons/Warning.vue';
export { default as MiSuccess } from './materialicons/Success.vue';
export { default as AccountCircle } from './materialicons/AccountCircle.vue';

View File

@ -0,0 +1,27 @@
/**
* BetterDiscord Material Design Icon
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* Material Design Icons
* Copyright (c) 2014 Google
* Apache 2.0 LICENSE
* https://www.apache.org/licenses/LICENSE-2.0.txt
*/
<template>
<span class="bd-material-design-icon">
<svg :width="size || 24" :height="size || 24" viewBox="0 0 24 24">
<path d="M 11.9992,19.1982C 9.4952,19.1982 7.2922,17.9193 6.0012,15.9783C 6.0272,13.9913 10.0052,12.8983 11.9992,12.8983C 13.9932,12.8983 17.9712,13.9913 17.9972,15.9783C 16.7062,17.9193 14.5032,19.1982 11.9992,19.1982 Z M 11.9992,4.99823C 13.6552,4.99823 14.9992,6.34222 14.9992,7.99823C 14.9992,9.65625 13.6552,10.9982 11.9992,10.9982C 10.3432,10.9982 8.9992,9.65625 8.9992,7.99823C 8.9992,6.34222 10.3432,4.99823 11.9992,4.99823 Z M 11.9992,1.99823C 6.4762,1.99823 1.9992,6.47424 1.9992,11.9982C 1.9992,17.5212 6.4762,21.9982 11.9992,21.9982C 17.5222,21.9982 21.9992,17.5212 21.9992,11.9982C 21.9992,6.47424 17.5222,1.99823 11.9992,1.99823 Z"></path>
</svg>
</span>
</template>
<script>
export default {
props: ['size']
}
</script>