diff --git a/.github/workflows/stable.yml b/.github/workflows/stable.yml new file mode 100644 index 0000000..abd99ff --- /dev/null +++ b/.github/workflows/stable.yml @@ -0,0 +1,196 @@ +name: Release build +on: + push: + branches: + - stable + +env: + FORCE_COLOR: true + +jobs: + build-linux: + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install Node dependencies + run: npm install + + - name: Install Electron-Builder + run: npm install -g electron-builder + + - name: Build + run: npm run build && electron-builder --linux && electron-builder --arm64 --linux && electron-builder --armv7l --linux + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: List all files in the dist directory + run: ls -l dist + - name: Delete unpacked builds + run: rm -rf dist/linux-unpacked && rm -rf dist/linux-arm64-unpacked && rm -rf dist/linux-armv7l-unpacked + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: ArmCordLinux + path: dist/ + + + build-mac: + runs-on: macos-latest + + steps: + - uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install Node dependencies + run: npm install + + - name: Install Electron-Builder + run: npm install -g electron-builder + + - name: Build + run: npm run build && electron-builder --macos + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: List all files in the dist directory + run: ls -l dist + - name: Delete unpacked builds + run: rm -rf dist/macos-unpacked + + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: ArmCordMac + path: dist/ + + build-windows: + runs-on: windows-latest + + steps: + - uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install Node dependencies + run: npm install + + - name: Install Electron-Builder + run: npm install -g electron-builder + + - name: Build + run: npm run build && electron-builder --windows + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Delete unpacked builds + run: Remove-Item -LiteralPath ".\dist\win-unpacked" -Force -Recurse + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: ArmCordWindows + path: dist/ + + + release: + runs-on: ubuntu-latest + needs: [build-linux, build-mac, build-windows] + + steps: + - uses: actions/download-artifact@v2 + with: + name: ArmCordMac + path: macos + + - uses: actions/download-artifact@v2 + with: + name: ArmCordWindows + path: windows + + - uses: actions/download-artifact@v2 + with: + name: ArmCordLinux + path: linux + - name: ls + run: ls + - name: Delete unwanted directories + run: rm -rf {linux,macos,windows}/*/ + rm -rf {linux,macos,windows}/.icon* + rm -rf {linux,macos,windows}/builder-debug.yml + - name: ls dirs + run: ls linux && ls macos && ls windows + - name: Get some values needed for the release + id: vars + shell: bash + run: | + echo "::set-output name=releaseTag::$(git describe --tags --abbrev=0)" + + - uses: dev-drprasad/delete-tag-and-release@v0.2.0 + with: + delete_release: true + tag_name: v3.0.6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create Release + uses: actions/github-script@v2 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + console.log('environment', process.versions); + + const fs = require('fs').promises; + + const { repo: { owner, repo }, sha } = context; + console.log({ owner, repo, sha }); + + const release = await github.repos.createRelease({ + owner, repo, + tag_name: process.env.releaseTag, + draft: true, + target_commitish: sha + }); + + console.log('created release', { release }); + + for (let file of await fs.readdir('linux')) { + // do whatever filtering you want here, I'm just uploading all the files + console.log('uploading', file); + await github.repos.uploadReleaseAsset({ + owner, repo, + release_id: release.data.id, + name: file, + data: await fs.readFile(`./linux/${file}`) + }); + } + for (let file of await fs.readdir('windows')) { + // do whatever filtering you want here, I'm just uploading all the files + console.log('uploading', file); + await github.repos.uploadReleaseAsset({ + owner, repo, + release_id: release.data.id, + name: file, + data: await fs.readFile(`./windows/${file}`) + }); + } + for (let file of await fs.readdir('macos')) { + // do whatever filtering you want here, I'm just uploading all the files + console.log('uploading', file); + await github.repos.uploadReleaseAsset({ + owner, repo, + release_id: release.data.id, + name: file, + data: await fs.readFile(`./macos/${file}`) + }); + } + env: + releaseTag: ${{ steps.vars.outputs.releaseTag }} \ No newline at end of file diff --git a/README.md b/README.md index c204d55..80e9f95 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ ArmCord is also available on the Snap store [here](https://snapcraft.io/armcord) [electron-discord-webapp](https://github.com/SpacingBat3/electron-discord-webapp) [custom-electron-titlebar (css only)](https://github.com/AlexTorresSk/custom-electron-titlebar) [electron-builder](https://electron.build) -[OpenAsar (~~stole~~ borrowed code for adding custom build info seemlessly)](https://github.com/GooseMod/OpenAsar) +[OpenAsar](https://github.com/GooseMod/OpenAsar) # Sponsors [![JetBrains supports ArmCord with free licenses to their software to core developers](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://jb.gg/OpenSourceSupport) diff --git a/build/icon.icns b/build/icon.icns new file mode 100644 index 0000000..f030674 Binary files /dev/null and b/build/icon.icns differ diff --git a/package.json b/package.json index bf25c06..8fbd10c 100644 --- a/package.json +++ b/package.json @@ -26,10 +26,10 @@ "@types/node": "^17.0.33", "@types/ws": "^8.5.3", "copyfiles": "^2.4.1", - "electron": "^18.2.4", + "electron": "^19.0.4", "electron-builder": "^23.0.3", "husky": "^8.0.1", - "prettier": "^2.5.1", + "prettier": "^2.6.2", "typescript": "^4.6.3" }, "dependencies": { @@ -44,6 +44,7 @@ "category": "Network" }, "linux": { + "icon": "build/icon.icns", "category": "Network", "maintainer": "smartfrigde@gmail.com", "target": [ diff --git a/src/content/setup.html b/src/content/setup.html index 6ed4c94..4b9d4bd 100644 --- a/src/content/setup.html +++ b/src/content/setup.html @@ -101,6 +101,7 @@ automaticPatches: false, mods: "cumcord", inviteWebsocket: true, + performanceMode: "none", blurType: "acrylic" }); setTimeout(() => window.armcordinternal.restart(), 5000); @@ -130,6 +131,7 @@ autoLaunch: true, minimizeToTray: true, automaticPatches: false, + performanceMode: "none", mods: options.mod, inviteWebsocket: true, blurType: "acrylic" @@ -145,6 +147,7 @@ automaticPatches: false, autoLaunch: true, mods: "none", + performanceMode: "none", inviteWebsocket: true, blurType: "acrylic" }); diff --git a/src/main.ts b/src/main.ts index ec61b77..8d1abb9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,7 @@ // Modules to control application life and create native browser window import {app, BrowserWindow, session, dialog} from "electron"; import "v8-compile-cache"; -import {getConfig, setup, checkIfConfigExists} from "./utils"; +import {getConfig, setup, checkIfConfigExists, injectElectronFlags} from "./utils"; import "./extensions/mods"; import "./extensions/plugin"; import "./tray"; @@ -12,6 +12,7 @@ export var settings: any; export var customTitlebar: boolean; export var tabs: boolean; + if (process.platform == "linux") { if (process.env.$XDG_SESSION_TYPE == "wayland") { console.log("Wayland specific patches applied."); @@ -24,7 +25,7 @@ if (process.platform == "linux") { } } checkIfConfigExists(); - +injectElectronFlags(); app.whenReady().then(async () => { switch (await getConfig("windowStyle")) { case "default": diff --git a/src/preload/preload.ts b/src/preload/preload.ts index 2d3fd71..fcde5a0 100644 --- a/src/preload/preload.ts +++ b/src/preload/preload.ts @@ -50,8 +50,30 @@ if (window.location.href.indexOf("splash.html") > -1) { } }); } +/* +MIT License -// Settings info version injection (Stolen and modified from OpenAsar, mwuh ha ha ha ha >:D) +Copyright (c) 2022 GooseNest + +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. +*/ +// Settings info version injection setInterval(() => { const host = document.getElementsByClassName("info-3pQQBb")[0]; if (!host || document.querySelector("#ac-ver")) return; diff --git a/src/settings/settings.html b/src/settings/settings.html index 7d723c5..c57151c 100644 --- a/src/settings/settings.html +++ b/src/settings/settings.html @@ -58,7 +58,14 @@

Client mod:

- +
+ +

Performance mode:

+
@@ -71,6 +78,7 @@ document.getElementById("mod").value = await settings.get("mods"); document.getElementById("channel").value = await settings.get("channel"); document.getElementById("theme").value = await settings.get("windowStyle"); + document.getElementById("performanceMode").value = await settings.get("performanceMode"); } loadSettings(); document.getElementById("save").addEventListener("click", function () { @@ -84,6 +92,7 @@ mods: document.getElementById("mod").value, blurType: "acrylic", inviteWebsocket: document.getElementById("websocket").checked, + performanceMode: document.getElementById("prfmMode").value, doneSetup: true }); }); diff --git a/src/utils.ts b/src/utils.ts index e331a96..2a42ff8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,7 @@ import * as fs from "fs"; import {app, dialog} from "electron"; import path from "path"; +import { defaultMaxListeners } from "events"; export var firstRun: boolean; export var isSetup: boolean; export var contentPath: string; @@ -42,6 +43,7 @@ export function setup() { automaticPatches: false, mods: "cumcord", blurType: "acrylic", + performanceMode: "none", inviteWebsocket: true, doneSetup: false }; @@ -63,7 +65,45 @@ export async function injectJS(inject: string) { document.body.appendChild(el); } +export async function injectElectronFlags() { + // MIT License + // Copyright (c) 2022 GooseNest + + // 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. + const presets = { + 'performance': `--enable-gpu-rasterization --enable-zero-copy --ignore-gpu-blocklist --enable-hardware-overlays=single-fullscreen,single-on-top,underlay --enable-features=EnableDrDc,CanvasOopRasterization,BackForwardCache:TimeToLiveInBackForwardCacheInSeconds/300/should_ignore_blocklists/true/enable_same_site/true,ThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes,UseSkiaRenderer,WebAssemblyLazyCompilation --disable-features=Vulkan --force_high_performance_gpu`, // Performance + 'battery': '--enable-features=TurnOffStreamingMediaCachingOnBattery --force_low_power_gpu' // Known to have better battery life for Chromium? + }; + switch (await getConfig("performanceMode")) { + case "performance": + console.log("Performance mode enabled"); + app.commandLine.appendSwitch(presets.performance); + break; + case "battery": + console.log("Battery mode enabled"); + app.commandLine.appendSwitch(presets.battery); + break; + default: + console.log("No performance modes set"); + } +} //ArmCord Settings/Storage manager export interface Settings { @@ -73,6 +113,7 @@ export interface Settings { minimizeToTray: boolean; automaticPatches: boolean; mods: string; + performanceMode: string, blurType: string; inviteWebsocket: boolean; doneSetup: boolean; diff --git a/src/window.ts b/src/window.ts index ddcdb33..9b6acf2 100644 --- a/src/window.ts +++ b/src/window.ts @@ -4,7 +4,7 @@ // I'm sorry for this mess but I'm not sure how to fix it. import {BrowserWindow, shell, app, ipcMain, dialog, clipboard} from "electron"; import path from "path"; -import {checkIfConfigIsBroken, firstRun, getConfig, contentPath, isSetup} from "./utils"; +import {checkIfConfigIsBroken, firstRun, getConfig, contentPath, isSetup, setConfig} from "./utils"; import {registerIpc} from "./ipc"; import startServer from "./socket"; import contextMenu from "electron-context-menu"; @@ -13,6 +13,7 @@ export var icon: string; export let mainWindow: BrowserWindow; export let inviteWindow: BrowserWindow; let guestWindows: BrowserWindow[] = []; +var osType = os.type() contextMenu({ showSaveImageAs: true, @@ -21,11 +22,50 @@ contextMenu({ }); async function doAfterDefiningTheWindow() { + var ignoreProtocolWarning = await getConfig("ignoreProtocolWarning"); checkIfConfigIsBroken(); registerIpc(); - mainWindow.webContents.userAgent = `Mozilla/5.0 (X11; ${os.type()} ${os.arch()}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36`; //fake useragent for screenshare to work + + // A little sloppy but it works :p + if (osType == 'Windows_NT') { + osType = "Windows " + os.release().split('.')[0] + " (" + os.release() + ")"; + } + mainWindow.webContents.userAgent = `Mozilla/5.0 (X11; ${osType} ${os.arch()}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36`; //fake useragent for screenshare to work mainWindow.webContents.setWindowOpenHandler(({url}) => { - shell.openExternal(url); + if (url.startsWith("https:" || url.startsWith("http:") || url.startsWith("mailto:"))) { + shell.openExternal(url); + } else { + if (ignoreProtocolWarning) { + shell.openExternal(url); + } else { + const options = { + type: "question", + buttons: ["Yes, please", "No, I don't"], + defaultId: 1, + title: url, + message: `Do you want to open ${url}?`, + detail: "This url was detected to not use normal browser protocols. It could mean that this url leads to a local program on your computer. Please check if you recognise it, before proceeding!", + checkboxLabel: "Remember my answer and ignore this warning for future sessions", + checkboxChecked: false + }; + + dialog.showMessageBox(mainWindow, options).then(({response, checkboxChecked}) => { + console.log(response, checkboxChecked); + if (checkboxChecked) { + if (response == 0) { + setConfig("ignoreProtocolWarning", true); + } else { + setConfig("ignoreProtocolWarning", false); + } + } + if (response == 0) { + shell.openExternal(url); + } else { + return; + } + }); + } + } return {action: "deny"}; }); mainWindow.webContents.session.webRequest.onBeforeRequest((details, callback) => { @@ -79,7 +119,7 @@ export function createCustomWindow() { height: 350, title: "ArmCord", darkTheme: true, - icon: path.join(__dirname, "/assets/icon_transparent.png"), + icon: path.join(__dirname, "../", "/assets/ac_icon_transparent.png"), frame: false, autoHideMenuBar: true, webPreferences: { @@ -95,7 +135,7 @@ export function createNativeWindow() { height: 350, title: "ArmCord", darkTheme: true, - icon: path.join(__dirname, "/assets/icon_transparent.png"), + icon: path.join(__dirname, "../", "/assets/ac_icon_transparent.png"), frame: true, autoHideMenuBar: true, webPreferences: { @@ -117,7 +157,7 @@ export function createTabsHost() { height: 350, title: "ArmCord", darkTheme: true, - icon: path.join(__dirname, "/assets/icon_transparent.png"), + icon: path.join(__dirname, "../", "/assets/ac_icon_transparent.png"), frame: true, autoHideMenuBar: true, webPreferences: { @@ -146,7 +186,7 @@ export function createTabsGuest(number: number) { height: 600, title: "ArmCord Guest Window " + number, darkTheme: true, - icon: path.join(__dirname, "/assets/icon_transparent.png"), + icon: path.join(__dirname, "../", "/assets/ac_icon_transparent.png"), frame: true, autoHideMenuBar: true, webPreferences: { @@ -183,7 +223,7 @@ export function createInviteWindow() { height: 600, title: "ArmCord Invite Manager", darkTheme: true, - icon: path.join(__dirname, "/assets/icon_transparent.png"), + icon: path.join(__dirname, "../", "/assets/ac_icon_transparent.png"), frame: true, autoHideMenuBar: true, webPreferences: {