mirror of
https://github.com/smartfrigde/armcord.git
synced 2024-08-14 23:56:58 +00:00
Compare commits
10 commits
842b6e8c5d
...
d40b76719a
Author | SHA1 | Date | |
---|---|---|---|
|
d40b76719a | ||
|
0a77987cec | ||
|
e893675d6a | ||
|
a072e389a5 | ||
|
cdc331392e | ||
|
dac886ecbd | ||
|
e5ab7a7232 | ||
|
cf4fe8e57b | ||
|
a3eea0b6ca | ||
|
5d80d44fc8 |
16 changed files with 247 additions and 58 deletions
33
.github/workflows/dev.yml
vendored
33
.github/workflows/dev.yml
vendored
|
@ -113,7 +113,40 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: ArmCordWindows.zip
|
name: ArmCordWindows.zip
|
||||||
path: dist/ArmCord-3.1.0-win.zip
|
path: dist/ArmCord-3.1.0-win.zip
|
||||||
|
build-windowsOnARM:
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set architecture
|
||||||
|
run: set npm_config_arch=arm64
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
|
||||||
|
|
||||||
|
- name: Install Node dependencies
|
||||||
|
run: pnpm install -g cargo-cp-artifact && pnpm install
|
||||||
|
|
||||||
|
- name: Install Electron-Builder
|
||||||
|
run: pnpm install -g electron-builder
|
||||||
|
|
||||||
|
- name: Replace the version number
|
||||||
|
run: (Get-Content src/utils.ts) -replace "\d\.\d\.\d", "DEV" | Out-File src/utils.ts
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: npm run build && electron-builder --windows zip --arm64
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: ArmCordWindowsArm64.zip
|
||||||
|
path: dist/ArmCord-3.1.0-win.zip
|
||||||
|
|
||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
@ -28,18 +28,21 @@
|
||||||
"homepage": "https://github.com/armcord/armcord#readme",
|
"homepage": "https://github.com/armcord/armcord#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^17.0.42",
|
"@types/node": "^17.0.42",
|
||||||
|
"@types/node-fetch": "^2.6.2",
|
||||||
"@types/ws": "^8.5.3",
|
"@types/ws": "^8.5.3",
|
||||||
"chalk-cli": "^5.0.0",
|
"chalk-cli": "^5.0.0",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"electron": "^20.1.0",
|
"electron": "^20.1.0",
|
||||||
"electron-builder": "^23.0.3",
|
"electron-builder": "^23.6.0",
|
||||||
"husky": "^8.0.1",
|
"husky": "^8.0.1",
|
||||||
"prettier": "^2.7.0",
|
"prettier": "^2.7.0",
|
||||||
"typescript": "^4.7.3"
|
"typescript": "^4.7.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pyke/vibe": "^0.3.1",
|
"@pyke/vibe": "github:pykeio/vibe#11984868ce9e007859ed91ff159c7f7f0a34e7ae",
|
||||||
"electron-context-menu": "github:ArmCord/electron-context-menu",
|
"electron-context-menu": "github:ArmCord/electron-context-menu",
|
||||||
|
"extract-zip": "^2.0.1",
|
||||||
|
"node-fetch": "v2",
|
||||||
"os-locale": "^6.0.2",
|
"os-locale": "^6.0.2",
|
||||||
"v8-compile-cache": "^2.3.0",
|
"v8-compile-cache": "^2.3.0",
|
||||||
"ws": "^8.8.0"
|
"ws": "^8.8.0"
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
lockfileVersion: 5.4
|
lockfileVersion: 5.4
|
||||||
|
|
||||||
specifiers:
|
specifiers:
|
||||||
'@pyke/vibe': ^0.3.1
|
'@pyke/vibe': github:pykeio/vibe#11984868ce9e007859ed91ff159c7f7f0a34e7ae
|
||||||
'@types/node': ^17.0.42
|
'@types/node': ^17.0.42
|
||||||
|
'@types/node-fetch': ^2.6.2
|
||||||
'@types/ws': ^8.5.3
|
'@types/ws': ^8.5.3
|
||||||
chalk-cli: ^5.0.0
|
chalk-cli: ^5.0.0
|
||||||
copyfiles: ^2.4.1
|
copyfiles: ^2.4.1
|
||||||
electron: ^20.1.0
|
electron: ^20.1.0
|
||||||
electron-builder: ^23.0.3
|
electron-builder: ^23.6.0
|
||||||
electron-context-menu: github:ArmCord/electron-context-menu
|
electron-context-menu: github:ArmCord/electron-context-menu
|
||||||
|
extract-zip: ^2.0.1
|
||||||
husky: ^8.0.1
|
husky: ^8.0.1
|
||||||
|
node-fetch: v2
|
||||||
os-locale: ^6.0.2
|
os-locale: ^6.0.2
|
||||||
prettier: ^2.7.0
|
prettier: ^2.7.0
|
||||||
typescript: ^4.7.3
|
typescript: ^4.7.3
|
||||||
|
@ -17,14 +20,17 @@ specifiers:
|
||||||
ws: ^8.8.0
|
ws: ^8.8.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@pyke/vibe': 0.3.1_electron@20.3.1
|
'@pyke/vibe': github.com/pykeio/vibe/11984868ce9e007859ed91ff159c7f7f0a34e7ae_electron@20.3.1
|
||||||
electron-context-menu: github.com/ArmCord/electron-context-menu/280c81398c02a063f46e3285a9708d8db1a7ce32
|
electron-context-menu: github.com/ArmCord/electron-context-menu/280c81398c02a063f46e3285a9708d8db1a7ce32
|
||||||
|
extract-zip: 2.0.1
|
||||||
|
node-fetch: 2.6.7
|
||||||
os-locale: 6.0.2
|
os-locale: 6.0.2
|
||||||
v8-compile-cache: 2.3.0
|
v8-compile-cache: 2.3.0
|
||||||
ws: 8.9.0
|
ws: 8.9.0
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/node': 17.0.45
|
'@types/node': 17.0.45
|
||||||
|
'@types/node-fetch': 2.6.2
|
||||||
'@types/ws': 8.5.3
|
'@types/ws': 8.5.3
|
||||||
chalk-cli: 5.0.0
|
chalk-cli: 5.0.0
|
||||||
copyfiles: 2.4.1
|
copyfiles: 2.4.1
|
||||||
|
@ -120,16 +126,6 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@pyke/vibe/0.3.1_electron@20.3.1:
|
|
||||||
resolution: {integrity: sha512-qgYtiN3cjJUdzBPHYXHILLiug//g1QXw7jLtDCi6HqCPxeIcn8zMslGlAAwS5i9e14UrHaSTUWkLEFEhWidfeQ==}
|
|
||||||
requiresBuild: true
|
|
||||||
peerDependencies:
|
|
||||||
electron: '>=11.0'
|
|
||||||
dependencies:
|
|
||||||
cargo-cp-artifact: 0.1.6
|
|
||||||
electron: 20.3.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@sindresorhus/is/0.14.0:
|
/@sindresorhus/is/0.14.0:
|
||||||
resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==}
|
resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -184,6 +180,13 @@ packages:
|
||||||
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
|
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/node-fetch/2.6.2:
|
||||||
|
resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==}
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 17.0.45
|
||||||
|
form-data: 3.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/node/16.11.64:
|
/@types/node/16.11.64:
|
||||||
resolution: {integrity: sha512-z5hPTlVFzNwtJ2LNozTpJcD1Cu44c4LNuzaq1mwxmiHWQh2ULdR6Vjwo1UGldzRpzL0yUEdZddnfqGW2G70z6Q==}
|
resolution: {integrity: sha512-z5hPTlVFzNwtJ2LNozTpJcD1Cu44c4LNuzaq1mwxmiHWQh2ULdR6Vjwo1UGldzRpzL0yUEdZddnfqGW2G70z6Q==}
|
||||||
|
|
||||||
|
@ -335,6 +338,7 @@ packages:
|
||||||
/asar/3.2.0:
|
/asar/3.2.0:
|
||||||
resolution: {integrity: sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg==}
|
resolution: {integrity: sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg==}
|
||||||
engines: {node: '>=10.12.0'}
|
engines: {node: '>=10.12.0'}
|
||||||
|
deprecated: Please use @electron/asar moving forward. There is no API change, just a package name change
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
chromium-pickle-js: 0.2.0
|
chromium-pickle-js: 0.2.0
|
||||||
|
@ -380,6 +384,7 @@ packages:
|
||||||
|
|
||||||
/base64-js/1.5.1:
|
/base64-js/1.5.1:
|
||||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||||
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/bluebird-lst/1.0.9:
|
/bluebird-lst/1.0.9:
|
||||||
|
@ -976,10 +981,12 @@ packages:
|
||||||
|
|
||||||
/fast-deep-equal/3.1.3:
|
/fast-deep-equal/3.1.3:
|
||||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/fast-json-stable-stringify/2.1.0:
|
/fast-json-stable-stringify/2.1.0:
|
||||||
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
|
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
|
||||||
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/fd-slicer/1.1.0:
|
/fd-slicer/1.1.0:
|
||||||
|
@ -1001,6 +1008,15 @@ packages:
|
||||||
path-exists: 4.0.0
|
path-exists: 4.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/form-data/3.0.1:
|
||||||
|
resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
dependencies:
|
||||||
|
asynckit: 0.4.0
|
||||||
|
combined-stream: 1.0.8
|
||||||
|
mime-types: 2.1.35
|
||||||
|
dev: true
|
||||||
|
|
||||||
/form-data/4.0.0:
|
/form-data/4.0.0:
|
||||||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
@ -1351,6 +1367,7 @@ packages:
|
||||||
|
|
||||||
/json-schema-traverse/0.4.1:
|
/json-schema-traverse/0.4.1:
|
||||||
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
||||||
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/json-stringify-safe/5.0.1:
|
/json-stringify-safe/5.0.1:
|
||||||
|
@ -1557,6 +1574,18 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/node-fetch/2.6.7:
|
||||||
|
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
|
||||||
|
engines: {node: 4.x || >=6.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
encoding: ^0.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
encoding:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
whatwg-url: 5.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/noms/0.0.0:
|
/noms/0.0.0:
|
||||||
resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==}
|
resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1692,6 +1721,7 @@ packages:
|
||||||
/punycode/2.1.1:
|
/punycode/2.1.1:
|
||||||
resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==}
|
resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/pupa/2.1.1:
|
/pupa/2.1.1:
|
||||||
|
@ -2026,6 +2056,10 @@ packages:
|
||||||
resolution: {integrity: sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==}
|
resolution: {integrity: sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
/tr46/0.0.3:
|
||||||
|
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/trim-newlines/4.0.2:
|
/trim-newlines/4.0.2:
|
||||||
resolution: {integrity: sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew==}
|
resolution: {integrity: sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
@ -2082,6 +2116,7 @@ packages:
|
||||||
|
|
||||||
/uri-js/4.4.1:
|
/uri-js/4.4.1:
|
||||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||||
|
requiresBuild: true
|
||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.1.1
|
punycode: 2.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2122,6 +2157,17 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/webidl-conversions/3.0.1:
|
||||||
|
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/whatwg-url/5.0.0:
|
||||||
|
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||||
|
dependencies:
|
||||||
|
tr46: 0.0.3
|
||||||
|
webidl-conversions: 3.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/which/2.0.2:
|
/which/2.0.2:
|
||||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
@ -2158,6 +2204,7 @@ packages:
|
||||||
/xmlbuilder/15.1.1:
|
/xmlbuilder/15.1.1:
|
||||||
resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
|
resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
|
||||||
engines: {node: '>=8.0'}
|
engines: {node: '>=8.0'}
|
||||||
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/xtend/4.0.2:
|
/xtend/4.0.2:
|
||||||
|
@ -2229,3 +2276,16 @@ packages:
|
||||||
electron-dl: 3.3.1
|
electron-dl: 3.3.1
|
||||||
electron-is-dev: 2.0.0
|
electron-is-dev: 2.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
github.com/pykeio/vibe/11984868ce9e007859ed91ff159c7f7f0a34e7ae_electron@20.3.1:
|
||||||
|
resolution: {tarball: https://codeload.github.com/pykeio/vibe/tar.gz/11984868ce9e007859ed91ff159c7f7f0a34e7ae}
|
||||||
|
id: github.com/pykeio/vibe/11984868ce9e007859ed91ff159c7f7f0a34e7ae
|
||||||
|
name: '@pyke/vibe'
|
||||||
|
version: 0.3.1
|
||||||
|
requiresBuild: true
|
||||||
|
peerDependencies:
|
||||||
|
electron: '>=11.0'
|
||||||
|
dependencies:
|
||||||
|
cargo-cp-artifact: 0.1.6
|
||||||
|
electron: 20.3.1
|
||||||
|
dev: false
|
||||||
|
|
3
src/.npmrc
Normal file
3
src/.npmrc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
node-linker=hoisted
|
||||||
|
public-hoist-pattern=*
|
||||||
|
shamefully-hoist=true
|
|
@ -53,7 +53,9 @@
|
||||||
<p class="text-center setup-ask" id="setup_question4">Select a client mod you want to install:</p>
|
<p class="text-center setup-ask" id="setup_question4">Select a client mod you want to install:</p>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<select name="mod" id="mod" class="dropdown-button">
|
<select name="mod" id="mod" class="dropdown-button">
|
||||||
<option value="none">None (check Discord)</option>
|
<option value="vencord">Vencord</option>
|
||||||
|
<option value="shelter">Shelter</option>
|
||||||
|
<option value="none">None</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-center" id="setup_question4_clientmodnotice">
|
<p class="text-center" id="setup_question4_clientmodnotice">
|
||||||
|
|
|
@ -47,6 +47,25 @@
|
||||||
console.log("ArmCord is up to date.");
|
console.log("ArmCord is up to date.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function check() {
|
||||||
|
if (armcordinternal.installState === "installing") {
|
||||||
|
text.innerHTML = "Installing mods";
|
||||||
|
} else if (armcordinternal.installState === "done") {
|
||||||
|
return true;
|
||||||
|
} else if (armcordinternal.installState === "modDownload") {
|
||||||
|
text.innerHTML = "Updating " + armcord.mods;
|
||||||
|
} else if (armcordinternal.installState === "none") {
|
||||||
|
text.innerHTML = "Nothing to install. Starting ArmCord";
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (check() == false) {
|
||||||
|
console.log("Installing");
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.armcord.splashEnd();
|
window.armcord.splashEnd();
|
||||||
switch (window.armcord.channel) {
|
switch (window.armcord.channel) {
|
||||||
|
@ -68,7 +87,7 @@
|
||||||
default:
|
default:
|
||||||
window.location.replace("https://discord.com/app");
|
window.location.replace("https://discord.com/app");
|
||||||
}
|
}
|
||||||
}, 5000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,11 @@ const unstrictCSP = () => {
|
||||||
|
|
||||||
const cspAllowAll = ["connect-src", "style-src", "img-src", "font-src"];
|
const cspAllowAll = ["connect-src", "style-src", "img-src", "font-src"];
|
||||||
|
|
||||||
const corsAllowUrls = ["https://raw.githubusercontent.com/Cordwood/builds/master/index.js"];
|
const corsAllowUrls = [
|
||||||
|
"https://raw.githubusercontent.com/Cordwood/builds/master/index.js",
|
||||||
|
"https://github.com/Vendicated/Vencord/releases/download/devbuild/browser.js",
|
||||||
|
"https://cors.armcord.xyz/raw.githubusercontent.com/uwu/shelter-builds/main/shelter.js"
|
||||||
|
];
|
||||||
|
|
||||||
electron.session.defaultSession.webRequest.onHeadersReceived(({responseHeaders, url}, done) => {
|
electron.session.defaultSession.webRequest.onHeadersReceived(({responseHeaders, url}, done) => {
|
||||||
let csp = responseHeaders!["content-security-policy"];
|
let csp = responseHeaders!["content-security-policy"];
|
||||||
|
|
|
@ -12,7 +12,7 @@ app.whenReady().then(() => {
|
||||||
const manifest = fs.readFileSync(`${userDataPath}/plugins/${file}/manifest.json`, "utf8");
|
const manifest = fs.readFileSync(`${userDataPath}/plugins/${file}/manifest.json`, "utf8");
|
||||||
var pluginFile = JSON.parse(manifest);
|
var pluginFile = JSON.parse(manifest);
|
||||||
session.defaultSession.loadExtension(`${userDataPath}/plugins/${file}`);
|
session.defaultSession.loadExtension(`${userDataPath}/plugins/${file}`);
|
||||||
console.log(`%cLoaded ${pluginFile.name} made by ${pluginFile.author}`, "color:red");
|
console.log(`[Mod loader] Loaded ${pluginFile.name} made by ${pluginFile.author}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,14 @@ import {
|
||||||
getLang,
|
getLang,
|
||||||
getWindowState,
|
getWindowState,
|
||||||
packageVersion,
|
packageVersion,
|
||||||
getDisplayVersion
|
getDisplayVersion,
|
||||||
|
modInstallState,
|
||||||
|
installModLoader
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import {customTitlebar} from "./main";
|
import {customTitlebar} from "./main";
|
||||||
import {createSettingsWindow} from "./settings/main";
|
import {createSettingsWindow} from "./settings/main";
|
||||||
import os from "os";
|
import os from "os";
|
||||||
|
import fs from "fs"
|
||||||
import path from "path";
|
import path from "path";
|
||||||
export function registerIpc() {
|
export function registerIpc() {
|
||||||
ipcMain.on("get-app-path", (event, arg) => {
|
ipcMain.on("get-app-path", (event, arg) => {
|
||||||
|
@ -73,6 +76,9 @@ export function registerIpc() {
|
||||||
ipcMain.on("displayVersion", (event) => {
|
ipcMain.on("displayVersion", (event) => {
|
||||||
event.returnValue = getDisplayVersion();
|
event.returnValue = getDisplayVersion();
|
||||||
});
|
});
|
||||||
|
ipcMain.on("modInstallState", (event) => {
|
||||||
|
event.returnValue = modInstallState;
|
||||||
|
});
|
||||||
ipcMain.on("get-package-version", (event) => {
|
ipcMain.on("get-package-version", (event) => {
|
||||||
event.returnValue = packageVersion;
|
event.returnValue = packageVersion;
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// Modules to control application life and create native browser window
|
// Modules to control application life and create native browser window
|
||||||
import {app, BrowserWindow, session} from "electron";
|
import {app, BrowserWindow, session} from "electron";
|
||||||
import "v8-compile-cache";
|
import "v8-compile-cache";
|
||||||
import {getConfig, checkIfConfigExists, injectElectronFlags} from "./utils";
|
import {getConfig, checkIfConfigExists, injectElectronFlags, installModLoader} from "./utils";
|
||||||
import "./extensions/mods";
|
import "./extensions/mods";
|
||||||
import "./extensions/plugin";
|
|
||||||
import "./tray";
|
import "./tray";
|
||||||
import {createCustomWindow, createNativeWindow, createTransparentWindow} from "./window";
|
import {createCustomWindow, createNativeWindow, createTransparentWindow, mainWindow} from "./window";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
export var iconPath: string;
|
export var iconPath: string;
|
||||||
export var settings: any;
|
export var settings: any;
|
||||||
|
@ -53,6 +52,7 @@ app.whenReady().then(async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await init();
|
await init();
|
||||||
|
await installModLoader()
|
||||||
session.fromPartition("some-partition").setPermissionRequestHandler((webContents, permission, callback) => {
|
session.fromPartition("some-partition").setPermissionRequestHandler((webContents, permission, callback) => {
|
||||||
if (permission === "notifications") {
|
if (permission === "notifications") {
|
||||||
// Approves the permissions request
|
// Approves the permissions request
|
||||||
|
|
|
@ -22,6 +22,7 @@ contextBridge.exposeInMainWorld("armcord", {
|
||||||
return result;
|
return result;
|
||||||
}),
|
}),
|
||||||
version: ipcRenderer.sendSync("get-app-version", "app-version"),
|
version: ipcRenderer.sendSync("get-app-version", "app-version"),
|
||||||
|
mods: ipcRenderer.sendSync("clientmod"),
|
||||||
packageVersion: ipcRenderer.sendSync("get-package-version", "app-version"),
|
packageVersion: ipcRenderer.sendSync("get-package-version", "app-version"),
|
||||||
getDisplayMediaSelector: getDisplayMediaSelector,
|
getDisplayMediaSelector: getDisplayMediaSelector,
|
||||||
splashEnd: () => ipcRenderer.send("splashEnd"),
|
splashEnd: () => ipcRenderer.send("splashEnd"),
|
||||||
|
@ -31,6 +32,7 @@ contextBridge.exposeInMainWorld("armcord", {
|
||||||
if (window.location.href.indexOf("splash.html") > -1 || window.location.href.indexOf("setup.html") > -1) {
|
if (window.location.href.indexOf("splash.html") > -1 || window.location.href.indexOf("setup.html") > -1) {
|
||||||
contextBridge.exposeInMainWorld("armcordinternal", {
|
contextBridge.exposeInMainWorld("armcordinternal", {
|
||||||
restart: () => ipcRenderer.send("restart"),
|
restart: () => ipcRenderer.send("restart"),
|
||||||
|
installState: ipcRenderer.sendSync("modInstallState"),
|
||||||
saveSettings: (...args: any) => ipcRenderer.send("saveSettings", ...args)
|
saveSettings: (...args: any) => ipcRenderer.send("saveSettings", ...args)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
|
import {ipcRenderer} from "electron";
|
||||||
import "./bridge";
|
import "./bridge";
|
||||||
import "./capturer";
|
import "./capturer";
|
||||||
import "./patch";
|
import "./patch";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import {injectHummusTitlebar, injectTitlebar} from "./titlebar";
|
import {injectHummusTitlebar, injectTitlebar} from "./titlebar";
|
||||||
import {sleep, addStyle, injectJS} from "../utils";
|
import {sleep, addStyle} from "../utils";
|
||||||
import {ipcRenderer} from "electron";
|
|
||||||
import {injectMobileStuff} from "./mobile";
|
import {injectMobileStuff} from "./mobile";
|
||||||
var version = ipcRenderer.sendSync("displayVersion");
|
var version = ipcRenderer.sendSync("displayVersion");
|
||||||
var channel = ipcRenderer.sendSync("channel");
|
var channel = ipcRenderer.sendSync("channel");
|
||||||
|
@ -23,10 +23,6 @@ declare global {
|
||||||
armcord: any;
|
armcord: any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const clientMods = {
|
|
||||||
goosemod: "https://api.goosemod.com/inject.js",
|
|
||||||
cordwood: "https://raw.githubusercontent.com/Cordwood/builds/master/index.js"
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log("ArmCord " + version);
|
console.log("ArmCord " + version);
|
||||||
ipcRenderer.on("themeLoader", (event, message) => {
|
ipcRenderer.on("themeLoader", (event, message) => {
|
||||||
|
@ -48,19 +44,7 @@ if (window.location.href.indexOf("splash.html") > -1) {
|
||||||
sleep(5000).then(async () => {
|
sleep(5000).then(async () => {
|
||||||
const cssPath = path.join(__dirname, "../", "/content/css/discord.css");
|
const cssPath = path.join(__dirname, "../", "/content/css/discord.css");
|
||||||
addStyle(fs.readFileSync(cssPath, "utf8"));
|
addStyle(fs.readFileSync(cssPath, "utf8"));
|
||||||
|
|
||||||
switch (ipcRenderer.sendSync("clientmod")) {
|
|
||||||
case "goosemod":
|
|
||||||
injectJS(clientMods.goosemod);
|
|
||||||
console.log("Loading GooseMod...");
|
|
||||||
await updateLang();
|
await updateLang();
|
||||||
break;
|
|
||||||
case "cordwood":
|
|
||||||
injectJS(clientMods.cordwood);
|
|
||||||
console.log("Loading Cordwood...");
|
|
||||||
await updateLang();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -74,15 +74,15 @@ export function createSettingsWindow() {
|
||||||
setConfigBulk(args);
|
setConfigBulk(args);
|
||||||
});
|
});
|
||||||
ipcMain.on("openStorageFolder", async (event) => {
|
ipcMain.on("openStorageFolder", async (event) => {
|
||||||
shell.openPath(storagePath);
|
shell.showItemInFolder(storagePath);
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
});
|
});
|
||||||
ipcMain.on("openThemesFolder", async (event) => {
|
ipcMain.on("openThemesFolder", async (event) => {
|
||||||
shell.openPath(themesPath);
|
shell.showItemInFolder(themesPath);
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
});
|
});
|
||||||
ipcMain.on("openPluginsFolder", async (event) => {
|
ipcMain.on("openPluginsFolder", async (event) => {
|
||||||
shell.openPath(pluginsPath);
|
shell.showItemInFolder(pluginsPath);
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
});
|
});
|
||||||
ipcMain.on("getLangName", async (event) => {
|
ipcMain.on("getLangName", async (event) => {
|
||||||
|
|
|
@ -123,7 +123,8 @@
|
||||||
<br />
|
<br />
|
||||||
<div class="switch acClientMod">
|
<div class="switch acClientMod">
|
||||||
<select name="mod" id="mod" class="left dropdown">
|
<select name="mod" id="mod" class="left dropdown">
|
||||||
<option value="goosemod">GooseMod</option>
|
<option value="vencord">Vencord</option>
|
||||||
|
<option value="shelter">Shelter</option>
|
||||||
<option value="none">None</option>
|
<option value="none">None</option>
|
||||||
</select>
|
</select>
|
||||||
<p class="header" id="settings-mod">Client mod</p>
|
<p class="header" id="settings-mod">Client mod</p>
|
||||||
|
@ -131,8 +132,9 @@
|
||||||
Client mods are programs that allow you customize your Discord experience. They can change appearance of
|
Client mods are programs that allow you customize your Discord experience. They can change appearance of
|
||||||
the client, modify behaviours or add new features!
|
the client, modify behaviours or add new features!
|
||||||
<br />
|
<br />
|
||||||
<b>GooseMod</b> - light, secure, and easy to use, with out of the box experience. Features a built-in
|
<b>Vencord</b> - lightweight, and easy to use client mod. Features a built-in store for plugins.
|
||||||
store for plugins.
|
<br />
|
||||||
|
<b>Shelter</b> - is a new generation client mod built to be essentially bulletproof.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
|
78
src/utils.ts
78
src/utils.ts
|
@ -1,6 +1,10 @@
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import {app, dialog} from "electron";
|
import {app, dialog, session} from "electron";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import fetch from "node-fetch"
|
||||||
|
import extract from "extract-zip"
|
||||||
|
import util from "util"
|
||||||
|
const streamPipeline = util.promisify(require('stream').pipeline)
|
||||||
export var firstRun: boolean;
|
export var firstRun: boolean;
|
||||||
export var contentPath: string;
|
export var contentPath: string;
|
||||||
export var transparency: boolean;
|
export var transparency: boolean;
|
||||||
|
@ -73,6 +77,7 @@ export function getDisplayVersion() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function injectJS(inject: string) {
|
export async function injectJS(inject: string) {
|
||||||
|
|
||||||
const js = await (await fetch(`${inject}`)).text();
|
const js = await (await fetch(`${inject}`)).text();
|
||||||
|
|
||||||
const el = document.createElement("script");
|
const el = document.createElement("script");
|
||||||
|
@ -252,11 +257,8 @@ export async function setConfig(object: string, toSet: any) {
|
||||||
fs.writeFileSync(getConfigLocation(), toSave, "utf-8");
|
fs.writeFileSync(getConfigLocation(), toSave, "utf-8");
|
||||||
}
|
}
|
||||||
export async function setConfigBulk(object: Settings) {
|
export async function setConfigBulk(object: Settings) {
|
||||||
const userDataPath = app.getPath("userData");
|
|
||||||
const storagePath = path.join(userDataPath, "/storage/");
|
|
||||||
const settingsFile = storagePath + "settings.json";
|
|
||||||
let toSave = JSON.stringify(object);
|
let toSave = JSON.stringify(object);
|
||||||
fs.writeFileSync(settingsFile, toSave, "utf-8");
|
fs.writeFileSync(getConfigLocation(), toSave, "utf-8");
|
||||||
}
|
}
|
||||||
export async function checkIfConfigExists() {
|
export async function checkIfConfigExists() {
|
||||||
const userDataPath = app.getPath("userData");
|
const userDataPath = app.getPath("userData");
|
||||||
|
@ -281,3 +283,69 @@ export async function checkIfConfigExists() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mods
|
||||||
|
async function updateModBundle() {
|
||||||
|
try {
|
||||||
|
console.log("Downloading mod bundle")
|
||||||
|
const distFolder = app.getPath("userData") + "/plugins/loader/dist/";
|
||||||
|
while (!fs.existsSync(distFolder)){
|
||||||
|
//waiting
|
||||||
|
}
|
||||||
|
var name: string = await getConfig("mods")
|
||||||
|
const clientMods = {
|
||||||
|
vencord: "https://github.com/Vendicated/Vencord/releases/download/devbuild/browser.js",
|
||||||
|
cordwood: "https://raw.githubusercontent.com/Cordwood/builds/master/index.js",
|
||||||
|
shelter: "https://raw.githubusercontent.com/uwu/shelter-builds/main/shelter.js"
|
||||||
|
};
|
||||||
|
var bundle: string = await (await fetch(clientMods[name as keyof typeof clientMods])).text()
|
||||||
|
fs.writeFileSync(distFolder + "bundle.js", bundle, "utf-8");
|
||||||
|
} catch (e) {
|
||||||
|
console.log("[Mod loader] Failed to install mods")
|
||||||
|
console.error(e)
|
||||||
|
dialog.showErrorBox(
|
||||||
|
"Oops, something went wrong.",
|
||||||
|
"ArmCord couldn't install mods, please check if you have stable internet connection and restart the app. If this issue persists, report it on the support server/Github issues."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export var modInstallState: string;
|
||||||
|
export async function installModLoader() {
|
||||||
|
if (await getConfig("mods") == "none") {
|
||||||
|
modInstallState = "none"
|
||||||
|
import("./extensions/plugin");
|
||||||
|
console.log("[Mod loader] Skipping")
|
||||||
|
} else {
|
||||||
|
const pluginFolder = app.getPath("userData") + "/plugins/";
|
||||||
|
if (!fs.existsSync(pluginFolder + "loader")) {
|
||||||
|
try {
|
||||||
|
modInstallState = "installing"
|
||||||
|
var zipPath = app.getPath("temp") + "/" + "loader.zip";
|
||||||
|
if (!fs.existsSync(pluginFolder)) {
|
||||||
|
fs.mkdirSync(pluginFolder);
|
||||||
|
console.log("[Mod loader] Created missing plugin folder");
|
||||||
|
}
|
||||||
|
var loaderZip = await fetch("https://armcord.xyz/loader.zip")
|
||||||
|
if (!loaderZip.ok) throw new Error(`unexpected response ${loaderZip.statusText}`)
|
||||||
|
await streamPipeline(loaderZip.body, fs.createWriteStream(zipPath))
|
||||||
|
await extract(zipPath, { dir: path.join(app.getPath("userData"), "plugins") })
|
||||||
|
modInstallState = "modDownload"
|
||||||
|
updateModBundle()
|
||||||
|
import("./extensions/plugin");
|
||||||
|
modInstallState = "done"
|
||||||
|
} catch(e) {
|
||||||
|
console.log("[Mod loader] Failed to install modloader")
|
||||||
|
console.error(e)
|
||||||
|
dialog.showErrorBox(
|
||||||
|
"Oops, something went wrong.",
|
||||||
|
"ArmCord couldn't install internal mod loader, please check if you have stable internet connection and restart the app. If this issue persists, report it on the support server/Github issues."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
modInstallState = "modDownload"
|
||||||
|
updateModBundle()
|
||||||
|
import("./extensions/plugin");
|
||||||
|
modInstallState = "done"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
setConfig,
|
setConfig,
|
||||||
setLang,
|
setLang,
|
||||||
setWindowState,
|
setWindowState,
|
||||||
transparency
|
transparency,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import {registerIpc} from "./ipc";
|
import {registerIpc} from "./ipc";
|
||||||
import {setMenu} from "./menu";
|
import {setMenu} from "./menu";
|
||||||
|
@ -53,7 +53,7 @@ async function doAfterDefiningTheWindow() {
|
||||||
if (transparency && process.platform === "win32") {
|
if (transparency && process.platform === "win32") {
|
||||||
import("@pyke/vibe").then(vibe => {
|
import("@pyke/vibe").then(vibe => {
|
||||||
vibe.applyEffect(mainWindow, "acrylic");
|
vibe.applyEffect(mainWindow, "acrylic");
|
||||||
vibe.setDarkMode(mainWindow);
|
vibe.forceTheme(mainWindow, 'dark');
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,9 @@ async function doAfterDefiningTheWindow() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
mainWindow.webContents.setWindowOpenHandler(({url}) => {
|
mainWindow.webContents.setWindowOpenHandler(({url}) => {
|
||||||
|
// Allow about:blank (used by Vencord QuickCss popup)
|
||||||
|
if (url === "about:blank") return { action: "allow" };
|
||||||
|
|
||||||
if (url.startsWith("https:" || url.startsWith("http:") || url.startsWith("mailto:"))) {
|
if (url.startsWith("https:" || url.startsWith("http:") || url.startsWith("mailto:"))) {
|
||||||
shell.openExternal(url);
|
shell.openExternal(url);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue