RichPresence fix

Light theme semi-fix
LightcordApi update
This commit is contained in:
Jean Ouina 2020-07-17 14:54:49 +02:00
parent c96f94b314
commit 7ec315a11f
29 changed files with 1490 additions and 986 deletions

View File

@ -1,21 +1,21 @@
MIT License
Copyright (c) 2015-present Jiiks | 2017-present Zack Rauen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
MIT License
Copyright (c) 2015-present Jiiks | 2017-present Zack Rauen | 2020-present Lightcord
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -952,20 +952,13 @@ body .ace_closeButton:active {
justify-content: space-between;
}
.theme-dark .ui-tab-bar-header .bd-changelog-button {
color: #72767d;
}
.theme-light .ui-tab-bar-header .bd-changelog-button {
color: #b9bbbe;
}
.ui-tab-bar-header .bd-icon {
cursor: pointer;
fill: #72767d;
fill: var(--text-muted);
}
.ui-tab-bar-header .bd-icon:hover {
fill: #fff;
fill: var(--text-normal);
}
.ui-tab-bar-separator {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,11 @@
/* BDEvents */
const EventEmitter = require("events");
export default new class BDEvents extends EventEmitter {
constructor(){
super()
window.Lightcord.BetterDiscord.BDEvents = this
}
dispatch(eventName, ...args) {this.emit(eventName, ...args);}
off(eventName, eventAction) {this.removeListener(eventName, eventAction);}
/* BDEvents */
const EventEmitter = require("events");
export default new class BDEvents extends EventEmitter {
constructor(){
super()
window.Lightcord.BetterDiscord.BDEvents = this
}
dispatch(eventName, ...args) {this.emit(eventName, ...args);}
off(eventName, eventAction) {this.removeListener(eventName, eventAction);}
};

View File

@ -1,149 +1,149 @@
import {bdthemeErrors, themeCookie, settingsCookie, bdthemes} from "../0globals";
import ContentManager from "./contentManager";
import DataStore from "./dataStore";
import BDEvents from "./bdEvents";
import Utils from "./utils";
import DOM from "./domtools";
class ThemeModule {
constructor(){
window.Lightcord.BetterDiscord.ThemeModule = this
}
get folder() {return ContentManager.themesFolder;}
}
ThemeModule.prototype.loadThemes = async function () {
this.loadThemeData();
bdthemeErrors.splice(0, 0, ...(await ContentManager.loadThemes()));
const themes = Object.keys(bdthemes);
for (let i = 0; i < themes.length; i++) {
const theme = bdthemes[themes[i]];
if (!themeCookie[theme.name]) themeCookie[theme.name] = false;
if (themeCookie[theme.name]) DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
await new Promise((resolve) => setTimeout(resolve, 10))
}
for (const theme in themeCookie) {
if (!bdthemes[theme]) delete themeCookie[theme];
}
this.saveThemeData();
// if (settingsCookie["fork-ps-5"]) ContentManager.watchContent("theme");
};
ThemeModule.prototype.enableTheme = function(name, reload = false) {
themeCookie[name] = true;
this.saveThemeData();
const theme = bdthemes[name];
DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been applied.`);
};
ThemeModule.prototype.enable = function (name, reload = false) {
return this.enableTheme(name, reload);
};
ThemeModule.prototype.disableTheme = function(name, reload = false) {
themeCookie[name] = false;
this.saveThemeData();
const theme = bdthemes[name];
DOM.removeStyle(DOM.escapeID(theme.id));
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been disabled.`);
};
ThemeModule.prototype.disable = function (name, reload = false) {
return this.disableTheme(name, reload);
};
ThemeModule.prototype.toggleTheme = function(theme) {
if (themeCookie[theme]) this.disableTheme(theme);
else this.enableTheme(theme);
};
ThemeModule.prototype.toggle = function (name, reload = false) {
return this.toggleTheme(name, reload);
};
ThemeModule.prototype.loadTheme = async function(filename) {
const error = await ContentManager.loadContent(filename, "theme");
if (error) {
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]});
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${filename} could not be loaded. It may not have been loaded.`, {type: "error"});
return Utils.err("ContentManager", `${filename} could not be loaded.`, error);
}
const theme = Object.values(bdthemes).find(p => p.filename == filename);
Utils.log("ContentManager", `${theme.name} v${theme.version} was loaded.`);
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme.name} v${theme.version} was loaded.`, {type: "success"});
BDEvents.dispatch("theme-loaded", theme.name);
};
ThemeModule.prototype.unloadTheme = function(filenameOrName) {
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
if (!bdtheme) return;
const theme = bdtheme.name;
if (themeCookie[theme]) this.disableTheme(theme, true);
const error = ContentManager.unloadContent(bdthemes[theme].filename, "theme");
delete bdthemes[theme];
if (error) {
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]});
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be unloaded. It may have not been loaded yet.`, {type: "error"});
return Utils.err("ContentManager", `${theme} could not be unloaded. It may have not been loaded yet.`, error);
}
Utils.log("ContentManager", `${theme} was unloaded.`);
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} was unloaded.`, {type: "success"});
BDEvents.dispatch("theme-unloaded", theme);
};
ThemeModule.prototype.delete = function(filenameOrName) {
const bdplugin = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
if (!bdplugin) return;
this.unloadTheme(bdplugin.filename);
const fullPath = require("path").resolve(ContentManager.pluginsFolder, bdplugin.filename);
require("fs").unlinkSync(fullPath);
};
ThemeModule.prototype.reloadTheme = async function(filenameOrName) {
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
if (!bdtheme) return this.loadTheme(filenameOrName);
const theme = bdtheme.name;
const error = await ContentManager.reloadContent(bdthemes[theme].filename, "theme");
if (themeCookie[theme]) this.disableTheme(theme, true), this.enableTheme(theme, true);
if (error) {
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]});
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be reloaded.`, {type: "error"});
return Utils.err("ContentManager", `${theme} could not be reloaded.`, error);
}
Utils.log("ContentManager", `${theme} v${bdthemes[theme].version} was reloaded.`);
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} v${bdthemes[theme].version} was reloaded.`, {type: "success"});
BDEvents.dispatch("theme-reloaded", theme);
};
ThemeModule.prototype.reload = function(name) {
return this.reloadTheme(name);
};
ThemeModule.prototype.edit = function(filenameOrName) {
const bdplugin = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
if (!bdplugin) return;
const fullPath = require("path").resolve(ContentManager.themesFolder, bdplugin.filename);
require("electron").shell.openItem(`${fullPath}`);
};
ThemeModule.prototype.updateThemeList = function() {
const results = ContentManager.loadNewContent("theme");
for (const filename of results.added) this.loadTheme(filename);
for (const name of results.removed) this.unloadTheme(name);
};
ThemeModule.prototype.loadThemeData = function() {
const saved = DataStore.getSettingGroup("themes");
if (saved) {
Object.assign(themeCookie, saved);
}
};
ThemeModule.prototype.saveThemeData = function () {
DataStore.setSettingGroup("themes", themeCookie);
};
import {bdthemeErrors, themeCookie, settingsCookie, bdthemes} from "../0globals";
import ContentManager from "./contentManager";
import DataStore from "./dataStore";
import BDEvents from "./bdEvents";
import Utils from "./utils";
import DOM from "./domtools";
class ThemeModule {
constructor(){
window.Lightcord.BetterDiscord.ThemeModule = this
}
get folder() {return ContentManager.themesFolder;}
}
ThemeModule.prototype.loadThemes = async function () {
this.loadThemeData();
bdthemeErrors.splice(0, 0, ...(await ContentManager.loadThemes()));
const themes = Object.keys(bdthemes);
for (let i = 0; i < themes.length; i++) {
const theme = bdthemes[themes[i]];
if (!themeCookie[theme.name]) themeCookie[theme.name] = false;
if (themeCookie[theme.name]) DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
await new Promise((resolve) => setTimeout(resolve, 10))
}
for (const theme in themeCookie) {
if (!bdthemes[theme]) delete themeCookie[theme];
}
this.saveThemeData();
// if (settingsCookie["fork-ps-5"]) ContentManager.watchContent("theme");
};
ThemeModule.prototype.enableTheme = function(name, reload = false) {
themeCookie[name] = true;
this.saveThemeData();
const theme = bdthemes[name];
DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been applied.`);
};
ThemeModule.prototype.enable = function (name, reload = false) {
return this.enableTheme(name, reload);
};
ThemeModule.prototype.disableTheme = function(name, reload = false) {
themeCookie[name] = false;
this.saveThemeData();
const theme = bdthemes[name];
DOM.removeStyle(DOM.escapeID(theme.id));
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been disabled.`);
};
ThemeModule.prototype.disable = function (name, reload = false) {
return this.disableTheme(name, reload);
};
ThemeModule.prototype.toggleTheme = function(theme) {
if (themeCookie[theme]) this.disableTheme(theme);
else this.enableTheme(theme);
};
ThemeModule.prototype.toggle = function (name, reload = false) {
return this.toggleTheme(name, reload);
};
ThemeModule.prototype.loadTheme = async function(filename) {
const error = await ContentManager.loadContent(filename, "theme");
if (error) {
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]});
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${filename} could not be loaded. It may not have been loaded.`, {type: "error"});
return Utils.err("ContentManager", `${filename} could not be loaded.`, error);
}
const theme = Object.values(bdthemes).find(p => p.filename == filename);
Utils.log("ContentManager", `${theme.name} v${theme.version} was loaded.`);
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme.name} v${theme.version} was loaded.`, {type: "success"});
BDEvents.dispatch("theme-loaded", theme.name);
};
ThemeModule.prototype.unloadTheme = function(filenameOrName) {
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
if (!bdtheme) return;
const theme = bdtheme.name;
if (themeCookie[theme]) this.disableTheme(theme, true);
const error = ContentManager.unloadContent(bdthemes[theme].filename, "theme");
delete bdthemes[theme];
if (error) {
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]});
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be unloaded. It may have not been loaded yet.`, {type: "error"});
return Utils.err("ContentManager", `${theme} could not be unloaded. It may have not been loaded yet.`, error);
}
Utils.log("ContentManager", `${theme} was unloaded.`);
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} was unloaded.`, {type: "success"});
BDEvents.dispatch("theme-unloaded", theme);
};
ThemeModule.prototype.delete = function(filenameOrName) {
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
if (!bdtheme) return;
this.unloadTheme(bdtheme.filename);
const fullPath = require("path").resolve(ContentManager.themesFolder, bdtheme.filename);
require("fs").unlinkSync(fullPath);
};
ThemeModule.prototype.reloadTheme = async function(filenameOrName) {
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
if (!bdtheme) return this.loadTheme(filenameOrName);
const theme = bdtheme.name;
const error = await ContentManager.reloadContent(bdthemes[theme].filename, "theme");
if (themeCookie[theme]) this.disableTheme(theme, true), this.enableTheme(theme, true);
if (error) {
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]});
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be reloaded.`, {type: "error"});
return Utils.err("ContentManager", `${theme} could not be reloaded.`, error);
}
Utils.log("ContentManager", `${theme} v${bdthemes[theme].version} was reloaded.`);
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} v${bdthemes[theme].version} was reloaded.`, {type: "success"});
BDEvents.dispatch("theme-reloaded", theme);
};
ThemeModule.prototype.reload = function(name) {
return this.reloadTheme(name);
};
ThemeModule.prototype.edit = function(filenameOrName) {
const bdplugin = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
if (!bdplugin) return;
const fullPath = require("path").resolve(ContentManager.themesFolder, bdplugin.filename);
require("electron").shell.openItem(`${fullPath}`);
};
ThemeModule.prototype.updateThemeList = function() {
const results = ContentManager.loadNewContent("theme");
for (const filename of results.added) this.loadTheme(filename);
for (const name of results.removed) this.unloadTheme(name);
};
ThemeModule.prototype.loadThemeData = function() {
const saved = DataStore.getSettingGroup("themes");
if (saved) {
Object.assign(themeCookie, saved);
}
};
ThemeModule.prototype.saveThemeData = function () {
DataStore.setSettingGroup("themes", themeCookie);
};
export default new ThemeModule();

View File

@ -1,9 +1,7 @@
import ErrorBoundary from "./errorBoundary";
import ContentColumn from "./contentColumn";
import Tools from "./tools";
import ReloadIcon from "./reloadIcon";
import AddonCard from "./addoncard";
import Scroller from "./scroller";
import Dropdown from "./components/dropdown";
import Search from "./components/search";
@ -15,7 +13,8 @@ import themeModule from "../modules/themeModule";
import WebpackModules from "../modules/webpackModules";
import BdApi from "../modules/bdApi";
import Utils from "../modules/utils";
import tooltipWrap from "./tooltipWrap";
import TooltipWrap from "./tooltipWrap";
import bdEvents from "../modules/bdEvents";
const Tooltip = WebpackModules.findByDisplayName("Tooltip");
@ -32,6 +31,25 @@ export default class CardList extends BDV2.reactComponent {
this.sort = this.sort.bind(this);
this.reverse = this.reverse.bind(this);
this.search = this.search.bind(this);
this.onAddonChanges = function(){
this.forceUpdate()
}
this.onAddonChanges = this.onAddonChanges.bind(this)
}
componentDidMount(){
const type = (this.isPlugins ? "plugin" : "theme") + "-"
bdEvents.on(`${type}loaded`, this.onAddonChanges)
bdEvents.on(`${type}unloaded`, this.onAddonChanges)
bdEvents.on(`${type}reloaded`, this.onAddonChanges)
}
componentWillUnmount(){
const type = (this.isPlugins ? "plugin" : "theme") + "-"
bdEvents.off(`${type}loaded`, this.onAddonChanges)
bdEvents.off(`${type}unloaded`, this.onAddonChanges)
bdEvents.off(`${type}reloaded`, this.onAddonChanges)
}
openFolder() {
@ -154,7 +172,7 @@ export default class CardList extends BDV2.reactComponent {
if(typeof window.PluginUpdates.checkAll !== "function")return null
if(!this.isPlugins)return null
return <tooltipWrap text="Checks for updates of plugins that support this feature. Right-click for a list.">
return <TooltipWrap text="Checks for updates of plugins that support this feature. Right-click for a list.">
<span style={{marginLeft: "10px"}}>
<Lightcord.Api.Components.inputs.Button color="brand" look="filled" size="min" hoverColor="default" onClick={() => {
try{
@ -174,7 +192,7 @@ export default class CardList extends BDV2.reactComponent {
Check for Updates
</Lightcord.Api.Components.inputs.Button>
</span>
</tooltipWrap>
</TooltipWrap>
}
render() {

View File

@ -1,13 +1,13 @@
import BDV2 from "../../modules/v2";
const React = BDV2.React;
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>;
}
import BDV2 from "../../modules/v2";
const React = BDV2.React;
export default class Delete extends React.Component {
render() {
const size = this.props.size || "24px";
return <svg className={this.props.className || ""} fill="var(--text-normal)" 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>;
}
}

View File

@ -1,12 +1,12 @@
import BDV2 from "../../modules/v2";
const React = BDV2.React;
export default class DownArrow extends React.Component {
render() {
const size = this.props.size || "16px";
return <svg className={this.props.className || ""} fill="#FFFFFF" viewBox="0 0 24 24" style={{width: size, height: size}}>
<path d="M8.12 9.29L12 13.17l3.88-3.88c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41l-4.59 4.59c-.39.39-1.02.39-1.41 0L6.7 10.7c-.39-.39-.39-1.02 0-1.41.39-.38 1.03-.39 1.42 0z"/>
</svg>;
}
import BDV2 from "../../modules/v2";
const React = BDV2.React;
export default class DownArrow extends React.Component {
render() {
const size = this.props.size || "16px";
return <svg className={this.props.className || ""} fill="var(--text-normal)" viewBox="0 0 24 24" style={{width: size, height: size}}>
<path d="M8.12 9.29L12 13.17l3.88-3.88c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41l-4.59 4.59c-.39.39-1.02.39-1.41 0L6.7 10.7c-.39-.39-.39-1.02 0-1.41.39-.38 1.03-.39 1.42 0z"/>
</svg>;
}
}

View File

@ -1,13 +1,13 @@
import BDV2 from "../../modules/v2";
const React = BDV2.React;
export default class Edit extends React.Component {
render() {
const size = this.props.size || "24px";
return <svg className={this.props.className || ""} 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="M0 0h24v24H0z" fill="none" />
</svg>;
}
import BDV2 from "../../modules/v2";
const React = BDV2.React;
export default class Edit extends React.Component {
render() {
const size = this.props.size || "24px";
return <svg className={this.props.className || ""} viewBox="0 0 24 24" fill="var(--text-normal)" 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="M0 0h24v24H0z" fill="none" />
</svg>;
}
}

View File

@ -1,18 +1,18 @@
{/* <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18px" height="18px">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/>
</svg> */}
import BDV2 from "../../modules/v2";
const React = BDV2.React;
export default class History extends React.Component {
render() {
const size = this.props.size || "18px";
return <svg viewBox="0 0 24 24" fill="#FFFFFF" className={this.props.className || ""} style={{width: size, height: size}} onClick={this.props.onClick}>
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/>
</svg>;
}
{/* <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18px" height="18px">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/>
</svg> */}
import BDV2 from "../../modules/v2";
const React = BDV2.React;
export default class History extends React.Component {
render() {
const size = this.props.size || "18px";
return <svg viewBox="0 0 24 24" fill="var(--text-normal)" className={this.props.className || ""} style={{width: size, height: size}} onClick={this.props.onClick}>
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/>
</svg>;
}
}

View File

@ -1,13 +1,13 @@
import BDV2 from "../../modules/v2";
const React = BDV2.React;
export default class Search extends React.Component {
render() {
const size = this.props.size || "16px";
return <svg className={this.props.className || ""} fill="#FFFFFF" viewBox="0 0 24 24" style={{width: size, height: size}}>
<path fill="none" d="M0 0h24v24H0V0z"/>
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</svg>;
}
import BDV2 from "../../modules/v2";
const React = BDV2.React;
export default class Search extends React.Component {
render() {
const size = this.props.size || "16px";
return <svg className={this.props.className || ""} fill="var(--text-normal)" viewBox="0 0 24 24" style={{width: size, height: size}}>
<path fill="none" d="M0 0h24v24H0V0z"/>
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</svg>;
}
}

View File

@ -7,7 +7,8 @@ import CustomRichPresence from "../modules/CustomRichPresence"
import timestampRender from "./timestampRender"
import { remote } from "electron";
import MarginTop from "./margintop";
import Utils from "../modules/utils";
import { uuidv4 } from "../modules/distant";
const React = BDV2.React;
@ -496,16 +497,12 @@ class RpcPreview extends React.Component {
}
render(){
let preview = new this.preview({
preview: this
})
preview.setState(this.state.rpc)
return (<div className="lc-tabWrapper">
<div className="lc-tabnav" style={{flex: "0 1 auto"}}>
<Tab preview={this} title="Full Profile" id="profile"/>
<Tab preview={this} title="User Popout" id="popout"/>
</div>
{preview.render()}
<PresenceErrorCatcher preview={this.preview} state={this.state.rpc} props={{preview: this}} key={this.state.active} />
</div>)
}
@ -550,6 +547,204 @@ class Tab extends React.Component {
}
}
let emptyClasses
class PresenceErrorCatcher extends React.Component {
componentDidCatch(err, errInfo){
console.error(err, errInfo)
this.setState({
error: true
})
}
render(){
if(!this.state){
this.state = {
error: false
}
}
if(!this.state.error){
try{
const preview = new this.props.preview(this.props.props)
preview.setState(this.props.state)
return preview.render()
}catch(err){
console.error(err)
this.state.error = true
return this.render()
}
}else{
emptyClasses = emptyClasses || BDV2.WebpackModules.find(e => e.emptyStateImage)
if(!emptyClasses){
Utils.showToast("An error occured. Please check the console for more informations.")
return null
}
return <div style={{
margin: "20px"
}}>
<div style={{
backgroundColor: "var(--background-primary)",
padding: "30px 30px",
borderRadius: "8px"
}} className="lc-tab-box-shadow">
<div className={emptyClasses.emptyStateImage} style={{
marginTop: "20px"
}}>
</div>
<div className={emptyClasses.emptyStateHeader}>An error occured</div>
<p className={emptyClasses.emptyStateSubtext}>
Please check the console for more informations. Join our ­
<a className={`${BDV2.anchorClasses.anchor} ${BDV2.anchorClasses.anchorUnderlineOnHover}`} role="button" tabindex={0} onClick={() => {
BDV2.joinLC()
}}>
support server
</a>
­ for help.
</p>
</div>
</div>
}
}
}
let popoutModules
let UserPopoutComponent
let PopoutProps
class Popout extends React.Component {
get modules(){
return popoutModules || (popoutModules = [
BDV2.WebpackModules.find(e => e.default && e.default.displayName === "FluxContainer(ForwardRef(SubscribeGuildMembersContainer(UserPopout)))"),
BDV2.WebpackModules.find(e => e.default && e.default.getCurrentUser)
])
}
render(){
let [
UserPopout,
userModule
] = this.modules
const user = userModule.default.getCurrentUser()
if(!UserPopoutComponent){
if(!UserPopout)throw new Error(`Couldn't find the UserPopout component.`)
const render1 = new UserPopout.default({userId: user.id, guildId: null, channelId: null, disableUserProfileLink: true}).render()
PopoutProps = render1.props
const render2 = render1.type.render(PopoutProps, null)
const render3 = new render2.type(render2.props).render()
UserPopoutComponent = render3.type
}
if(!UserPopoutComponent)throw new Error(`Couldn't find the UserPopoutComponent component.`)
let data = Object.assign({}, defaultRPC, this.props.preview.props.settings.state.data)
const activity = (function(){
if(!this.game)return null
let game = {
name: this.game.name || defaultRPC.name,
application_id: this.game.application_id || defaultRPC.application_id,
details: this.game.details || undefined,
state: this.game.state || undefined,
timestamps: this.game["timestamps.start"] ? {
start: this.game["timestamps.start"]
} : undefined,
assets: this.game["assets.large"] ? {
large_image: this.game["assets.large"],
small_image: this.game["assets.small"] || undefined
} : undefined,
type: 0
}
return game
}).call({
game: data
})
PopoutProps = new UserPopout.default({userId: user.id, guildId: null, channelId: null, disableUserProfileLink: true}).render().props
const popout = new UserPopoutComponent(Object.assign({}, PopoutProps, {
activity: activity
})).render().props.children // bypass tracking
// remove the stop propagation shit.
const container = <div {...window.Lightcord.Api._.excludeProperties(popout.props, ["onClick", "onContextMenu"])} />
return <div className="lc-userPopout lc-tab-box-shadow">
{container}
</div>
}
}
let profileModules
let UserProfileComponent
let ProfileProps
let connectedProfileStore
class Profile extends React.Component {
get modules(){
return profileModules || (profileModules = [
BDV2.WebpackModules.find(e => e.default && e.default.displayName === "UserProfile"),
BDV2.WebpackModules.find(e => e.default && e.default.getCurrentUser)
])
}
render(){
let [
UserProfile,
userModule
] = this.modules
const user = userModule.default.getCurrentUser()
if(!UserProfileComponent){
const render1 = new UserProfile.default({
user: user
}).render()
connectedProfileStore = render1.type
const render2 = new render1.type(render1.props).render()
const render3 = render2.type.render(render2.props, null)
const render4 = new render3.type(render3.props).render()
UserProfileComponent = render4.type
}
if(!UserProfileComponent)throw new Error(`Couldn't find the UserProfileComponent component.`)
let data = Object.assign({}, defaultRPC, this.props.preview.props.settings.state.data)
const activity = (function(){
if(!this.game)return null
let game = {
name: this.game.name || defaultRPC.name,
application_id: this.game.application_id || defaultRPC.application_id,
details: this.game.details || undefined,
state: this.game.state || undefined,
timestamps: this.game["timestamps.start"] ? {
start: this.game["timestamps.start"]
} : undefined,
assets: this.game["assets.large"] ? {
large_image: this.game["assets.large"],
small_image: this.game["assets.small"] || undefined
} : undefined,
type: 0
}
return game
}).call({
game: data
})
ProfileProps = new connectedProfileStore({
user: user,
close: () => {}
}).render().props
const profile = new UserProfileComponent(Object.assign({}, ProfileProps, {
activity: activity
})).render().props.children // bypass tracking
console.log(profile)
profile.props.style = {
width: "auto"
}
return <div className="lc-tab lc-tab-box-shadow">
{profile}
</div>
}
}
/*
let popoutModule
class Popout extends React.Component { // TODO: Probably use internal Components instead of making it from scratch.
get modules(){
@ -979,4 +1174,4 @@ class Timestamp extends React.Component {
{this.props.message}
</div>
}
}
}*/

View File

@ -1,23 +1,23 @@
import BDV2 from "../modules/v2";
import Tooltip from "./tooltip";
export default class extends React.Component {
constructor(props) {
super(props);
}
async componentDidMount() {
const {style = "black", side = "top", text = ""} = this.props;
this.node = BDV2.reactDom.findDOMNode(this);
this.tooltip = new Tooltip(this.node, text, {style, side});
}
componentWillUnmount() {
this.tooltip.hide();
delete this.tooltip;
}
render() {
return this.props.children;
}
import BDV2 from "../modules/v2";
import Tooltip from "./tooltip";
export default class extends React.Component {
constructor(props) {
super(props);
}
async componentDidMount() {
const {style = "black", side = "top", text = ""} = this.props;
this.node = BDV2.reactDom.findDOMNode(this);
this.tooltip = new Tooltip(this.node, text, {style, side});
}
componentWillUnmount() {
this.tooltip.hide();
delete this.tooltip;
}
render() {
return this.props.children;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -176,7 +176,7 @@ export default class ColorPicker extends React.PureComponent<ColorPickerProps, {
static help = {
info: "To convert hex colors to decimal, you can do `Lightcord.Api.Utils.HexColorToDecimal('#yourcolor')` and go back with `Lightcord.Api.Utils.DecimalColorToHex(7506394)`",
warn: "The component may not appear instantly. The component need to be loaded, so you could experience 50-300ms loading time depending on your internet connection."
warn: "The component may not appear instantly. The component needs to be loaded, so you could experience 50-300ms loading time depending on your internet connection."
}
}
let AllPreviews

View File

@ -4,6 +4,10 @@ import uuid from "./modules/uuid"
import Utils from "./modules/Utils"
import DiscordTools from "./modules/DiscordTools"
import * as patchers from "./modules/patchers"
import excludeProperties from "./modules/excludeProperties"
import cloneNullProto from "./modules/cloneNullProto"
import NOOP from "./modules/noop"
import unfreeze from "./modules/Unfreeze"
patchers.patch()
const LightcordApi = {
@ -11,7 +15,13 @@ const LightcordApi = {
Components: Components,
uuid: uuid,
Utils: Utils,
DiscordTools: DiscordTools
DiscordTools: DiscordTools,
_: {
excludeProperties: excludeProperties,
cloneNullProto: cloneNullProto,
NOOP: NOOP,
unfreeze: unfreeze
}
}
declare global {
@ -47,7 +57,11 @@ export interface LightcordGlobal {
devMode: boolean,
callRingingBeat: boolean
},
Api: LightcordApiGlobal
Api: LightcordApiGlobal,
BetterDiscord: {
BdApi: typeof import("@bandagedbd/bdapi").BdApi,
[mod:string]:any
}
}
/**

View File

@ -43,10 +43,15 @@ export default new class DiscordTools {
return notification
}
playSound(sound:Sound){
createSound(sound:Sound){
soundModule = soundModule || WebpackLoader.findByUniqueProperties(["createSound"])
if(!soundModule)throw new WebpackLoaderError("Couldn't find soundModule here.")
const created = soundModule.createSound(sound)
return created
}
playSound(sound:Sound){
const created = this.createSound(sound)
created.play()
return created
}
@ -102,6 +107,45 @@ export class Notice extends EventEmitter {
})
}
/**
* Will be called whem the notice is removed.
*/
on(event: "removed", listener: () => void):this
/**
* Will be called when the notice is visible or not.
*/
on(event: "showing", listener: (isShowing:boolean) => void):this
/**
* Will be called when the notice queue changes.
*/
on(event: "index", listener: (index:number) => void):this
on(event: string, listener: (...args:any[]) => void){
return super.on(event, listener)
}
/**
* Will be called whem the notice is removed.
*/
once(event: "removed", listener: () => void):this
/**
* Will be called when the notice is visible or not.
*/
once(event: "showing", listener: (isShowing:boolean) => void):this
/**
* Will be called when the notice queue changes.
*/
once(event: "index", listener: (index:number) => void):this
once(event: string, listener: (...args:any[]) => void){
return super.once(event, listener)
}
off(event: "removed", listener: () => void):this
off(event: "showing", listener: (isShowing:boolean) => void):this
off(event: "index", listener: (index:number) => void):this
off(event: string, listener: (...args:any[]) => void){
return super.off(event, listener)
}
state:{
removed:boolean,
showing:boolean,

View File

@ -38,7 +38,7 @@ export default new class Utils {
}
FindReact(dom:Element, traverseUp:number = 0):React.Component|React.PureComponent{
/** took from https://stackoverflow.com/questions/29321742/react-getting-a-component-from-a-dom-element-for-debugging */
// taken from https://stackoverflow.com/questions/29321742/react-getting-a-component-from-a-dom-element-for-debugging#39165137
const key = Object.keys(dom).find(key=>key.startsWith("__reactInternalInstance$"));
const domFiber = dom[key];
if (domFiber == null) return null;

View File

@ -47,7 +47,9 @@ export function patch(){
})
/** END NOTICE */
/** START IN-APP NOTIFICATIONS */
//getModule(e => true)
/** END IN-APP NOTIFICATIONS */
}
function getModule(filter: (mod:any) => boolean):Promise<any>{

View File

@ -3,7 +3,7 @@ const path = require("path")
const terser = require("terser")
const util = require("util")
const production = true
const production = false
let fs = require("fs")

View File

@ -0,0 +1 @@
module.exports = require("@lightcord/api")

View File

@ -0,0 +1,11 @@
{
"name": "@lightcord/components",
"version": "1.0.0",
"description": "Components that can be found inside Ligthcord Api",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Lightcord",
"license": "ISC"
}

View File

@ -1,7 +1,7 @@
{
"name": "lightcord",
"version": "0.1.2",
"description": "",
"description": "A simple - customizable - Discord Client.",
"main": "dist/index.js",
"scripts": {
"compile": "node compile.js",

View File

@ -1,24 +1,25 @@
// bootstrap constants
// after startup, these constants will be merged into core module constants
// since they are used in both locations (see app/Constants.js)
import { releaseChannel } from './buildInfo';
import appSettings from './appSettings';
const pak = require("../package.json")
import * as path from "path"
const settings = appSettings.getSettings();
function capitalizeFirstLetter(s) {
return s.charAt(0).toUpperCase() + s.slice(1);
}
export const APP_NAME = 'Lightcord' + (releaseChannel === 'stable' ? '' : capitalizeFirstLetter(releaseChannel));
const APP_ID_BASE = 'com.squirrel';
export const APP_ID = `${APP_ID_BASE}.${APP_NAME}.${APP_NAME}`;
export const API_ENDPOINT = settings.get('API_ENDPOINT') || 'https://discord.com/api';
export const UPDATE_ENDPOINT = settings.get('UPDATE_ENDPOINT') || API_ENDPOINT;
export const mainAppDirname = path.join(__dirname, "..")
export const version:string = pak.version
// bootstrap constants
// after startup, these constants will be merged into core module constants
// since they are used in both locations (see app/Constants.js)
import { releaseChannel } from './buildInfo';
import appSettings from './appSettings';
const pak = require("../package.json" as any) as any
import * as path from "path"
const settings = appSettings.getSettings();
function capitalizeFirstLetter(s) {
return s.charAt(0).toUpperCase() + s.slice(1);
}
export const APP_NAME = 'Lightcord' + (releaseChannel === 'stable' ? '' : capitalizeFirstLetter(releaseChannel));
const APP_ID_BASE = 'com.squirrel';
export const APP_ID = `${APP_ID_BASE}.${APP_NAME}.${APP_NAME}`;
export const API_ENDPOINT = settings.get('API_ENDPOINT') || 'https://discord.com/api';
export const UPDATE_ENDPOINT = settings.get('UPDATE_ENDPOINT') || API_ENDPOINT;
export const mainAppDirname = path.join(__dirname, "..")
export const version:string = pak.version
export const packageJSON:any = pak

View File

@ -19,6 +19,8 @@ import * as moduleUpdater from "./common/moduleUpdater"
import * as paths from "./common/paths"
import { create } from "./singleInstance";
import * as splashScreen from "./splashScreen"
import { join } from "path"
import { homedir } from "os"
if (process.platform === 'linux') {
// Some people are reporting audio problems on Linux that are fixed by setting
@ -55,6 +57,22 @@ function hasArgvFlag(flag) {
//Transform main thread into async
(async function Main(){
await electron.app.whenReady()
if(process.argv.includes("--should-create-shortcut")){
console.log(`Creating shortcuts.`)
if(process.platform === "win32"){
electron.shell.writeShortcutLink(join(homedir(), "Desktop", "Lightcord.lnk"), "create", {
"appUserModelId": Constants.APP_ID,
description: Constants.packageJSON.description,
target: process.execPath
})
electron.shell.writeShortcutLink(join(electron.app.getPath("appData"), "Microsoft", "Windows", "Start Menu", "Programs", "Lightcord.lnk"), "create", {
"appUserModelId": Constants.APP_ID,
description: Constants.packageJSON.description,
target: process.execPath
})
}
}
console.log(`Initializing Lightcord.`)
console.log(`Version: ${buildInfo.version}
@ -82,14 +100,16 @@ commit: ${buildInfo.commit}`)
coreModule.setMainWindowVisible(!startMinimized)
}, (args) => {
if (args != null && args.length > 0 && args[0] === '--squirrel-uninstall') {
electron.app.quit();
return;
}
if(args.length === 1 && args[0] === "--overlay-host"){ // this is a patch for Lightcord that focus itself
//console.warn("OVERLAY HOST DÉTECTÉ. EVENNEMENT IGNORÉ MAIS POURRAIT CAUSER UN PROBLÈME.")
return
if(args && args.length > 0){
if(args.length > 0 && args[0] === '--squirrel-uninstall') {
electron.app.quit();
return;
}
if(args && args.length === 1 && args[0] === "--overlay-host"){ // this is a patch for Lightcord that focus itself
//console.warn("OVERLAY HOST DÉTECTÉ. EVENNEMENT IGNORÉ MAIS POURRAIT CAUSER UN PROBLÈME.")
return
}
}
if (coreModule) {

View File

@ -1,106 +1,106 @@
import * as electron from "electron"
import * as net from "net"
import * as path from "path"
import * as fs from "fs"
import * as os from "os"
import * as buildinfo from "./buildInfo"
function deleteSocketFile(socketPath) {
if (process.platform === 'win32') {
return;
}
if (fs.existsSync(socketPath)) {
try {
fs.unlinkSync(socketPath);
} catch (error) {
// Ignore ENOENT errors in case the file was deleted between the exists
// check and the call to unlink sync. This occurred occasionally on CI
// which is why this check is here.
if (error.code !== 'ENOENT') {
throw error;
}
}
}
}
/**
* Creates server to listen for additional atom application launches.
*
* You can run the command multiple times, but after the first launch
* the other launches will just pass their information to this server and then
* close immediately.
*/
function listenForArgumentsFromNewProcess(socketPath, callback) {
deleteSocketFile(socketPath);
const server = net.createServer(connection => {
connection.on('data', data => {
const args = JSON.parse(data.toString());
callback(args);
});
});
server.listen(socketPath);
server.on('error', error => console.error('Application server failed', error));
return server;
}
function tryStart(socketPath, callback, otherAppFound) {
// FIXME: Sometimes when socketPath doesn't exist, net.connect would strangely
// take a few seconds to trigger 'error' event, it could be a bug of node
// or atom-shell, before it's fixed we check the existence of socketPath to
// speedup startup.
if (process.platform !== 'win32' && !fs.existsSync(socketPath)) {
callback();
return;
}
const client = net.connect({ path: socketPath }, () => {
client.write(JSON.stringify(process.argv.slice(1)), () => {
client.end();
otherAppFound();
});
});
client.on('error', callback);
}
function makeSocketPath() {
let name = electron.app.name ? electron.app.name : electron.app.getName();
if (buildinfo.releaseChannel !== 'stable') {
name += buildinfo.releaseChannel;
}
if (process.platform === 'win32') {
return '\\\\.\\pipe\\' + name + '-sock';
} else {
return path.join(os.tmpdir(), name + '.sock');
}
}
export function create(startCallback, newProcessCallback) {
const socketPath = makeSocketPath();
tryStart(socketPath, () => {
const server = listenForArgumentsFromNewProcess(socketPath, newProcessCallback);
electron.app.on('will-quit', () => {
server.close();
deleteSocketFile(socketPath);
});
//@ts-ignore
electron.app.on('will-exit', () => {
server.close();
deleteSocketFile(socketPath);
});
startCallback();
}, () => {
console.log('Another instance exists. Quitting.');
electron.app.exit(0);
});
}
export function pipeCommandLineArgs(noOtherAppFoundCallback, otherAppFound) {
tryStart(makeSocketPath(), noOtherAppFoundCallback, otherAppFound);
import * as electron from "electron"
import * as net from "net"
import * as path from "path"
import * as fs from "fs"
import * as os from "os"
import * as buildinfo from "./buildInfo"
function deleteSocketFile(socketPath) {
if (process.platform === 'win32') {
return;
}
if (fs.existsSync(socketPath)) {
try {
fs.unlinkSync(socketPath);
} catch (error) {
// Ignore ENOENT errors in case the file was deleted between the exists
// check and the call to unlink sync. This occurred occasionally on CI
// which is why this check is here.
if (error.code !== 'ENOENT') {
throw error;
}
}
}
}
/**
* Creates server to listen for additional atom application launches.
*
* You can run the command multiple times, but after the first launch
* the other launches will just pass their information to this server and then
* close immediately.
*/
function listenForArgumentsFromNewProcess(socketPath, callback) {
deleteSocketFile(socketPath);
const server = net.createServer(connection => {
connection.on('data', data => {
const args = JSON.parse(data.toString());
callback(args);
});
});
server.listen(socketPath);
server.on('error', error => console.error('Application server failed', error));
return server;
}
function tryStart(socketPath, callback, otherAppFound) {
// FIXME: Sometimes when socketPath doesn't exist, net.connect would strangely
// take a few seconds to trigger 'error' event, it could be a bug of node
// or atom-shell, before it's fixed we check the existence of socketPath to
// speedup startup.
if (process.platform !== 'win32' && !fs.existsSync(socketPath)) {
callback();
return;
}
const client = net.connect({ path: socketPath }, () => {
client.write(JSON.stringify(process.argv.slice(1)), () => {
client.end();
otherAppFound();
});
});
client.on('error', callback);
}
function makeSocketPath() {
let name = electron.app.name ? electron.app.name : electron.app.getName();
if (buildinfo.releaseChannel !== 'stable') {
name += buildinfo.releaseChannel;
}
if (process.platform === 'win32') {
return '\\\\.\\pipe\\' + name + '-sock';
} else {
return path.join(os.tmpdir(), name + '.sock');
}
}
export function create(startCallback, newProcessCallback) {
const socketPath = makeSocketPath();
tryStart(socketPath, () => {
const server = listenForArgumentsFromNewProcess(socketPath, newProcessCallback);
electron.app.on('will-quit', () => {
server.close();
deleteSocketFile(socketPath);
});
//@ts-ignore
electron.app.on('will-exit', () => {
server.close();
deleteSocketFile(socketPath);
});
startCallback();
}, () => {
console.log('Another instance exists. Quitting.');
electron.app.exit(0);
});
}
export function pipeCommandLineArgs(noOtherAppFoundCallback, otherAppFound) {
tryStart(makeSocketPath(), noOtherAppFoundCallback, otherAppFound);
}