diff --git a/client/src/dev/serveremu.js b/client/src/dev/serveremu.js new file mode 100644 index 00000000..0c13ff7e --- /dev/null +++ b/client/src/dev/serveremu.js @@ -0,0 +1,232 @@ +const dummyTags = ['dark', 'light', 'simple', 'minimal', 'extra', 'something', 'tag', 'whatever', 'another', 'transparent']; + +export default class ServerEmu { + + static async themes(args) { + if (!this._themes) this._themes = this.generateThemes(); + + await new Promise(r => setTimeout(r, Math.random() * 3000)); + + let docs = []; + + if (args && args.sterm) { + const { sterm } = args; + const reg = new RegExp(sterm, 'gi'); + docs = this._themes.filter(doc => doc.tags.includes(sterm) || reg.exec(doc.name) || reg.exec(doc.description)); + } else { + docs = this._themes; + } + + if (args.sort) { + switch (args.sort) { + case 'updated': + if (args.ascending) docs = docs.sort((docA, docB) => new Date(docA.updated).getTime() - new Date(docB.updated).getTime()); + else docs = docs.sort((docA, docB) => new Date(docB.updated).getTime() - new Date(docA.updated).getTime()); + break; + case 'installs': + if (args.ascending) docs = docs.sort((docA, docB) => docA.installs - docB.installs); + else docs = docs.sort((docA, docB) => docB.installs - docA.installs); + break; + case 'users': + if (args.ascending) docs = docs.sort((docA, docB) => docA.activeUsers - docB.activeUsers); + else docs = docs.sort((docA, docB) => docB.activeUsers - docA.activeUsers); + break; + case 'rating': + if (args.ascending) docs = docs.sort((docA, docB) => docA.rating - docB.rating); + else docs = docs.sort((docA, docB) => docB.rating - docA.rating); + break; + } + } + + const total = docs.length; + const pages = Math.ceil(total / 9); + + let page = 1; + if (args && args.page) { + page = args.page; + docs = docs.slice((page - 1) * 9, page * 9); + } else { + docs = docs.slice(0, 9); + } + + return { + docs, + filters: { + sterm: args.sterm || '', + ascending: args.ascending || false, + sort: args.sort || 'name' + }, + pagination: { + total, + pages, + limit: 9, + page + } + } + } + + static async plugins(args) { + if (!this._plugins) this._plugins = this.generatePlugins(); + + await new Promise(r => setTimeout(r, Math.random() * 3000)); + + let docs = []; + + if (args && args.sterm) { + const { sterm } = args; + const reg = new RegExp(sterm, 'gi'); + docs = this._plugins.filter(doc => doc.tags.includes(sterm) || reg.exec(doc.name) || reg.exec(doc.description)); + } else { + docs = this._plugins; + } + + if (args.sort) { + switch (args.sort) { + case 'updated': + if (args.ascending) docs = docs.sort((docA, docB) => new Date(docA.updated).getTime() - new Date(docB.updated).getTime()); + else docs = docs.sort((docA, docB) => new Date(docB.updated).getTime() - new Date(docA.updated).getTime()); + break; + case 'installs': + if (args.ascending) docs = docs.sort((docA, docB) => docA.installs - docB.installs); + else docs = docs.sort((docA, docB) => docB.installs - docA.installs); + break; + case 'users': + if (args.ascending) docs = docs.sort((docA, docB) => docA.activeUsers - docB.activeUsers); + else docs = docs.sort((docA, docB) => docB.activeUsers - docA.activeUsers); + break; + case 'rating': + if (args.ascending) docs = docs.sort((docA, docB) => docA.rating - docB.rating); + else docs = docs.sort((docA, docB) => docB.rating - docA.rating); + break; + } + } + + const total = docs.length; + const pages = Math.ceil(total / 9); + + let page = 1; + if (args && args.page) { + page = args.page; + docs = docs.slice((page - 1) * 9, page * 9); + } else { + docs = docs.slice(0, 9); + } + + return { + docs, + filters: { + sterm: args.sterm || '', + ascending: args.ascending || false, + sort: args.sort || 'name' + }, + pagination: { + total, + pages, + limit: 9, + page + } + } + } + + static generateThemes() { + const docs = []; + const count = Math.floor(Math.random() * 50 + 30); + + for (let i = 0; i < count; i++) { + const id = `theme${i}-${this.randomId()}`; + const name = `Dummy Theme ${i}`; + const tags = dummyTags.sort(() => .5 - Math.random()).slice(0, 3); + + docs.push({ + id, + name, + tags, + installs: Math.floor(Math.random() * 5000) + 5000, + updated: this.randomTimestamp(), + rating: Math.floor(Math.random() * 500) + 500, + activeUsers: Math.floor(Math.random() * 1000) + 1000, + rated: Math.random() > .5, + version: this.randomVersion(), + repository: this.dummyThemeRepo, + files: this.dummyFiles, + author: this.dummyAuthor, + description: '' + }); + } + + return docs; + } + + static generatePlugins() { + const docs = []; + const count = Math.floor(Math.random() * 50 + 30); + + for (let i = 0; i < count; i++) { + const id = `plugin${i}-${this.randomId()}`; + const name = `Dummy Plugin ${i}`; + const tags = dummyTags.sort(() => .5 - Math.random()).slice(0, 3); + + docs.push({ + id, + name, + tags, + installs: Math.floor(Math.random() * 5000) + 5000, + updated: this.randomTimestamp(), + rating: Math.floor(Math.random() * 500) + 500, + activeUsers: Math.floor(Math.random() * 1000) + 1000, + rated: Math.random() > .5, + version: this.randomVersion(), + repository: this.dummyPluginRepo, + files: this.dummyFiles, + author: this.dummyAuthor, + description: '' + }); + } + + return docs; + } + + static get dummyThemeRepo() { + return { + name: 'ExampleRepository', + baseUri: 'https://github.com/Jiiks/ExampleRepository', + rawUri: 'https://github.com/Jiiks/ExampleRepository/raw/master', + assetUri: 'https://api.github.com/repos/Jiiks/ExampleRepository/releases/assets/10023264' + } + } + + static get dummyPluginRepo() { + return { + name: 'ExampleRepository', + baseUri: 'https://github.com/Jiiks/ExampleRepository', + rawUri: 'https://github.com/Jiiks/ExampleRepository/raw/master', + assetUri: 'https://api.github.com/repos/Jiiks/ExampleRepository/releases/assets/10023265' + } + } + + static get dummyFiles() { + return { + readme: 'Example/readme.md', + previews: [{ + large: 'Example/preview1-big.png', + thumb: 'Example/preview1-small.png' + }] + } + } + + static get dummyAuthor() { + return 'Someone'; + } + + static randomId() { + return btoa(Math.random()).substring(3, 9); + } + + static randomTimestamp() { + return `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`; + } + + static randomVersion() { + return `${Math.round(Math.random() * 3)}.${Math.round(Math.random() * 10)}.${Math.round(Math.random() * 10)}`; + } +} diff --git a/client/src/index.js b/client/src/index.js index 7cfe6483..c64134a0 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -10,7 +10,7 @@ import { DOM, BdUI, BdMenu, Modals, Toasts, Notifications, BdContextMenu, DiscordContextMenu } from 'ui'; import BdCss from './styles/index.scss'; -import { Events, CssEditor, Globals, Settings, Database, Updater, ModuleManager, PluginManager, ThemeManager, ExtModuleManager, Vendor, Patcher, MonkeyPatch, ReactComponents, ReactHelpers, ReactAutoPatcher, DiscordApi, BdWebApi, Connectivity, Cache, Reflection } from 'modules'; +import { Events, CssEditor, Globals, Settings, Database, Updater, ModuleManager, PluginManager, ThemeManager, ExtModuleManager, Vendor, Patcher, MonkeyPatch, ReactComponents, ReactHelpers, ReactAutoPatcher, DiscordApi, BdWebApi, Connectivity, Cache, Reflection, PackageInstaller } from 'modules'; import { ClientLogger as Logger, ClientIPC, Utils } from 'common'; import { BuiltinManager, EmoteModule, ReactDevtoolsModule, VueDevtoolsModule, TrackingProtection, E2EE } from 'builtin'; import electron from 'electron'; @@ -31,7 +31,7 @@ class BetterDiscord { DOM, BdUI, BdMenu, Modals, Reflection, Toasts, Notifications, BdContextMenu, DiscordContextMenu, Events, CssEditor, Globals, Settings, Database, Updater, - ModuleManager, PluginManager, ThemeManager, ExtModuleManager, + ModuleManager, PluginManager, ThemeManager, ExtModuleManager, PackageInstaller, Vendor, Patcher, MonkeyPatch, ReactComponents, ReactHelpers, ReactAutoPatcher, DiscordApi, diff --git a/client/src/modules/bdwebapi.js b/client/src/modules/bdwebapi.js index 393eb112..bd9d325a 100644 --- a/client/src/modules/bdwebapi.js +++ b/client/src/modules/bdwebapi.js @@ -8,6 +8,8 @@ * LICENSE file in the root directory of this source tree. */ +import ServerEmu from '../dev/serveremu'; + import { request } from 'vendor'; const APIBASE = 'ifyouareinwebtestthenyouknowwhatthisshouldbe'; // Do not push @@ -19,54 +21,6 @@ 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() { @@ -75,6 +29,12 @@ export default class BdWebApi { }; } + static get plugins() { + return { + get: this.getPlugins + }; + } + static get users() { return { get: this.getUsers @@ -89,7 +49,8 @@ export default class BdWebApi { } static getThemes(args) { - return dummyThemes(); + return ServerEmu.themes(args); + // return dummyThemes(); /* if (!args) return request.get(ENDPOINTS.themes); const { id } = args; @@ -99,6 +60,10 @@ export default class BdWebApi { */ } + static getPlugins(args) { + return ServerEmu.plugins(args); + } + static getUsers(args) { if (!args) return request.get(ENDPOINTS.users); const { id } = args; diff --git a/client/src/modules/packageinstaller.js b/client/src/modules/packageinstaller.js index f870c4d8..9a19e7a3 100644 --- a/client/src/modules/packageinstaller.js +++ b/client/src/modules/packageinstaller.js @@ -103,7 +103,7 @@ export default class PackageInstaller { } /** - * Install package from remote location. Only github/bdapi is supoorted. + * Install package from remote location. Only github/bdapi is supported. * @param {String} remoteLocation Remote resource location */ static async installRemotePackage(remoteLocation) { @@ -113,6 +113,7 @@ export default class PackageInstaller { const options = { uri: remoteLocation, + encoding: null, headers: { 'User-Agent': 'BetterDiscordClient', 'Accept': 'application/octet-stream' @@ -122,9 +123,10 @@ export default class PackageInstaller { const response = await request.get(options); const outputPath = path.join(Globals.getPath('tmp'), Security.hash('sha256', response, 'hex')); fs.writeFileSync(outputPath, response); + console.log('response', response); + console.log('output', outputPath); await this.dragAndDropHandler(outputPath); - rimraf(outputPath, err => { if (err) console.log(err); }); diff --git a/client/src/styles/partials/bdsettings/contentview.scss b/client/src/styles/partials/bdsettings/contentview.scss index c0272110..9f5a084f 100644 --- a/client/src/styles/partials/bdsettings/contentview.scss +++ b/client/src/styles/partials/bdsettings/contentview.scss @@ -33,9 +33,49 @@ } .bd-searchSort { - span { + margin-top: 10px; + justify-content: flex-end; + + > span { color: #fff; - line-height: 40px; + display: flex; + justify-content: flex-end; + font-size: 12px; + height: 14px; + padding: 3px; + } + + .bd-sort { + display: flex; + color: $coldimwhite; + font-size: 12px; + padding: 3px; + height: 14px; + cursor: pointer; + transition: color .2s ease-in-out; + font-weight: 700; + + &:hover { + color: #fff; + } + + &.bd-active { + color: $colbdgreen; + } + + .bd-materialDesignIcon { + fill: $colbdgreen; + } + + svg { + height: 14px; + } + + &.bd-flipY { + .bd-materialDesignIcon { + transform: scaleY(-1); + } + } } } } @@ -44,6 +84,7 @@ margin-top: 10px; .bd-spinnerContainer { + min-height: 40px; padding: 0; } diff --git a/client/src/styles/partials/bdsettings/remotecard.scss b/client/src/styles/partials/bdsettings/remotecard.scss index c50759be..7c62e121 100644 --- a/client/src/styles/partials/bdsettings/remotecard.scss +++ b/client/src/styles/partials/bdsettings/remotecard.scss @@ -5,17 +5,13 @@ border-radius: 0; border-bottom: 1px solid rgba(114, 118, 126, .3); - &:hover { - transform: scale(1.005); - } - .bd-remoteCardTitle { color: #b9bbbe; font-weight: 700; } .bd-remoteCardLikes { - color: #f00; + color: $colerr; font-size: 12px; font-weight: 600; } @@ -52,8 +48,19 @@ color: #828a97; font-size: 10px; display: flex; - flex-direction: column; - justify-content: flex-end; + align-items: flex-end; + + .bd-remoteCardTag { + > div { + display: inline; + cursor: pointer; + margin-left: 2px; + + &:hover { + color: #fff; + } + } + } } .bd-buttonGroup { diff --git a/client/src/ui/components/bd/PluginsView.vue b/client/src/ui/components/bd/PluginsView.vue index 16226dbb..92758bc8 100644 --- a/client/src/ui/components/bd/PluginsView.vue +++ b/client/src/ui/components/bd/PluginsView.vue @@ -28,18 +28,31 @@