diff --git a/PKGBUILD b/build/linux/PKGBUILD similarity index 100% rename from PKGBUILD rename to build/linux/PKGBUILD diff --git a/build/linux/make-linux.sh b/build/linux/make-linux.sh new file mode 100755 index 00000000..f4b6a44a --- /dev/null +++ b/build/linux/make-linux.sh @@ -0,0 +1,30 @@ +#!/bin/bash +SOURCES="$(readlink -f "$(dirname "$(which "$0")")/../..")"; +case "$(uname -m)" in + 'x86_64') HOST='x64' ;; + 'x86'|'i'?'86'|'pentium4') HOST='ia32' ;; + 'armv7h') HOST='armv7l' ;; + 'aarch64') HOST='arm64' ;; + 'mips'*) HOST='mips64el' ;; + *) HOST="$(uname -m)" ;; +esac; +ARCH=("x64" "ia32" "armv7l" "arm64" "mips64el"); +# Makers that can be used to cross-package on any host arch: +MAKERS=("electron-forge-maker-appimage" # AppImage maker + "@electron-forge/maker-deb"); # DEB package maker + +# Package native: +src="$SOURCES" bash -c 'cd "$src"; npm run make'; + +# Package for non-native arch: +err=0 +for maker in "${MAKERS[@]}"; do + [[ -z "$maker_list" ]] || maker_list="${maker_list},"; + maker_list="${maker_list}${maker}"; +done; +for cpu_arch in "${ARCH[@]}"; do + [[ "$cpu_arch" == "$HOST" || "$err" != 0 ]] && break; + makers="$maker_list" arch="$cpu_arch" src="$SOURCES" bash -c 'cd "$src"; npm run make -- -p linux -a "$arch" --targets "$makers"'; + err=$?; +done; +exit $err; \ No newline at end of file diff --git a/CONTRIBUTIONS.md b/docs/CONTRIBUTING.md similarity index 93% rename from CONTRIBUTIONS.md rename to docs/CONTRIBUTING.md index bff6b325..bed37931 100644 --- a/CONTRIBUTIONS.md +++ b/docs/CONTRIBUTING.md @@ -20,6 +20,11 @@ npm i && npm run make ``` Type `npm run make -- --help` in the Terminal for more advanced usage. +To package for all supported architectures for Linux (on Linux hosts): +```sh +npm i && npm run make:linux +``` + ## Packaging Sometimes you don't want to package the application (to quickly debug the application after packaging it) or there's no standard for your OS supported by the Electron Forge. Fortunately, you can still produce the directory containing the electron binary for your OS and architecture and packaged sources in `app.asar` with the Electron Forge. diff --git a/LICENSE.md b/docs/LICENSE.md similarity index 100% rename from LICENSE.md rename to docs/LICENSE.md diff --git a/README.md b/docs/README.md similarity index 62% rename from README.md rename to docs/README.md index ca083e92..2948b1a4 100644 --- a/README.md +++ b/docs/README.md @@ -1,25 +1,25 @@ # Electron Discord WebApp -[![MIT license](https://img.shields.io/badge/License-MIT-C23939.svg)](./LICENSE.md) +[![MIT license](https://img.shields.io/badge/License-MIT-C23939.svg)](LICENSE.md) [![Electron](https://img.shields.io/badge/Made%20with-Electron-486F8F.svg)](https://www.electronjs.org/) -[![GitHub release](https://img.shields.io/github/release/SpacingBat3/electron-discord-webapp.svg)](../../tags) -[![Github downloads](https://img.shields.io/github/downloads/SpacingBat3/electron-discord-webapp/total.svg)](../../releases) -[![GitHub contributors](https://img.shields.io/github/contributors/SpacingBat3/electron-discord-webapp.svg)](../../graphs/contributors) +[![GitHub release](https://img.shields.io/github/release/SpacingBat3/electron-discord-webapp.svg)](../../../tags) +[![Github downloads](https://img.shields.io/github/downloads/SpacingBat3/electron-discord-webapp/total.svg)](../../../releases) +[![GitHub contributors](https://img.shields.io/github/contributors/SpacingBat3/electron-discord-webapp.svg)](../../../graphs/contributors) [![PRs/Translations Welcome](https://img.shields.io/badge/PRs/Translations-welcome-brightgreen.svg)](#want-to-contribute-to-my-project) -[![Pi-Apps badge](https://badgen.net/badge/Pi-Apps%3F/Yes!/c51a4a?icon=https://raw.githubusercontent.com/Botspot/pi-apps/3d61f713573ba591aba50c32dd95c9df2f845b37/icons/logo.svg)](https://github.com/Botspot/pi-apps) +[![Pi-Apps badge](https://badgen.net/badge/Pi-Apps%3F/Yes!/c51a4a?icon=http://raw.githubusercontent.com/Botspot/pi-apps/3d61f713573ba591aba50c32dd95c9df2f845b37/icons/logo.svg)](https://github.com/Botspot/pi-apps) -A Discord Web App based on the [Electron](https://github.com/electron/electron) engine, tweaked with the [Electron Forge](). +A Discord Web App based on the [Electron](https://github.com/electron/electron) engine, enchanced with the [Electron Forge](https://github.com/electron-userland/electron-forge). It is completely indpenendent from [Discord-Electron](https://github.com/GyozaGuy/Discord-Electron), but I used it to learn why my previous attempts of doing electron discord app have failed – so I really apprieciate that someone wrote that code. I've previously forked his work, but right now I've made a seperate one – this fixed some issues that the fork had and overall improved multiple things a lot. ## Docs: -- [Contribution](./CONTRIBUTIONS.md) - - [Run from the sources](./CONTRIBUTIONS.md#run) - - [Creating the distributables](./CONTRIBUTIONS.md#creating-distributables) - - [Packaging the application](./CONTRIBUTIONS.md#packaging) -- [Translating the application](./TRANSLATE.md) - - [JSON basics](./TRANSLATE.md#dont-know-the-json-syntax) - - [Credits](./TRANSLATE.md#the-people-that-hepled-me-with-the-app-translation) -- [License](./LICENSE.md) +- [Contributing](CONTRIBUTING.md) + - [Run from the sources](CONTRIBUTING.md#run) + - [Creating the distributables](CONTRIBUTING.md#creating-distributables) + - [Packaging the application](CONTRIBUTING.md#packaging) +- [Translating the application](TRANSLATE.md) + - [JSON basics](TRANSLATE.md#dont-know-the-json-syntax) + - [Credits](TRANSLATE.md#the-people-that-hepled-me-with-the-app-translation) +- [License](LICENSE.md) ## License This project is redistributed under the [MIT License](LICENSE.md). diff --git a/TRANSLATE.md b/docs/TRANSLATE.md similarity index 100% rename from TRANSLATE.md rename to docs/TRANSLATE.md diff --git a/icons/app.icns b/icons/app.icns new file mode 100644 index 00000000..a3795f55 Binary files /dev/null and b/icons/app.icns differ diff --git a/icons/app.ico b/icons/app.ico new file mode 100644 index 00000000..ff3f30fb Binary files /dev/null and b/icons/app.ico differ diff --git a/package.json b/package.json index 577a8944..c980791f 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,14 @@ { "name": "electron-discord-webapp", "productName": "Electron Discord Web App", - "version": "1.0.0", + "version": "1.0.1", "description": "A Discord Web App based on the Electron Engine.", "main": "src/js/main.js", "scripts": { "start": "electron-forge start", "package": "electron-forge package", - "make": "electron-forge make" + "make": "electron-forge make", + "make:linux": "bash ./build/linux/make-linux.sh" }, "keywords": [ "discord", @@ -43,46 +44,6 @@ "electron-squirrel-startup": "^1.0.0" }, "config": { - "forge": { - "packagerConfig": { - "executableName": "electron-discord-webapp" - }, - "makers": [ - { - "name": "@electron-forge/maker-squirrel", - "config": { - "name": "electron_discord_webapp" - } - }, - { - "name": "@electron-forge/maker-zip", - "platforms": [ - "darwin" - ] - }, - { - "name": "@electron-forge/maker-deb", - "config": {} - }, - { - "name": "@electron-forge/maker-rpm", - "config": {} - }, - { - "name": "electron-forge-maker-appimage", - "config": { - "icon": "icons/app.png" - } - } - ], - "publishers": [ - { - "name": "@electron-forge/publisher-github", - "config": { - "prerelease": true - } - } - ] - } + "forge": "./src/js/configForge.js" } } diff --git a/src/js/configForge.js b/src/js/configForge.js new file mode 100644 index 00000000..74a3fa0c --- /dev/null +++ b/src/js/configForge.js @@ -0,0 +1,69 @@ +/* + * Electron Forge Config (configForge.js) + */ + +// Let's import some keys from the package.json: +const packageJson = require(`../../package.json`); +// Global variables in the config: +const iconFile = "icons/app.png" + +module.exports = { + packagerConfig: { + executableName: packageJson.name, // name instead of the productName + asar: true, + icon: iconFile, // used in Windows and MacOS binaries + extraResource: [ + "docs/LICENSE.md", + iconFile + ], + quiet: true, + ignore:[ + "docs", + "build" + ] + }, + makers: [ + { + name: "@electron-forge/maker-squirrel", + config: { + name: "electron_discord_webapp" + } + }, + { + name: "@electron-forge/maker-zip", + platforms: [ + "darwin" + ] + }, + { + name: "electron-forge-maker-appimage", + config: { + icon: iconFile + } + }, + { + name: "@electron-forge/maker-deb", + config: { + options: { + icon: iconFile + } + } + }, + { + name: "@electron-forge/maker-rpm", + config: { + options: { + icon: iconFile + } + } + } + ], + publishers: [ + { + name: "@electron-forge/publisher-github", + config: { + prerelease: true + } + } + ] +} diff --git a/src/js/main.js b/src/js/main.js index 4b28a34f..c7af7eb4 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -1,92 +1,103 @@ - +/* + * Main process script (main.js) + */ + // Load the stuff we need to have there: -const { app, BrowserWindow, shell, ipcMain, Menu } = require('electron') -const fs = require('fs') -const path = require('path') -const appConfig = new require('electron-json-config') -var deepmerge = require('deepmerge') +const { app, BrowserWindow, shell, ipcMain, Menu } = require('electron'); +const fs = require('fs'); +const path = require('path'); +const appConfig = new require('electron-json-config'); +const deepmerge = require('deepmerge'); -/* Get current app dir – also removes the need of importing icons - manually to the electron package dir. */ +/* + * Get current app dir – also removes the need of importing icons + * manually to the electron package dir. + */ -const appDir = app.getAppPath() +const appDir = app.getAppPath(); -/* Check if we are using the packaged version. - This also fixes for "About" icon (that can't be loaded with the electron - when it is packaged in ASAR) */ +/* + * Check if we are using the packaged version. + * This also fixes for "About" icon (that can't be loaded with the + * electron when it is packaged in ASAR) + */ if (appDir.indexOf("app.asar") < 0) { - var appIconDir = `${appDir}/icons` + var appIconDir = `${appDir}/icons`; } else { - var appIconDir = process.resourcesPath + var appIconDir = process.resourcesPath; } -var packageJson = require(`${appDir}/package.json`) // Read package.json +const packageJson = require(`${appDir}/package.json`); // Read package.json // Load scripts: -const getUserAgent = require(`${appDir}/src/js/userAgent.js`) -const getMenu = require(`${appDir}/src/js/menus.js`) +const getUserAgent = require(`${appDir}/src/js/userAgent.js`); +const getMenu = require(`${appDir}/src/js/menus.js`); // Load string translations: function loadTranslations() { - var systemLang = app.getLocale() - var localStrings = `src/lang/${systemLang}/strings.json` - var globalStrings = require(`${appDir}/src/lang/en-GB/strings.json`) + const systemLang = app.getLocale(); + var localStrings = `src/lang/${systemLang}/strings.json`; + var globalStrings = require(`${appDir}/src/lang/en-GB/strings.json`); if(fs.existsSync(path.join(appDir, localStrings))) { - var localStrings = require(`${appDir}/src/lang/${systemLang}/strings.json`) - var l10nStrings = deepmerge(globalStrings, localStrings) + var localStrings = require(`${appDir}/src/lang/${systemLang}/strings.json`); + var l10nStrings = deepmerge(globalStrings, localStrings); } else { - var l10nStrings = globalStrings // Default lang to english + var l10nStrings = globalStrings; // Default lang to english } - return l10nStrings + return l10nStrings; } // Vars to modify app behavior -var appURL = 'https://discord.com/app' -var appIcon = `${appIconDir}/app.png` -var appTrayIcon = `${appDir}/icons/tray.png` -var appTrayPing = `${appDir}/icons/tray-ping.png` -var appTrayIconSmall = `${appDir}/icons/tray-small.png` -var winWidth = 1000 -var winHeight = 600 +const appURL = 'https://discord.com/app'; +const appIcon = `${appIconDir}/app.png`; +const appTrayIcon = `${appDir}/icons/tray.png`; +const appTrayPing = `${appDir}/icons/tray-ping.png`; +const appTrayIconSmall = `${appDir}/icons/tray-small.png`; +var winWidth = 1000; +var winHeight = 600; // "About" information -var appFullName = app.getName() -var appVersion = packageJson.version; -var appAuthor = packageJson.author.name -var appYear = '2020' // the year since this app exists -var appRepo = packageJson.homepage; -var chromiumVersion = process.versions.chrome +const appFullName = app.getName() +const appVersion = packageJson.version; +const appAuthor = packageJson.author.name; +const appYear = '2020'; // the year since this app exists +const updateYear = '2021'; // the year when the last update got released +const appRepo = packageJson.homepage; +const chromiumVersion = process.versions.chrome; -/* Remember to add yourself to the contributors array in the package.json - if you're improving the code of this application */ +/* + * Remember to add yourself to the contributors array in the package.json + * if you're improving the code of this application + */ if (Array.isArray(packageJson.contributors) && packageJson.contributors.length) { - var appContributors = [ appAuthor, ...packageJson.contributors ] + var appContributors = [ appAuthor, ...packageJson.contributors ]; } else { - var appContributors = [appAuthor] + var appContributors = [appAuthor]; } -// "Static" Variables that shouldn't be changed +// "Dynamic" variables that shouldn't be changed: -let tray = null -var wantQuit = false -var currentYear = new Date().getFullYear() -var stringContributors = appContributors.join(', ') -var mainWindow = null -var noInternet = false -const singleInstance = app.requestSingleInstanceLock() +let tray = null; +var wantQuit = false; +var stringContributors = appContributors.join(', '); +var mainWindow = null; +var noInternet = false; +const singleInstance = app.requestSingleInstanceLock(); -/* Migrate old config dir to the new one. - This option exist because of the compability reasons - with v0.1.X versions of this script */ +/* + * Migrate old config dir to the new one. + * This option exist because of the compability reasons + * with v0.1.X versions of this script + */ -const oldUserPath = path.join(app.getPath('userData'), '..', packageJson.name) +const oldUserPath = path.join(app.getPath('userData'), '..', packageJson.name); if(fs.existsSync(oldUserPath)) { - fs.rmdirSync(app.getPath('userData'), { recursive: true }) - fs.renameSync(oldUserPath, app.getPath('userData')) + fs.rmdirSync(app.getPath('userData'), { recursive: true }); + fs.renameSync(oldUserPath, app.getPath('userData')); } // Known boolean keys from config @@ -94,22 +105,22 @@ if(fs.existsSync(oldUserPath)) { configKnownObjects = [ 'disableTray', 'hideMenuBar' -] +]; -// Year format for copyright +// Year format for the copyright -if (appYear == currentYear){ - var copyYear = appYear +if (appYear == updateYear){ + var copyYear = appYear; } else { - var copyYear = `${appYear}-${currentYear}` + var copyYear = `${appYear}-${updateYear}`; } -fakeUserAgent = getUserAgent(chromiumVersion) +fakeUserAgent = getUserAgent(chromiumVersion); // "About" Panel: function aboutPanel() { - l10nStrings = loadTranslations() + l10nStrings = loadTranslations(); const aboutWindow = app.setAboutPanelOptions({ applicationName: appFullName, iconPath: appIcon, @@ -118,26 +129,28 @@ function aboutPanel() { website: appRepo, credits: `${l10nStrings.help.contributors} ${stringContributors}`, copyright: `Copyright © ${copyYear} ${appAuthor}\n\n${l10nStrings.help.credits}` - }) - return aboutWindow + }); + return aboutWindow; } function createWindow() { - const mainWindowState = windowStateKeeper('win') // Check the window state + // Check the window state + + const mainWindowState = windowStateKeeper('win'); // Get known boolean vars from the config for (var x = 0, len = configKnownObjects.length; x < len; x++) { - var y = configKnownObjects[x] + var y = configKnownObjects[x]; if (appConfig.get(y)) { - this[y] = appConfig.get(y) + this[y] = appConfig.get(y); } else { this[y] = false; } } - l10nStrings = loadTranslations() // Load translations for this window + l10nStrings = loadTranslations(); // Load translations for this window // Browser window @@ -151,35 +164,36 @@ function createWindow() { nodeIntegration: false, // won't work with the true value devTools: false } - }) - win.loadURL(appURL,{userAgent: fakeUserAgent}) - win.setAutoHideMenuBar(hideMenuBar) - win.setMenuBarVisibility(!hideMenuBar) - mainWindowState.track(win) + }); + win.loadURL(appURL,{userAgent: fakeUserAgent}); + win.setAutoHideMenuBar(hideMenuBar); + win.setMenuBarVisibility(!hideMenuBar); + mainWindowState.track(win); // Load all menus: - cmenu = getMenu.context(win) - if(!disableTray) tray = getMenu.tray(appTrayIcon, appTrayIconSmall, win) - menubar = getMenu.bar(packageJson.repository.url, win) + cmenu = getMenu.context(win); + if(!disableTray) tray = getMenu.tray(appTrayIcon, appTrayIconSmall, win); + menubar = getMenu.bar(packageJson.repository.url, win); // Open external URLs in default browser win.webContents.on('new-window', (event, externalURL) => { event.preventDefault(); - shell.openExternal(externalURL) - }) + shell.openExternal(externalURL); + }); // "Red dot" icon feature + win.webContents.once('did-finish-load', () => { win.webContents.on('page-favicon-updated', () => { if(!win.isFocused() && !disableTray) tray.setImage(appTrayPing); - }) + }); app.on('browser-window-focus', () => { - if(!disableTray) tray.setImage(appTrayIcon) - }) - }) + if(!disableTray) tray.setImage(appTrayIcon); + }); + }); return win } @@ -200,7 +214,7 @@ function windowStateKeeper(windowName) { windowState = { width: winWidth, - height: winHeight, + height: winHeight }; } function saveState() { @@ -222,43 +236,43 @@ function windowStateKeeper(windowName) { width: windowState.width, height: windowState.height, isMaximized: windowState.isMaximized, - track, + track }); } -// Check if other scripts wants app to quit +// Check if other scripts wants app to quit (through the IPC) ipcMain.on('want-to-quit', () => { - var wantQuit = true - app.quit() -}) + var wantQuit = true; + app.quit(); +}); if (!singleInstance) { - app.quit() + app.quit(); } else { app.on('second-instance', (event, commandLine, workingDirectory) => { if (mainWindow){ - if(!mainWindow.isVisible()) mainWindow.show() - if(mainWindow.isMinimized()) mainWindow.restore() - mainWindow.focus() + if(!mainWindow.isVisible()) mainWindow.show(); + if(mainWindow.isMinimized()) mainWindow.restore(); + mainWindow.focus(); } - }) + }); app.on('ready', () => { - mainWindow = createWindow() // catch window as mainWindow - aboutWindow = aboutPanel() - }) + mainWindow = createWindow(); // catch window as mainWindow + aboutWindow = aboutPanel(); + }); } app.on('window-all-closed', () => { if (process.platform !== 'darwin') { - app.quit() + app.quit(); } -}) +}); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { - mainWindow = createWindow() - aboutWindow = aboutPanel() + mainWindow = createWindow(); + aboutWindow = aboutPanel(); } -}) +}); diff --git a/src/js/menus.js b/src/js/menus.js index 62857968..8072a4fc 100644 --- a/src/js/menus.js +++ b/src/js/menus.js @@ -1,3 +1,6 @@ +/* + * Menu Objects (menus.js + */ const { app, Menu, BrowserWindow, MenuItem, Tray, Notification, dialog, shell } = require('electron') const appConfig = new require('electron-json-config') var wantQuit = false @@ -105,9 +108,7 @@ exports.bar = (repoLink, mainWindow) => { } } }, - /* This will be back once I'll do my own JS game :) - * Right now, if you're reading this, you can use it - * to integrate any website within my app (for some reason) + /* An unused placeholder { label: "Template", @@ -119,13 +120,13 @@ exports.bar = (repoLink, mainWindow) => { height: 480, modal: true, background: "#000", - icon: `${appDir}/icons/game.png` + icon: `${appDir}/icons/temp.png` }) child.loadFile(`${appDir}/offline/index.html`) child.setAutoHideMenuBar(true) child.setMenuBarVisibility(false) child.removeMenu() - console.log("Do you want to have a Bad Time? 'Cause if you don't close this window right now... you're not going to like what's happen next.") + console.log("You just found an easter egg!") } } diff --git a/src/js/userAgent.js b/src/js/userAgent.js index 880ff221..ea88f46d 100644 --- a/src/js/userAgent.js +++ b/src/js/userAgent.js @@ -1,14 +1,19 @@ -// Checks the platform and generates the proper User Agent: +/* + * Fake UserAgent generator (userAgent.js) + */ + const { app } = require('electron') module.exports = function(chromeVersion){ if (process.platform == 'darwin') { - var fakeUserAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`; + var fakeUserAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`; } else if (process.platform == 'win32') { var fakeUserAgent = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`; } else { - /* Don't lie we're using ARM (or x86) CPU – maybe then Discord will understand - then how popular it is on Raspberries and Linux ARM ;) */ + /* + * Don't lie we're using ARM (or x86) CPU – maybe then Discord will understand + * how popular it is on Raspberries and Linux ARM ;) + */ if (process.arch == 'arm64') { var cpuArch = "aarch64" } else if (process.arch == 'arm') {