add edit/delete and improve window
This commit is contained in:
parent
0ea223c8f3
commit
3e933923e8
35
css/main.css
35
css/main.css
|
@ -232,15 +232,28 @@
|
||||||
padding: 2px 0;
|
padding: 2px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.floating-window-buttons {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
.floating-window-buttons .button {
|
.floating-window-buttons .button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0 2px;
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-window-buttons .close-button svg {
|
.floating-window-buttons .button svg {
|
||||||
|
fill: #dcddde;
|
||||||
margin-top: 1.5px;
|
margin-top: 1.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.floating-window-buttons .button:hover svg {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-window-buttons .button:hover {
|
||||||
|
background-color: #36393F;
|
||||||
|
}
|
||||||
|
|
||||||
.floating-window-buttons .close-button:hover {
|
.floating-window-buttons .close-button:hover {
|
||||||
background-color: #f04747;
|
background-color: #f04747;
|
||||||
}
|
}
|
||||||
|
@ -537,6 +550,19 @@ background-color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.bd-addon-button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-addon-button + .bd-addon-button {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* BEGIN EMOTE STYLING */
|
/* BEGIN EMOTE STYLING */
|
||||||
/* =================== */
|
/* =================== */
|
||||||
#emote-container {
|
#emote-container {
|
||||||
|
@ -1153,6 +1179,13 @@ color: #f6f6f7;
|
||||||
max-width: 750px;
|
max-width: 750px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.floating-addon-window {
|
||||||
|
min-width: 535px;
|
||||||
|
min-height: 605px;
|
||||||
|
max-height: 90%;
|
||||||
|
max-width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
/* Ace Editor Settings */
|
/* Ace Editor Settings */
|
||||||
#ace_settingsmenu_container {
|
#ace_settingsmenu_container {
|
||||||
background: rgba(0,0,0, 0.7)!important;
|
background: rgba(0,0,0, 0.7)!important;
|
||||||
|
|
54
js/main.js
54
js/main.js
File diff suppressed because one or more lines are too long
|
@ -28,10 +28,12 @@ export default [
|
||||||
type: "category",
|
type: "category",
|
||||||
id: "addons",
|
id: "addons",
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
|
shown: false,
|
||||||
settings: [
|
settings: [
|
||||||
{type: "switch", id: "addonErrors", value: true},
|
{type: "switch", id: "addonErrors", value: true},
|
||||||
{type: "switch", id: "autoScroll", value: true},
|
{type: "switch", id: "autoScroll", value: true},
|
||||||
{type: "switch", id: "autoReload", value: true}
|
{type: "switch", id: "autoReload", value: true},
|
||||||
|
{type: "dropdown", id: "editAction", value: "detached", options: [{value: "detached"}, {value: "system"}]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -71,6 +71,14 @@ export default {
|
||||||
name: "Automatic Loading",
|
name: "Automatic Loading",
|
||||||
note: "Automatically loads, reloads, and unloads plugins and themes"
|
note: "Automatically loads, reloads, and unloads plugins and themes"
|
||||||
},
|
},
|
||||||
|
editAction: {
|
||||||
|
name: "Edit Action",
|
||||||
|
note: "Where plugins & themes appear when editing",
|
||||||
|
options: {
|
||||||
|
detached: "Detached Window",
|
||||||
|
system: "System Editor"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
customcss: {
|
customcss: {
|
||||||
name: "Custom CSS",
|
name: "Custom CSS",
|
||||||
|
@ -191,7 +199,11 @@ export default {
|
||||||
version: "Version",
|
version: "Version",
|
||||||
added: "Date Added",
|
added: "Date Added",
|
||||||
modified: "Date Modified",
|
modified: "Date Modified",
|
||||||
search: "Search {{type}}"
|
search: "Search {{type}}",
|
||||||
|
editAddon: "Edit",
|
||||||
|
deleteAddon: "Delete",
|
||||||
|
confirmDelete: "Are you sure you want to delete {{name}}?",
|
||||||
|
confirmationText: "You have unsaved changes to {{name}}. Closing this window will lose all those changes.",
|
||||||
},
|
},
|
||||||
Emotes: {
|
Emotes: {
|
||||||
loading: "Loading emotes in the background do not reload.",
|
loading: "Loading emotes in the background do not reload.",
|
||||||
|
@ -225,9 +237,10 @@ export default {
|
||||||
query: "for {{query}}"
|
query: "for {{query}}"
|
||||||
},
|
},
|
||||||
Modals: {
|
Modals: {
|
||||||
confirmClose: "Are You Sure?",
|
confirmAction: "Are You Sure?",
|
||||||
okay: "Okay",
|
okay: "Okay",
|
||||||
cancel: "Cancel",
|
cancel: "Cancel",
|
||||||
|
close: "Close",
|
||||||
name: "Name",
|
name: "Name",
|
||||||
message: "Message",
|
message: "Message",
|
||||||
error: "Error",
|
error: "Error",
|
||||||
|
|
|
@ -6,13 +6,20 @@ import DataStore from "./datastore";
|
||||||
import AddonError from "../structs/addonerror";
|
import AddonError from "../structs/addonerror";
|
||||||
import MetaError from "../structs/metaerror";
|
import MetaError from "../structs/metaerror";
|
||||||
import Toasts from "../ui/toasts";
|
import Toasts from "../ui/toasts";
|
||||||
|
import DiscordModules from "./discordmodules";
|
||||||
|
import Strings from "./strings";
|
||||||
|
|
||||||
|
import AddonEditor from "../ui/misc/addoneditor";
|
||||||
|
import FloatingWindowContainer from "../ui/floating/container";
|
||||||
|
|
||||||
|
const React = DiscordModules.React;
|
||||||
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const Module = require("module").Module;
|
const Module = require("module").Module;
|
||||||
Module.globalPaths.push(path.resolve(require("electron").remote.app.getAppPath(), "node_modules"));
|
Module.globalPaths.push(path.resolve(require("electron").remote.app.getAppPath(), "node_modules"));
|
||||||
|
|
||||||
const splitRegex = /[^\S\r\n]*?\n[^\S\r\n]*?\*[^\S\r\n]?/;
|
const splitRegex = /[^\S\r\n]*?\r?\n[^\S\r\n]*?\*[^\S\r\n]?/;
|
||||||
const escapedAtRegex = /^\\@/;
|
const escapedAtRegex = /^\\@/;
|
||||||
|
|
||||||
const stripBOM = function(fileContent) {
|
const stripBOM = function(fileContent) {
|
||||||
|
@ -28,6 +35,7 @@ export default class AddonManager {
|
||||||
get moduleExtension() {return "";}
|
get moduleExtension() {return "";}
|
||||||
get extension() {return "";}
|
get extension() {return "";}
|
||||||
get addonFolder() {return "";}
|
get addonFolder() {return "";}
|
||||||
|
get language() {return "";}
|
||||||
get prefix() {return "addon";}
|
get prefix() {return "addon";}
|
||||||
get collection() {return "settings";}
|
get collection() {return "settings";}
|
||||||
get category() {return "addons";}
|
get category() {return "addons";}
|
||||||
|
@ -258,4 +266,60 @@ export default class AddonManager {
|
||||||
if (Settings.get(this.collection, this.category, this.id)) this.watchAddons();
|
if (Settings.get(this.collection, this.category, this.id)) this.watchAddons();
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteAddon(idOrFileOrAddon) {
|
||||||
|
const addon = typeof(idOrFileOrAddon) == "string" ? this.addonList.find(c => c.id == idOrFileOrAddon || c.filename == idOrFileOrAddon) : idOrFileOrAddon;
|
||||||
|
return fs.unlinkSync(path.resolve(this.addonFolder, addon.filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
saveAddon(idOrFileOrAddon, content) {
|
||||||
|
const addon = typeof(idOrFileOrAddon) == "string" ? this.addonList.find(c => c.id == idOrFileOrAddon || c.filename == idOrFileOrAddon) : idOrFileOrAddon;
|
||||||
|
return fs.writeFileSync(path.resolve(this.addonFolder, addon.filename), content);
|
||||||
|
}
|
||||||
|
|
||||||
|
editAddon(idOrFileOrAddon, system) {
|
||||||
|
const addon = typeof(idOrFileOrAddon) == "string" ? this.addonList.find(c => c.id == idOrFileOrAddon || c.filename == idOrFileOrAddon) : idOrFileOrAddon;
|
||||||
|
const fullPath = path.resolve(this.addonFolder, addon.filename);
|
||||||
|
if (typeof(system) == "undefined") system = Settings.get("settings", "addons", "editAction") == "system";
|
||||||
|
if (system) return require("electron").shell.openItem(`${fullPath}`);
|
||||||
|
return this.openDetached(addon);
|
||||||
|
}
|
||||||
|
|
||||||
|
openDetached(addon) {
|
||||||
|
const fullPath = path.resolve(this.addonFolder, addon.filename);
|
||||||
|
const content = fs.readFileSync(fullPath).toString();
|
||||||
|
|
||||||
|
const editorRef = React.createRef();
|
||||||
|
const editor = React.createElement(AddonEditor, {
|
||||||
|
id: "bd-floating-editor-" + addon.name,
|
||||||
|
ref: editorRef,
|
||||||
|
content: content,
|
||||||
|
save: this.saveAddon.bind(this, addon),
|
||||||
|
openNative: this.editAddon.bind(this, addon, true),
|
||||||
|
language: this.language
|
||||||
|
});
|
||||||
|
|
||||||
|
FloatingWindowContainer.open({
|
||||||
|
onClose: () => {
|
||||||
|
this.isDetached = false;
|
||||||
|
},
|
||||||
|
onResize: () => {
|
||||||
|
if (!editorRef || !editorRef.current || !editorRef.current.resize) return;
|
||||||
|
editorRef.current.resize();
|
||||||
|
},
|
||||||
|
title: addon.name,
|
||||||
|
id: content.id,
|
||||||
|
className: "floating-addon-window",
|
||||||
|
height: 470,
|
||||||
|
width: 410,
|
||||||
|
center: true,
|
||||||
|
resizable: true,
|
||||||
|
children: editor,
|
||||||
|
confirmClose: () => {
|
||||||
|
if (!editorRef || !editorRef.current) return false;
|
||||||
|
return editorRef.current.hasUnsavedChanges;
|
||||||
|
},
|
||||||
|
confirmationText: Strings.Addons.confirmationText.format({name: addon.name})
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -19,6 +19,7 @@ export default new class PluginManager extends AddonManager {
|
||||||
get extension() {return ".plugin.js";}
|
get extension() {return ".plugin.js";}
|
||||||
get addonFolder() {return path.resolve(Config.dataPath, "plugins");}
|
get addonFolder() {return path.resolve(Config.dataPath, "plugins");}
|
||||||
get prefix() {return "plugin";}
|
get prefix() {return "plugin";}
|
||||||
|
get language() {return "javascript";}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -37,7 +38,11 @@ export default new class PluginManager extends AddonManager {
|
||||||
folder: this.addonFolder,
|
folder: this.addonFolder,
|
||||||
onChange: this.togglePlugin.bind(this),
|
onChange: this.togglePlugin.bind(this),
|
||||||
reload: this.reloadPlugin.bind(this),
|
reload: this.reloadPlugin.bind(this),
|
||||||
refreshList: this.updatePluginList.bind(this)
|
refreshList: this.updatePluginList.bind(this),
|
||||||
|
saveAddon: this.saveAddon.bind(this),
|
||||||
|
editAddon: this.editAddon.bind(this),
|
||||||
|
deleteAddon: this.deleteAddon.bind(this),
|
||||||
|
prefix: this.prefix
|
||||||
})});
|
})});
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ export default new class ThemeManager extends AddonManager {
|
||||||
get extension() {return ".theme.css";}
|
get extension() {return ".theme.css";}
|
||||||
get addonFolder() {return path.resolve(Config.dataPath, "themes");}
|
get addonFolder() {return path.resolve(Config.dataPath, "themes");}
|
||||||
get prefix() {return "theme";}
|
get prefix() {return "theme";}
|
||||||
|
get language() {return "css";}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
const errors = super.initialize();
|
const errors = super.initialize();
|
||||||
|
@ -23,7 +24,11 @@ export default new class ThemeManager extends AddonManager {
|
||||||
folder: this.addonFolder,
|
folder: this.addonFolder,
|
||||||
onChange: this.toggleTheme.bind(this),
|
onChange: this.toggleTheme.bind(this),
|
||||||
reload: this.reloadTheme.bind(this),
|
reload: this.reloadTheme.bind(this),
|
||||||
refreshList: this.updateThemeList.bind(this)
|
refreshList: this.updateThemeList.bind(this),
|
||||||
|
saveAddon: this.saveAddon.bind(this),
|
||||||
|
editAddon: this.editAddon.bind(this),
|
||||||
|
deleteAddon: this.deleteAddon.bind(this),
|
||||||
|
prefix: this.prefix
|
||||||
})});
|
})});
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {React, Strings} from "modules";
|
||||||
|
|
||||||
import Screen from "../../structs/screen";
|
import Screen from "../../structs/screen";
|
||||||
import CloseButton from "../icons/close";
|
import CloseButton from "../icons/close";
|
||||||
|
import MaximizeIcon from "../icons/fullscreen";
|
||||||
import Modals from "../modals";
|
import Modals from "../modals";
|
||||||
|
|
||||||
export default class FloatingWindow extends React.Component {
|
export default class FloatingWindow extends React.Component {
|
||||||
|
@ -18,6 +19,7 @@ export default class FloatingWindow extends React.Component {
|
||||||
this.window = React.createRef();
|
this.window = React.createRef();
|
||||||
|
|
||||||
this.close = this.close.bind(this);
|
this.close = this.close.bind(this);
|
||||||
|
this.maximize = this.maximize.bind(this);
|
||||||
this.onDrag = this.onDrag.bind(this);
|
this.onDrag = this.onDrag.bind(this);
|
||||||
this.onDragStart = this.onDragStart.bind(this);
|
this.onDragStart = this.onDragStart.bind(this);
|
||||||
this.onDragStop = this.onDragStop.bind(this);
|
this.onDragStop = this.onDragStop.bind(this);
|
||||||
|
@ -55,7 +57,6 @@ export default class FloatingWindow extends React.Component {
|
||||||
|
|
||||||
onDrag(e) {
|
onDrag(e) {
|
||||||
const div = this.window.current;
|
const div = this.window.current;
|
||||||
div.style.position = "fixed";
|
|
||||||
div.style.top = (e.clientY - this.offY) + "px";
|
div.style.top = (e.clientY - this.offY) + "px";
|
||||||
div.style.left = (e.clientX - this.offX) + "px";
|
div.style.left = (e.clientX - this.offX) + "px";
|
||||||
}
|
}
|
||||||
|
@ -75,6 +76,9 @@ export default class FloatingWindow extends React.Component {
|
||||||
<div className="floating-window-titlebar" ref={this.titlebar}>
|
<div className="floating-window-titlebar" ref={this.titlebar}>
|
||||||
<span className="title">{this.props.title}</span>
|
<span className="title">{this.props.title}</span>
|
||||||
<div className="floating-window-buttons">
|
<div className="floating-window-buttons">
|
||||||
|
<div className="button maximize-button" onClick={this.maximize}>
|
||||||
|
<MaximizeIcon size="18px" />
|
||||||
|
</div>
|
||||||
<div className="button close-button" onClick={this.close}>
|
<div className="button close-button" onClick={this.close}>
|
||||||
<CloseButton />
|
<CloseButton />
|
||||||
</div>
|
</div>
|
||||||
|
@ -97,11 +101,19 @@ export default class FloatingWindow extends React.Component {
|
||||||
if (this.props.close && shouldClose) this.props.close();
|
if (this.props.close && shouldClose) this.props.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maximize() {
|
||||||
|
this.window.current.style.width = "100%";
|
||||||
|
this.window.current.style.height = "100%";
|
||||||
|
this.window.current.style.top = "20px";
|
||||||
|
this.window.current.style.left = "0px";
|
||||||
|
if (this.props.onResize) this.props.onResize();
|
||||||
|
}
|
||||||
|
|
||||||
confirmClose() {
|
confirmClose() {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
Modals.showConfirmationModal(Strings.Modals.confirmClose, this.props.confirmationText, {
|
Modals.showConfirmationModal(Strings.Modals.confirmAction, this.props.confirmationText, {
|
||||||
danger: true,
|
danger: true,
|
||||||
confirmText: "Close",
|
confirmText: Strings.Modals.close,
|
||||||
onConfirm: () => {resolve(true);},
|
onConfirm: () => {resolve(true);},
|
||||||
onCancel: () => {resolve(false);}
|
onCancel: () => {resolve(false);}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {React} from "modules";
|
||||||
|
|
||||||
|
export default class Delete extends React.Component {
|
||||||
|
render() {
|
||||||
|
const size = this.props.size || "24px";
|
||||||
|
return <svg className={this.props.className || ""} fill="#FFFFFF" viewBox="0 0 24 24" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||||
|
<path fill="none" d="M0 0h24v24H0V0z"/><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z"/>
|
||||||
|
<path fill="none" d="M0 0h24v24H0z"/>
|
||||||
|
</svg>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ import {React} from "modules";
|
||||||
export default class Edit extends React.Component {
|
export default class Edit extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const size = this.props.size || "24px";
|
const size = this.props.size || "24px";
|
||||||
return <svg viewBox="0 0 24 24" style={{width: size, height: size}}>
|
return <svg viewBox="0 0 24 24" fill="#FFFFFF" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||||
<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" />
|
<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" />
|
||||||
<path d="M0 0h24v24H0z" fill="none" />
|
<path d="M0 0h24v24H0z" fill="none" />
|
||||||
</svg>;
|
</svg>;
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {React} from "modules";
|
||||||
|
|
||||||
|
export default class FullScreen extends React.Component {
|
||||||
|
render() {
|
||||||
|
const size = this.props.size || "24px";
|
||||||
|
return <svg className={this.props.className || ""} fill="#FFFFFF" viewBox="0 0 24 24" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||||
|
<path fill="none" d="M0 0h24v24H0V0z"/>
|
||||||
|
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>
|
||||||
|
</svg>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
import {React, Strings} from "modules";
|
||||||
|
|
||||||
|
import Editor from "../customcss/editor";
|
||||||
|
import Save from "../icons/save";
|
||||||
|
import Edit from "../icons/edit";
|
||||||
|
import Cog from "../icons/cog";
|
||||||
|
|
||||||
|
export default class AddonEditor extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.hasUnsavedChanges = false;
|
||||||
|
this.onChange = this.onChange.bind(this);
|
||||||
|
this.save = this.save.bind(this);
|
||||||
|
this.openNative = this.openNative.bind(this);
|
||||||
|
this.update = this.update.bind(this);
|
||||||
|
|
||||||
|
this.controls = [
|
||||||
|
{label: React.createElement(Save, {size: "18px"}), tooltip: Strings.CustomCSS.save, onClick: this.save},
|
||||||
|
{label: React.createElement(Edit, {size: "18px"}), tooltip: Strings.CustomCSS.openNative, onClick: this.openNative},
|
||||||
|
{label: React.createElement(Cog, {size: "18px"}), tooltip: Strings.CustomCSS.settings, onClick: "showSettings"}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEditor(newCSS) {
|
||||||
|
if (!this.editor) return;
|
||||||
|
this.editor.value = newCSS;
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {return this.editor.session.getValue();}
|
||||||
|
set value(newValue) {
|
||||||
|
this.editor.setValue(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
showSettings() {return this.editor.keyBinding.$defaultHandler.commands.showSettingsMenu.exec(this.editor);}
|
||||||
|
resize() {return this.editor.resize();}
|
||||||
|
|
||||||
|
setEditorRef(editor) {
|
||||||
|
this.editor = editor;
|
||||||
|
if (this.props.editorRef && typeof(this.props.editorRef.current) !== "undefined") this.props.editorRef.current = editor;
|
||||||
|
else if (this.props.editorRef) this.props.editorRef = editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <Editor ref={this.setEditorRef.bind(this)} language={this.props.language} id={this.props.id || "bd-addon-editor"} controls={this.controls} value={this.props.content} onChange={this.onChange} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange() {
|
||||||
|
this.hasUnsavedChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
save(event, content) {
|
||||||
|
this.hasUnsavedChanges = false;
|
||||||
|
if (this.props.save) this.props.save(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
openNative() {
|
||||||
|
if (this.props.openNative) this.props.openNative();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
import {React, Logger, Strings} from "modules";
|
import {React, Logger, Strings} from "modules";
|
||||||
import CloseButton from "../icons/close";
|
import CloseButton from "../icons/close";
|
||||||
import ReloadIcon from "../icons/reload";
|
import ReloadIcon from "../icons/reload";
|
||||||
|
import EditIcon from "../icons/edit";
|
||||||
|
import DeleteIcon from "../icons/delete";
|
||||||
import Switch from "./components/switch";
|
import Switch from "./components/switch";
|
||||||
|
|
||||||
export default class AddonCard extends React.Component {
|
export default class AddonCard extends React.Component {
|
||||||
|
@ -119,7 +121,9 @@ export default class AddonCard extends React.Component {
|
||||||
<div className="bd-addon-header">
|
<div className="bd-addon-header">
|
||||||
<span className="bd-title">{this.buildTitle(name, version, author)}</span>
|
<span className="bd-title">{this.buildTitle(name, version, author)}</span>
|
||||||
<div className="bd-controls">
|
<div className="bd-controls">
|
||||||
{this.props.showReloadIcon && <ReloadIcon className="bd-reload bd-reload-card" onClick={this.reload} />}
|
{this.props.editAddon && <div className="bd-addon-button" onClick={this.props.editAddon}><EditIcon /></div>}
|
||||||
|
{this.props.deleteAddon && <div className="bd-addon-button" onClick={this.props.deleteAddon}><DeleteIcon /></div>}
|
||||||
|
{this.props.showReloadIcon && <div className="bd-addon-button" onClick={this.reload}><ReloadIcon className="bd-reload bd-reload-card" /></div>}
|
||||||
<Switch checked={this.props.enabled} onChange={this.onChange} />
|
<Switch checked={this.props.enabled} onChange={this.onChange} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {React, Settings, Strings} from "modules";
|
import {React, Settings, Strings, Events} from "modules";
|
||||||
|
|
||||||
|
import Modals from "../modals";
|
||||||
import SettingsTitle from "./title";
|
import SettingsTitle from "./title";
|
||||||
import ReloadIcon from "../icons/reload";
|
import ReloadIcon from "../icons/reload";
|
||||||
import AddonCard from "./addoncard";
|
import AddonCard from "./addoncard";
|
||||||
|
@ -14,6 +15,21 @@ export default class AddonList extends React.Component {
|
||||||
this.sort = this.sort.bind(this);
|
this.sort = this.sort.bind(this);
|
||||||
this.reverse = this.reverse.bind(this);
|
this.reverse = this.reverse.bind(this);
|
||||||
this.search = this.search.bind(this);
|
this.search = this.search.bind(this);
|
||||||
|
this.update = this.update.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
Events.on(`${this.props.prefix}-loaded`, this.update);
|
||||||
|
Events.on(`${this.props.prefix}-unloaded`, this.update);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
Events.off(`${this.props.prefix}-loaded`, this.update);
|
||||||
|
Events.off(`${this.props.prefix}-unloaded`, this.update);
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
reload() {
|
reload() {
|
||||||
|
@ -89,9 +105,31 @@ export default class AddonList extends React.Component {
|
||||||
}
|
}
|
||||||
const hasSettings = addon.type && typeof(addon.plugin.getSettingsPanel) === "function";
|
const hasSettings = addon.type && typeof(addon.plugin.getSettingsPanel) === "function";
|
||||||
const getSettings = hasSettings && addon.plugin.getSettingsPanel.bind(addon.plugin);
|
const getSettings = hasSettings && addon.plugin.getSettingsPanel.bind(addon.plugin);
|
||||||
return <AddonCard showReloadIcon={showReloadIcon} key={addon.id} enabled={addonState[addon.id]} addon={addon} onChange={onChange} reload={reload} hasSettings={hasSettings} getSettingsPanel={getSettings} />;
|
return <AddonCard editAddon={this.editAddon.bind(this, addon.id)} deleteAddon={this.deleteAddon.bind(this, addon.id)} showReloadIcon={showReloadIcon} key={addon.id} enabled={addonState[addon.id]} addon={addon} onChange={onChange} reload={reload} hasSettings={hasSettings} getSettingsPanel={getSettings} />;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editAddon(id) {
|
||||||
|
if (this.props.editAddon) this.props.editAddon(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteAddon(id) {
|
||||||
|
const addon = this.props.addonList.find(a => a.id == id);
|
||||||
|
const shouldDelete = await this.confirmDelete(addon);
|
||||||
|
if (!shouldDelete) return;
|
||||||
|
if (this.props.deleteAddon) this.props.deleteAddon(addon);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDelete(addon) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
Modals.showConfirmationModal(Strings.Modals.confirmAction, Strings.Addons.confirmDelete.format({name: addon.name}), {
|
||||||
|
danger: true,
|
||||||
|
confirmText: Strings.Addons.deleteAddon,
|
||||||
|
onConfirm: () => {resolve(true);},
|
||||||
|
onCancel: () => {resolve(false);}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue