mirror of
https://github.com/element-hq/element-desktop.git
synced 2026-09-08 11:22:43 -04:00
Compare commits
72
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72dfdda0e5 | ||
|
|
679cf24a85 | ||
|
|
7f3bbc2156 | ||
|
|
5c650d6ee3 | ||
|
|
fb7a43569c | ||
|
|
7620af7f70 | ||
|
|
fe30072067 | ||
|
|
93331da979 | ||
|
|
af13eb8929 | ||
|
|
b346984ab7 | ||
|
|
a23c6162f3 | ||
|
|
82d459a95d | ||
|
|
5395fee162 | ||
|
|
c3781b9ee4 | ||
|
|
b2d77edff2 | ||
|
|
f4be3bcda2 | ||
|
|
5585292143 | ||
|
|
5a2b390f0f | ||
|
|
92e61681a9 | ||
|
|
1a25dbfd44 | ||
|
|
9beaecc350 | ||
|
|
9d74a215b7 | ||
|
|
b9ac577d98 | ||
|
|
956ae15983 | ||
|
|
e10c4a10e0 | ||
|
|
6032815f75 | ||
|
|
597524a0fb | ||
|
|
a74d22a84b | ||
|
|
c33211cf7e | ||
|
|
6eb79076c6 | ||
|
|
c000c0ce52 | ||
|
|
73fc7e957f | ||
|
|
961cfc7746 | ||
|
|
0ac568c137 | ||
|
|
629eb3958a | ||
|
|
68797d393e | ||
|
|
723ea9d5ff | ||
|
|
b1b15e1e7b | ||
|
|
c164f3cbe9 | ||
|
|
aa4c25016d | ||
|
|
16f253690a | ||
|
|
f542491467 | ||
|
|
c6d44418b5 | ||
|
|
a99f9a6aee | ||
|
|
86149805b9 | ||
|
|
0faac52dae | ||
|
|
040344eeab | ||
|
|
799b23e2a9 | ||
|
|
d766aef435 | ||
|
|
1142160222 | ||
|
|
c280560c2c | ||
|
|
19f04d4362 | ||
|
|
2f48519dce | ||
|
|
6508e171db | ||
|
|
333361fc02 | ||
|
|
9d08c84331 | ||
|
|
0b67e84574 | ||
|
|
23fac479fe | ||
|
|
56370de568 | ||
|
|
9bdb1fadd4 | ||
|
|
86a386449f | ||
|
|
b52787a49e | ||
|
|
8c91ead163 | ||
|
|
9d6008519e | ||
|
|
d3fbf1366b | ||
|
|
f4bf425148 | ||
|
|
19351d766d | ||
|
|
017721ecca | ||
|
|
5d0f3921b4 | ||
|
|
ee28079c2b | ||
|
|
7c87fff295 | ||
|
|
f5fa0ae7be |
No files matched your search
+14
-16
@@ -1,8 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: ["matrix-org"],
|
plugins: ["matrix-org"],
|
||||||
extends: [
|
extends: ["plugin:matrix-org/javascript"],
|
||||||
"plugin:matrix-org/javascript",
|
|
||||||
],
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 2021,
|
ecmaVersion: 2021,
|
||||||
},
|
},
|
||||||
@@ -20,19 +18,19 @@ module.exports = {
|
|||||||
"prefer-promise-reject-errors": "off",
|
"prefer-promise-reject-errors": "off",
|
||||||
"no-async-promise-executor": "off",
|
"no-async-promise-executor": "off",
|
||||||
},
|
},
|
||||||
overrides: [{
|
overrides: [
|
||||||
files: ["{src,scripts,hak}/**/*.{ts,tsx}"],
|
{
|
||||||
extends: [
|
files: ["{src,scripts,hak}/**/*.{ts,tsx}"],
|
||||||
"plugin:matrix-org/typescript",
|
extends: ["plugin:matrix-org/typescript"],
|
||||||
],
|
rules: {
|
||||||
rules: {
|
// Things we do that break the ideal style
|
||||||
// Things we do that break the ideal style
|
"prefer-promise-reject-errors": "off",
|
||||||
"prefer-promise-reject-errors": "off",
|
"quotes": "off",
|
||||||
"quotes": "off",
|
|
||||||
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
// We're okay with assertion errors when we ask for them
|
// We're okay with assertion errors when we ask for them
|
||||||
"@typescript-eslint/no-non-null-assertion": "off",
|
"@typescript-eslint/no-non-null-assertion": "off",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}],
|
],
|
||||||
};
|
};
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
* [ ] Ensure your code works with manual testing
|
- [ ] Ensure your code works with manual testing
|
||||||
* [ ] Linter and other CI checks pass
|
- [ ] Linter and other CI checks pass
|
||||||
* [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/vector-im/element-desktop/blob/develop/CONTRIBUTING.md))
|
- [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/vector-im/element-desktop/blob/develop/CONTRIBUTING.md))
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
If you would like to specify text for the changelog entry other than your PR title, add the following:
|
If you would like to specify text for the changelog entry other than your PR title, add the following:
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": [
|
"extends": ["github>matrix-org/renovate-config-element-web"]
|
||||||
"github>matrix-org/renovate-config-element-web"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
name: Backport
|
name: Backport
|
||||||
on:
|
on:
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types:
|
types:
|
||||||
- closed
|
- closed
|
||||||
- labeled
|
- labeled
|
||||||
branches:
|
branches:
|
||||||
- develop
|
- develop
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
backport:
|
backport:
|
||||||
name: Backport
|
name: Backport
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Only react to merged PRs for security reasons.
|
# Only react to merged PRs for security reasons.
|
||||||
# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target.
|
# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target.
|
||||||
if: >
|
if: >
|
||||||
github.event.pull_request.merged
|
github.event.pull_request.merged
|
||||||
&& (
|
&& (
|
||||||
github.event.action == 'closed'
|
github.event.action == 'closed'
|
||||||
|| (
|
|| (
|
||||||
github.event.action == 'labeled'
|
github.event.action == 'labeled'
|
||||||
&& contains(github.event.label.name, 'backport')
|
&& contains(github.event.label.name, 'backport')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
steps:
|
steps:
|
||||||
- uses: tibdex/backport@v2
|
- uses: tibdex/backport@v2
|
||||||
with:
|
with:
|
||||||
labels_template: "<%= JSON.stringify([...labels, 'X-Release-Blocker']) %>"
|
labels_template: "<%= JSON.stringify([...labels, 'X-Release-Blocker']) %>"
|
||||||
# We can't use GITHUB_TOKEN here or CI won't run on the new PR
|
# We can't use GITHUB_TOKEN here or CI won't run on the new PR
|
||||||
github_token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
github_token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
name: Build and Test
|
name: Build and Test
|
||||||
on:
|
on:
|
||||||
pull_request: { }
|
pull_request: {}
|
||||||
push:
|
push:
|
||||||
branches: [ develop, master ]
|
branches: [develop, master]
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
uses: ./.github/workflows/build_windows.yaml
|
uses: ./.github/workflows/build_windows.yaml
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
arch: [ x64, x86 ]
|
arch: [x64, x86]
|
||||||
with:
|
with:
|
||||||
arch: ${{ matrix.arch }}
|
arch: ${{ matrix.arch }}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
uses: ./.github/workflows/build_linux.yaml
|
uses: ./.github/workflows/build_linux.yaml
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
sqlcipher: [ system, static ]
|
sqlcipher: [system, static]
|
||||||
with:
|
with:
|
||||||
sqlcipher: ${{ matrix.sqlcipher }}
|
sqlcipher: ${{ matrix.sqlcipher }}
|
||||||
|
|
||||||
@@ -46,18 +46,17 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# Disable macOS tests for now, they fail to run in CI, needs investigation.
|
- name: macOS Universal
|
||||||
# - name: macOS Universal
|
os: macos
|
||||||
# os: macos
|
artifact: macos
|
||||||
# artifact: macos
|
executable: "./dist/mac-universal/Element.app/Contents/MacOS/Element"
|
||||||
# executable: "./dist/mac-universal/Element.app/Contents/MacOS/Element"
|
prepare_cmd: "find ./dist/mac-universal/Element.app -type f | perl -lne 'print if -B' | tr '\\n' '\\0' | xargs -0 -n1 chmod 755"
|
||||||
# prepare_cmd: "chmod +x ./dist/mac-universal/Element.app/Contents/MacOS/Element"
|
- name: "Linux (sqlcipher: system)"
|
||||||
- name: 'Linux (sqlcipher: system)'
|
|
||||||
os: ubuntu
|
os: ubuntu
|
||||||
artifact: linux-sqlcipher-system
|
artifact: linux-sqlcipher-system
|
||||||
executable: "element-desktop"
|
executable: "element-desktop"
|
||||||
prepare_cmd: "sudo apt install ./dist/*.deb"
|
prepare_cmd: "sudo apt install ./dist/*.deb"
|
||||||
- name: 'Linux (sqlcipher: static)'
|
- name: "Linux (sqlcipher: static)"
|
||||||
os: ubuntu
|
os: ubuntu
|
||||||
artifact: linux-sqlcipher-static
|
artifact: linux-sqlcipher-static
|
||||||
executable: "element-desktop"
|
executable: "element-desktop"
|
||||||
@@ -101,6 +100,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Artifacts
|
- name: Upload Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.artifact }}
|
name: ${{ matrix.artifact }}
|
||||||
path: test_artifacts
|
path: test_artifacts
|
||||||
|
|||||||
@@ -1,58 +1,64 @@
|
|||||||
|
# This workflow relies on actions/cache to store the hak dependency artifacts as they take a long time to build
|
||||||
|
# Due to this extra care must be taken to only ever run all build_* scripts against the same branch to ensure
|
||||||
|
# the correct cache scoping, and additional care must be taken to not run untrusted actions on the develop branch.
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
inputs:
|
||||||
sqlcipher:
|
sqlcipher:
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: "How to link sqlcipher, one of 'system' | 'static'"
|
description: "How to link sqlcipher, one of 'system' | 'static'"
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: webapp
|
name: webapp
|
||||||
|
|
||||||
- name: Cache .hak
|
- name: Cache .hak
|
||||||
uses: actions/cache@v3
|
id: cache
|
||||||
with:
|
uses: actions/cache@v3
|
||||||
key: ${{ hashFiles('./yarn.lock') }}
|
with:
|
||||||
path: |
|
key: ${{ runner.os }}-${{ hashFiles('hakDependencies.json', 'electronVersion') }}
|
||||||
./.hak
|
path: |
|
||||||
|
./.hak
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rs/toolchain@v1
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
with:
|
uses: actions-rs/toolchain@v1
|
||||||
toolchain: stable
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
|
||||||
- name: Install libsqlcipher-dev
|
- name: Install libsqlcipher-dev
|
||||||
if: inputs.sqlcipher == 'system'
|
if: steps.cache.outputs.cache-hit != 'true' && inputs.sqlcipher == 'system'
|
||||||
run: sudo apt-get install -y libsqlcipher-dev
|
run: sudo apt-get install -y libsqlcipher-dev
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
cache: "yarn"
|
cache: "yarn"
|
||||||
|
|
||||||
# Does not need branch matching as only analyses this layer
|
# Does not need branch matching as only analyses this layer
|
||||||
- name: Install Deps
|
- name: Install Deps
|
||||||
run: "yarn install --pure-lockfile"
|
run: "yarn install --pure-lockfile"
|
||||||
|
|
||||||
- name: Build Natives
|
- name: Build Natives
|
||||||
run: "yarn build:native"
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
env:
|
run: "yarn build:native"
|
||||||
SQLCIPHER_STATIC: ${{ inputs.sqlcipher == 'static' && '1' || '' }}
|
env:
|
||||||
|
SQLCIPHER_STATIC: ${{ inputs.sqlcipher == 'static' && '1' || '' }}
|
||||||
|
|
||||||
- name: Build App
|
- name: Build App
|
||||||
run: "yarn build --publish never -l"
|
run: "yarn build --publish never -l"
|
||||||
|
|
||||||
- name: Install .deb
|
- name: Install .deb
|
||||||
run: "sudo apt install ./dist/*.deb"
|
run: "sudo apt install ./dist/*.deb"
|
||||||
|
|
||||||
- name: Upload Artifacts
|
- name: Upload Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: linux-sqlcipher-${{ inputs.sqlcipher }}
|
name: linux-sqlcipher-${{ inputs.sqlcipher }}
|
||||||
path: dist
|
path: dist
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
@@ -1,45 +1,51 @@
|
|||||||
|
# This workflow relies on actions/cache to store the hak dependency artifacts as they take a long time to build
|
||||||
|
# Due to this extra care must be taken to only ever run all build_* scripts against the same branch to ensure
|
||||||
|
# the correct cache scoping, and additional care must be taken to not run untrusted actions on the develop branch.
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: webapp
|
name: webapp
|
||||||
|
|
||||||
- name: Cache .hak
|
- name: Cache .hak
|
||||||
uses: actions/cache@v3
|
id: cache
|
||||||
with:
|
uses: actions/cache@v3
|
||||||
key: ${{ hashFiles('./yarn.lock') }}
|
with:
|
||||||
path: |
|
key: ${{ runner.os }}-${{ hashFiles('hakDependencies.json', 'electronVersion') }}
|
||||||
./.hak
|
path: |
|
||||||
|
./.hak
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rs/toolchain@v1
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
with:
|
uses: actions-rs/toolchain@v1
|
||||||
toolchain: stable
|
with:
|
||||||
target: aarch64-apple-darwin
|
toolchain: stable
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
cache: "yarn"
|
cache: "yarn"
|
||||||
|
|
||||||
# Does not need branch matching as only analyses this layer
|
# Does not need branch matching as only analyses this layer
|
||||||
- name: Install Deps
|
- name: Install Deps
|
||||||
run: "yarn install --pure-lockfile"
|
run: "yarn install --pure-lockfile"
|
||||||
|
|
||||||
- name: Build Natives
|
- name: Build Natives
|
||||||
run: "yarn build:native:universal"
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
run: "yarn build:native:universal"
|
||||||
|
|
||||||
- name: Build App
|
- name: Build App
|
||||||
run: "yarn build:universal --publish never"
|
run: "yarn build:universal --publish never"
|
||||||
|
|
||||||
- name: Upload Artifacts
|
- name: Upload Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: macos
|
name: macos
|
||||||
path: dist
|
path: dist
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
@@ -1,35 +1,43 @@
|
|||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
inputs:
|
||||||
config:
|
config:
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: "The config directory to use"
|
description: "The config directory to use"
|
||||||
version:
|
version:
|
||||||
type: string
|
type: string
|
||||||
required: false
|
required: false
|
||||||
description: "The version tag to fetch, or 'develop', will pick automatically if not passed"
|
description: "The version tag to fetch, or 'develop', will pick automatically if not passed"
|
||||||
jobs:
|
jobs:
|
||||||
prepare:
|
prepare:
|
||||||
name: Prepare
|
name: Prepare
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
cache: "yarn"
|
cache: "yarn"
|
||||||
|
|
||||||
- name: Install Deps
|
- name: Install Deps
|
||||||
run: "yarn install --pure-lockfile"
|
run: "yarn install --pure-lockfile"
|
||||||
|
|
||||||
- name: Fetch Element Web
|
- name: Fetch Element Web
|
||||||
run: yarn run fetch --noverify -d ${{ inputs.config }} ${{ inputs.version }}
|
run: yarn run fetch --noverify -d ${{ inputs.config }} ${{ inputs.version }}
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
# We split this out to save the build_* scripts having to do it to make use of `hashFiles` in the cache action
|
||||||
with:
|
- name: Generate cache hash files
|
||||||
name: webapp
|
run: |
|
||||||
retention-days: 1
|
yarn run --silent electron --version > electronVersion
|
||||||
path: |
|
cat package.json | jq -c .hakDependencies > hakDependencies.json
|
||||||
webapp.asar
|
|
||||||
package.json
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: webapp
|
||||||
|
retention-days: 1
|
||||||
|
path: |
|
||||||
|
webapp.asar
|
||||||
|
package.json
|
||||||
|
electronVersion
|
||||||
|
hakDependencies.json
|
||||||
@@ -1,87 +1,93 @@
|
|||||||
|
# This workflow relies on actions/cache to store the hak dependency artifacts as they take a long time to build
|
||||||
|
# Due to this extra care must be taken to only ever run all build_* scripts against the same branch to ensure
|
||||||
|
# the correct cache scoping, and additional care must be taken to not run untrusted actions on the develop branch.
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
inputs:
|
||||||
arch:
|
arch:
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: "The architecture to build for, one of 'x64' | 'x86'"
|
description: "The architecture to build for, one of 'x64' | 'x86'"
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: kanga333/variable-mapper@master
|
- uses: kanga333/variable-mapper@master
|
||||||
id: config
|
id: config
|
||||||
with:
|
with:
|
||||||
key: "${{ inputs.arch }}"
|
key: "${{ inputs.arch }}"
|
||||||
export_to: output
|
export_to: output
|
||||||
map: |
|
map: |
|
||||||
{
|
{
|
||||||
"x64": {
|
"x64": {
|
||||||
"target": "x86_64-pc-windows-msvc"
|
"target": "x86_64-pc-windows-msvc"
|
||||||
},
|
},
|
||||||
"x86": {
|
"x86": {
|
||||||
"target": "i686-pc-windows-msvc",
|
"target": "i686-pc-windows-msvc",
|
||||||
"build-args": "--ia32"
|
"build-args": "--ia32"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: webapp
|
name: webapp
|
||||||
|
|
||||||
- name: Cache .hak
|
- name: Cache .hak
|
||||||
uses: actions/cache@v3
|
id: cache
|
||||||
with:
|
uses: actions/cache@v3
|
||||||
key: ${{ runner.os }}-${{ hashFiles('./yarn.lock') }}
|
with:
|
||||||
path: |
|
key: ${{ runner.os }}-${{ hashFiles('hakDependencies.json', 'electronVersion') }}
|
||||||
./.hak
|
path: |
|
||||||
|
./.hak
|
||||||
|
|
||||||
- name: Set up build tools
|
- name: Set up build tools
|
||||||
uses: ilammy/msvc-dev-cmd@v1
|
uses: ilammy/msvc-dev-cmd@v1
|
||||||
with:
|
with:
|
||||||
arch: ${{ inputs.arch }}
|
arch: ${{ inputs.arch }}
|
||||||
|
|
||||||
# ActiveTCL package on choco is from 2015,
|
# ActiveTCL package on choco is from 2015,
|
||||||
# this one is newer but includes more than we need
|
# this one is newer but includes more than we need
|
||||||
- name: Choco install tclsh
|
- name: Choco install tclsh
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
choco install -y magicsplat-tcl-tk --no-progress
|
choco install -y magicsplat-tcl-tk --no-progress
|
||||||
echo "${HOME}/AppData/Local/Apps/Tcl86/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
echo "${HOME}/AppData/Local/Apps/Tcl86/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||||
|
|
||||||
- name: Choco install NetWide Assembler
|
- name: Choco install NetWide Assembler
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
choco install -y nasm --no-progress
|
choco install -y nasm --no-progress
|
||||||
echo "C:/Program Files/NASM" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
echo "C:/Program Files/NASM" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rs/toolchain@v1
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
with:
|
uses: actions-rs/toolchain@v1
|
||||||
toolchain: stable
|
with:
|
||||||
target: ${{ steps.config.outputs.target }}
|
toolchain: stable
|
||||||
|
target: ${{ steps.config.outputs.target }}
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
cache: "yarn"
|
cache: "yarn"
|
||||||
|
|
||||||
# Does not need branch matching as only analyses this layer
|
# Does not need branch matching as only analyses this layer
|
||||||
- name: Install Deps
|
- name: Install Deps
|
||||||
run: "yarn install --pure-lockfile"
|
run: "yarn install --pure-lockfile"
|
||||||
|
|
||||||
- name: Build Natives
|
- name: Build Natives
|
||||||
run: |
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
refreshenv
|
run: |
|
||||||
yarn build:native --target ${{ steps.config.outputs.target }}
|
refreshenv
|
||||||
|
yarn build:native --target ${{ steps.config.outputs.target }}
|
||||||
|
|
||||||
- name: Build App
|
- name: Build App
|
||||||
run: "yarn build --publish never -w ${{ steps.config.outputs.build-args }}"
|
run: "yarn build --publish never -w ${{ steps.config.outputs.build-args }}"
|
||||||
|
|
||||||
- name: Upload Artifacts
|
- name: Upload Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: win-${{ inputs.arch }}
|
name: win-${{ inputs.arch }}
|
||||||
path: dist
|
path: dist
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
@@ -1,43 +1,43 @@
|
|||||||
name: Generate packages.element.io directory indexes
|
name: Generate packages.element.io directory indexes
|
||||||
on:
|
on:
|
||||||
# Trigger a rebuild of all indexes if the template gets updated
|
# Trigger a rebuild of all indexes if the template gets updated
|
||||||
push:
|
push:
|
||||||
branches: [ develop ]
|
branches: [develop]
|
||||||
paths:
|
paths:
|
||||||
- 'packages.element.io/**'
|
- "packages.element.io/**"
|
||||||
# Trigger a daily rebuild for nightlies
|
# Trigger a daily rebuild for nightlies
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 11 * * *'
|
- cron: "0 11 * * *"
|
||||||
# Manual trigger for rebuilding for releases
|
# Manual trigger for rebuilding for releases
|
||||||
workflow_dispatch: { }
|
workflow_dispatch: {}
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
name: "Deploy"
|
name: "Deploy"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
environment: develop
|
environment: packages.element.io
|
||||||
env:
|
|
||||||
R2_BUCKET: 'packages-element-io'
|
|
||||||
R2_URL: ${{ secrets.CF_R2_S3_API }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
cache: "yarn"
|
|
||||||
|
|
||||||
- name: Install Deps
|
|
||||||
run: "yarn install --pure-lockfile"
|
|
||||||
|
|
||||||
- name: Copy static files
|
|
||||||
if: github.event_name == 'push'
|
|
||||||
run: aws s3 cp --recursive packages.element.io/ s3://$R2_BUCKET/ --endpoint-url $R2_URL --region auto
|
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
|
R2_BUCKET: "packages-element-io"
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_TOKEN }}
|
R2_URL: ${{ secrets.CF_R2_S3_API }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Generate directory indexes
|
- uses: actions/setup-node@v3
|
||||||
run: scripts/generate-packages-index.ts
|
with:
|
||||||
env:
|
cache: "yarn"
|
||||||
CF_R2_S3_API: ${{ secrets.CF_R2_S3_API }}
|
|
||||||
CF_R2_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
|
- name: Install Deps
|
||||||
CF_R2_TOKEN: ${{ secrets.CF_R2_TOKEN }}
|
run: "yarn install --pure-lockfile"
|
||||||
|
|
||||||
|
- name: Copy static files
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
run: aws s3 cp --recursive packages.element.io/ s3://$R2_BUCKET/ --endpoint-url $R2_URL --region auto
|
||||||
|
env:
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_TOKEN }}
|
||||||
|
|
||||||
|
- name: Generate directory indexes
|
||||||
|
run: scripts/generate-packages-index.ts
|
||||||
|
env:
|
||||||
|
CF_R2_S3_API: ${{ secrets.CF_R2_S3_API }}
|
||||||
|
CF_R2_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
|
||||||
|
CF_R2_TOKEN: ${{ secrets.CF_R2_TOKEN }}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
name: Pull Request
|
name: Pull Request
|
||||||
on:
|
on:
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [ opened, edited, labeled, unlabeled, synchronize ]
|
types: [opened, edited, labeled, unlabeled, synchronize]
|
||||||
concurrency: ${{ github.workflow }}-${{ github.event.pull_request.head.ref }}
|
concurrency: ${{ github.workflow }}-${{ github.event.pull_request.head.ref }}
|
||||||
jobs:
|
jobs:
|
||||||
action:
|
action:
|
||||||
uses: matrix-org/matrix-js-sdk/.github/workflows/pull_request.yaml@develop
|
uses: matrix-org/matrix-js-sdk/.github/workflows/pull_request.yaml@develop
|
||||||
with:
|
with:
|
||||||
labels: "T-Defect,T-Enhancement,T-Task"
|
labels: "T-Defect,T-Enhancement,T-Task"
|
||||||
secrets:
|
secrets:
|
||||||
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
@@ -1,43 +1,43 @@
|
|||||||
name: Static Analysis
|
name: Static Analysis
|
||||||
on:
|
on:
|
||||||
pull_request: { }
|
pull_request: {}
|
||||||
push:
|
push:
|
||||||
branches: [ develop, master ]
|
branches: [develop, master]
|
||||||
jobs:
|
jobs:
|
||||||
ts_lint:
|
ts_lint:
|
||||||
name: "Typescript Syntax Check"
|
name: "Typescript Syntax Check"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
cache: 'yarn'
|
cache: "yarn"
|
||||||
|
|
||||||
# Does not need branch matching as only analyses this layer
|
# Does not need branch matching as only analyses this layer
|
||||||
- name: Install Deps
|
- name: Install Deps
|
||||||
run: "yarn install --pure-lockfile"
|
run: "yarn install --pure-lockfile"
|
||||||
|
|
||||||
- name: Typecheck
|
- name: Typecheck
|
||||||
run: "yarn run lint:types"
|
run: "yarn run lint:types"
|
||||||
|
|
||||||
i18n_lint:
|
i18n_lint:
|
||||||
name: "i18n Check"
|
name: "i18n Check"
|
||||||
uses: matrix-org/matrix-react-sdk/.github/workflows/i18n_check.yml@develop
|
uses: matrix-org/matrix-react-sdk/.github/workflows/i18n_check.yml@develop
|
||||||
|
|
||||||
js_lint:
|
js_lint:
|
||||||
name: "ESLint"
|
name: "ESLint"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
cache: 'yarn'
|
cache: "yarn"
|
||||||
|
|
||||||
# Does not need branch matching as only analyses this layer
|
# Does not need branch matching as only analyses this layer
|
||||||
- name: Install Deps
|
- name: Install Deps
|
||||||
run: "yarn install --pure-lockfile"
|
run: "yarn install --pure-lockfile"
|
||||||
|
|
||||||
- name: Run Linter
|
- name: Run Linter
|
||||||
run: "yarn run lint:js"
|
run: "yarn run lint:js"
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
name: Upgrade Dependencies
|
name: Upgrade Dependencies
|
||||||
on:
|
on:
|
||||||
workflow_dispatch: { }
|
workflow_dispatch: {}
|
||||||
jobs:
|
jobs:
|
||||||
upgrade:
|
upgrade:
|
||||||
uses: matrix-org/matrix-js-sdk/.github/workflows/upgrade_dependencies.yml@develop
|
uses: matrix-org/matrix-js-sdk/.github/workflows/upgrade_dependencies.yml@develop
|
||||||
secrets:
|
secrets:
|
||||||
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
@@ -14,3 +14,6 @@ node_modules/
|
|||||||
.vscode/
|
.vscode/
|
||||||
/test_artifacts/
|
/test_artifacts/
|
||||||
/coverage/
|
/coverage/
|
||||||
|
yarn-error.log
|
||||||
|
/hak/**/*.js
|
||||||
|
/scripts/hak/**/*.js
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/build/
|
||||||
|
/dockerbuild/
|
||||||
|
/lib/
|
||||||
|
/node_modules/
|
||||||
|
/packages.elememt.io/
|
||||||
|
/webapp
|
||||||
|
/src/i18n/strings
|
||||||
|
/CHANGELOG.md
|
||||||
|
/package-lock.json
|
||||||
|
/yarn.lock
|
||||||
|
|
||||||
|
**/.idea
|
||||||
|
.vscode
|
||||||
|
.vscode/
|
||||||
|
.tmp
|
||||||
|
.env
|
||||||
|
/coverage
|
||||||
|
/.npmrc
|
||||||
|
/*.log
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require("eslint-plugin-matrix-org/.prettierrc.js");
|
||||||
+184
@@ -1,3 +1,187 @@
|
|||||||
|
Changes in [1.11.22](https://github.com/vector-im/element-desktop/releases/tag/v1.11.22) (2023-01-31)
|
||||||
|
=====================================================================================================
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
* Bump version number to fix problems upgrading from v1.11.21-rc.1
|
||||||
|
|
||||||
|
Changes in [1.11.21](https://github.com/vector-im/element-desktop/releases/tag/v1.11.21) (2023-01-31)
|
||||||
|
=====================================================================================================
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
* Move pin drop out of labs ([\#22993](https://github.com/vector-im/element-web/pull/22993)).
|
||||||
|
* Quotes for rte ([\#9932](https://github.com/matrix-org/matrix-react-sdk/pull/9932)). Contributed by @alunturner.
|
||||||
|
* Show the room name in the room header during calls ([\#9942](https://github.com/matrix-org/matrix-react-sdk/pull/9942)). Fixes vector-im/element-web#24268.
|
||||||
|
* Add code blocks to rich text editor ([\#9921](https://github.com/matrix-org/matrix-react-sdk/pull/9921)). Contributed by @alunturner.
|
||||||
|
* Add new style for inline code ([\#9936](https://github.com/matrix-org/matrix-react-sdk/pull/9936)). Contributed by @florianduros.
|
||||||
|
* Add disabled button state to rich text editor ([\#9930](https://github.com/matrix-org/matrix-react-sdk/pull/9930)). Contributed by @alunturner.
|
||||||
|
* Change the rageshake "app" for auto-rageshakes ([\#9909](https://github.com/matrix-org/matrix-react-sdk/pull/9909)).
|
||||||
|
* Device manager - tweak settings display ([\#9905](https://github.com/matrix-org/matrix-react-sdk/pull/9905)). Contributed by @kerryarchibald.
|
||||||
|
* Add list functionality to rich text editor ([\#9871](https://github.com/matrix-org/matrix-react-sdk/pull/9871)). Contributed by @alunturner.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
* Fix RTE focus behaviour in threads ([\#9969](https://github.com/matrix-org/matrix-react-sdk/pull/9969)). Fixes vector-im/element-web#23755. Contributed by @florianduros.
|
||||||
|
* #22204 Issue: Centered File info in lightbox ([\#9971](https://github.com/matrix-org/matrix-react-sdk/pull/9971)). Fixes vector-im/element-web#22204. Contributed by @Spartan09.
|
||||||
|
* Fix seekbar position for zero length audio ([\#9949](https://github.com/matrix-org/matrix-react-sdk/pull/9949)). Fixes vector-im/element-web#24248.
|
||||||
|
* Allow thread panel to be closed after being opened from notification ([\#9937](https://github.com/matrix-org/matrix-react-sdk/pull/9937)). Fixes vector-im/element-web#23764 vector-im/element-web#23852 and vector-im/element-web#24213. Contributed by @justjanne.
|
||||||
|
* Only highlight focused menu item if focus is supposed to be visible ([\#9945](https://github.com/matrix-org/matrix-react-sdk/pull/9945)). Fixes vector-im/element-web#23582.
|
||||||
|
* Prevent call durations from breaking onto multiple lines ([\#9944](https://github.com/matrix-org/matrix-react-sdk/pull/9944)).
|
||||||
|
* Tweak call lobby buttons to more closely match designs ([\#9943](https://github.com/matrix-org/matrix-react-sdk/pull/9943)).
|
||||||
|
* Do not show a broadcast as live immediately after the recording has stopped ([\#9947](https://github.com/matrix-org/matrix-react-sdk/pull/9947)). Fixes vector-im/element-web#24233.
|
||||||
|
* Clear the RTE before sending a message ([\#9948](https://github.com/matrix-org/matrix-react-sdk/pull/9948)). Contributed by @florianduros.
|
||||||
|
* Fix {enter} press in RTE ([\#9927](https://github.com/matrix-org/matrix-react-sdk/pull/9927)). Contributed by @florianduros.
|
||||||
|
* Fix the problem that the password reset email has to be confirmed twice ([\#9926](https://github.com/matrix-org/matrix-react-sdk/pull/9926)). Fixes vector-im/element-web#24226.
|
||||||
|
* replace .at() with array.length-1 ([\#9933](https://github.com/matrix-org/matrix-react-sdk/pull/9933)). Fixes matrix-org/element-web-rageshakes#19281.
|
||||||
|
* Fix broken threads list timestamp layout ([\#9922](https://github.com/matrix-org/matrix-react-sdk/pull/9922)). Fixes vector-im/element-web#24243 and vector-im/element-web#24191. Contributed by @justjanne.
|
||||||
|
* Disable multiple messages when {enter} is pressed multiple times ([\#9929](https://github.com/matrix-org/matrix-react-sdk/pull/9929)). Fixes vector-im/element-web#24249. Contributed by @florianduros.
|
||||||
|
* Fix logout devices when resetting the password ([\#9925](https://github.com/matrix-org/matrix-react-sdk/pull/9925)). Fixes vector-im/element-web#24228.
|
||||||
|
* Fix: Poll replies overflow when not enough space ([\#9924](https://github.com/matrix-org/matrix-react-sdk/pull/9924)). Fixes vector-im/element-web#24227. Contributed by @kerryarchibald.
|
||||||
|
* State event updates are not forwarded to the widget from invitation room ([\#9802](https://github.com/matrix-org/matrix-react-sdk/pull/9802)). Contributed by @maheichyk.
|
||||||
|
* Fix error when viewing source of redacted events ([\#9914](https://github.com/matrix-org/matrix-react-sdk/pull/9914)). Fixes vector-im/element-web#24165. Contributed by @clarkf.
|
||||||
|
* Replace outdated css attribute ([\#9912](https://github.com/matrix-org/matrix-react-sdk/pull/9912)). Fixes vector-im/element-web#24218. Contributed by @justjanne.
|
||||||
|
* Clear isLogin theme override when user is no longer viewing login screens ([\#9911](https://github.com/matrix-org/matrix-react-sdk/pull/9911)). Fixes vector-im/element-web#23893.
|
||||||
|
* Fix reply action in message context menu notif & file panels ([\#9895](https://github.com/matrix-org/matrix-react-sdk/pull/9895)). Fixes vector-im/element-web#23970.
|
||||||
|
* Fix issue where thread dropdown would not show up correctly ([\#9872](https://github.com/matrix-org/matrix-react-sdk/pull/9872)). Fixes vector-im/element-web#24040. Contributed by @justjanne.
|
||||||
|
* Fix unexpected composer growing ([\#9889](https://github.com/matrix-org/matrix-react-sdk/pull/9889)). Contributed by @florianduros.
|
||||||
|
* Fix misaligned timestamps for thread roots which are emotes ([\#9875](https://github.com/matrix-org/matrix-react-sdk/pull/9875)). Fixes vector-im/element-web#23897. Contributed by @justjanne.
|
||||||
|
|
||||||
|
Changes in [1.11.20](https://github.com/vector-im/element-desktop/releases/tag/v1.11.20) (2023-01-20)
|
||||||
|
=====================================================================================================
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
* (no effect on Element Desktop) (Part 2) fix crash on browsers that don't support `Array.at`
|
||||||
|
|
||||||
|
Changes in [1.11.19](https://github.com/vector-im/element-desktop/releases/tag/v1.11.19) (2023-01-20)
|
||||||
|
=====================================================================================================
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
* (no effect on Element Desktop) Fix crash on browsers that don't support `Array.at` ([\#9935](https://github.com/matrix-org/matrix-react-sdk/pull/9935)). Contributed by @andybalaam.
|
||||||
|
|
||||||
|
Changes in [1.11.18](https://github.com/vector-im/element-desktop/releases/tag/v1.11.18) (2023-01-18)
|
||||||
|
=====================================================================================================
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
* Switch threads on for everyone ([\#9879](https://github.com/matrix-org/matrix-react-sdk/pull/9879)).
|
||||||
|
* Make threads use new Unable to Decrypt UI ([\#9876](https://github.com/matrix-org/matrix-react-sdk/pull/9876)). Fixes vector-im/element-web#24060.
|
||||||
|
* Add edit and remove actions to link in RTE ([\#9864](https://github.com/matrix-org/matrix-react-sdk/pull/9864)).
|
||||||
|
* Remove extensible events v1 experimental rendering ([\#9881](https://github.com/matrix-org/matrix-react-sdk/pull/9881)).
|
||||||
|
* Make create poll dialog scale better (PSG-929) ([\#9873](https://github.com/matrix-org/matrix-react-sdk/pull/9873)). Fixes vector-im/element-web#21855.
|
||||||
|
* Change RTE mode icons ([\#9861](https://github.com/matrix-org/matrix-react-sdk/pull/9861)).
|
||||||
|
* Device manager - prune client information events after remote sign out ([\#9874](https://github.com/matrix-org/matrix-react-sdk/pull/9874)).
|
||||||
|
* Check connection before starting broadcast ([\#9857](https://github.com/matrix-org/matrix-react-sdk/pull/9857)).
|
||||||
|
* Enable sent receipt for poll start events (PSG-962) ([\#9870](https://github.com/matrix-org/matrix-react-sdk/pull/9870)).
|
||||||
|
* Change clear notifications to have more readable copy ([\#9867](https://github.com/matrix-org/matrix-react-sdk/pull/9867)).
|
||||||
|
* Combine search results when the query is present in multiple successive messages ([\#9855](https://github.com/matrix-org/matrix-react-sdk/pull/9855)). Fixes vector-im/element-web#3977. Contributed by @grimhilt.
|
||||||
|
* Disable bubbles for broadcasts ([\#9860](https://github.com/matrix-org/matrix-react-sdk/pull/9860)). Fixes vector-im/element-web#24140.
|
||||||
|
* Enable reactions and replies for broadcasts ([\#9856](https://github.com/matrix-org/matrix-react-sdk/pull/9856)). Fixes vector-im/element-web#24042.
|
||||||
|
* Improve switching between rich and plain editing modes ([\#9776](https://github.com/matrix-org/matrix-react-sdk/pull/9776)).
|
||||||
|
* Redesign the picture-in-picture window ([\#9800](https://github.com/matrix-org/matrix-react-sdk/pull/9800)). Fixes vector-im/element-web#23980.
|
||||||
|
* User on-boarding tasks now appear in a static order. ([\#9799](https://github.com/matrix-org/matrix-react-sdk/pull/9799)). Contributed by @GoodGuyMarco.
|
||||||
|
* Device manager - contextual menus ([\#9832](https://github.com/matrix-org/matrix-react-sdk/pull/9832)).
|
||||||
|
* If listening a non-live broadcast and changing the room, the broadcast will be paused ([\#9825](https://github.com/matrix-org/matrix-react-sdk/pull/9825)). Fixes vector-im/element-web#24078.
|
||||||
|
* Consider own broadcasts from other device as a playback ([\#9821](https://github.com/matrix-org/matrix-react-sdk/pull/9821)). Fixes vector-im/element-web#24068.
|
||||||
|
* Add link creation to rich text editor ([\#9775](https://github.com/matrix-org/matrix-react-sdk/pull/9775)).
|
||||||
|
* Add mark as read option in room setting ([\#9798](https://github.com/matrix-org/matrix-react-sdk/pull/9798)). Fixes vector-im/element-web#24053.
|
||||||
|
* Device manager - current device design and copy tweaks ([\#9801](https://github.com/matrix-org/matrix-react-sdk/pull/9801)).
|
||||||
|
* Unify notifications panel event design ([\#9754](https://github.com/matrix-org/matrix-react-sdk/pull/9754)).
|
||||||
|
* Add actions for integration manager to send and read certain events ([\#9740](https://github.com/matrix-org/matrix-react-sdk/pull/9740)).
|
||||||
|
* Device manager - design tweaks ([\#9768](https://github.com/matrix-org/matrix-react-sdk/pull/9768)).
|
||||||
|
* Change room list sorting to activity and unread first by default ([\#9773](https://github.com/matrix-org/matrix-react-sdk/pull/9773)). Fixes vector-im/element-web#24014.
|
||||||
|
* Add a config flag to enable the rust crypto-sdk ([\#9759](https://github.com/matrix-org/matrix-react-sdk/pull/9759)).
|
||||||
|
* Improve decryption error UI by consolidating error messages and providing instructions when possible ([\#9544](https://github.com/matrix-org/matrix-react-sdk/pull/9544)). Contributed by @duxovni.
|
||||||
|
* Honor font settings in Element Call ([\#9751](https://github.com/matrix-org/matrix-react-sdk/pull/9751)). Fixes vector-im/element-web#23661.
|
||||||
|
* Device manager - use deleteAccountData to prune device manager client information events ([\#9734](https://github.com/matrix-org/matrix-react-sdk/pull/9734)).
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
* Display rooms & threads as unread (bold) if threads have unread messages. ([\#9763](https://github.com/matrix-org/matrix-react-sdk/pull/9763)). Fixes vector-im/element-web#23907.
|
||||||
|
* Don't prefer STIXGeneral over the default font ([\#9711](https://github.com/matrix-org/matrix-react-sdk/pull/9711)). Fixes vector-im/element-web#23899.
|
||||||
|
* Use the same avatar colour when creating 1:1 DM rooms ([\#9850](https://github.com/matrix-org/matrix-react-sdk/pull/9850)). Fixes vector-im/element-web#23476.
|
||||||
|
* Fix space lock icon size ([\#9854](https://github.com/matrix-org/matrix-react-sdk/pull/9854)). Fixes vector-im/element-web#24128.
|
||||||
|
* Make calls automatically disconnect if the widget disappears ([\#9862](https://github.com/matrix-org/matrix-react-sdk/pull/9862)). Fixes vector-im/element-web#23664.
|
||||||
|
* Fix emoji in RTE editing ([\#9827](https://github.com/matrix-org/matrix-react-sdk/pull/9827)).
|
||||||
|
* Fix export with attachments on formats txt and json ([\#9851](https://github.com/matrix-org/matrix-react-sdk/pull/9851)). Fixes vector-im/element-web#24130. Contributed by @grimhilt.
|
||||||
|
* Fixed empty `Content-Type` for encrypted uploads ([\#9848](https://github.com/matrix-org/matrix-react-sdk/pull/9848)). Contributed by @K3das.
|
||||||
|
* Fix sign-in instead link on password reset page ([\#9820](https://github.com/matrix-org/matrix-react-sdk/pull/9820)). Fixes vector-im/element-web#24087.
|
||||||
|
* The seekbar now initially shows the current position ([\#9796](https://github.com/matrix-org/matrix-react-sdk/pull/9796)). Fixes vector-im/element-web#24051.
|
||||||
|
* Fix: Editing a poll will silently change it to a closed poll ([\#9809](https://github.com/matrix-org/matrix-react-sdk/pull/9809)). Fixes vector-im/element-web#23176.
|
||||||
|
* Make call tiles look less broken in the right panel ([\#9808](https://github.com/matrix-org/matrix-react-sdk/pull/9808)). Fixes vector-im/element-web#23716.
|
||||||
|
* Prevent unnecessary m.direct updates ([\#9805](https://github.com/matrix-org/matrix-react-sdk/pull/9805)). Fixes vector-im/element-web#24059.
|
||||||
|
* Fix checkForPreJoinUISI for thread roots ([\#9803](https://github.com/matrix-org/matrix-react-sdk/pull/9803)). Fixes vector-im/element-web#24054.
|
||||||
|
* Snap in PiP widget when content changed ([\#9797](https://github.com/matrix-org/matrix-react-sdk/pull/9797)). Fixes vector-im/element-web#24050.
|
||||||
|
* Load RTE components only when RTE labs is enabled ([\#9804](https://github.com/matrix-org/matrix-react-sdk/pull/9804)).
|
||||||
|
* Ensure that events are correctly updated when they are edited. ([\#9789](https://github.com/matrix-org/matrix-react-sdk/pull/9789)).
|
||||||
|
* When stopping a broadcast also stop the playback ([\#9795](https://github.com/matrix-org/matrix-react-sdk/pull/9795)). Fixes vector-im/element-web#24052.
|
||||||
|
* Prevent to start two broadcasts at the same time ([\#9744](https://github.com/matrix-org/matrix-react-sdk/pull/9744)). Fixes vector-im/element-web#23973.
|
||||||
|
* Correctly handle limited sync responses by resetting the thread timeline ([\#3056](https://github.com/matrix-org/matrix-js-sdk/pull/3056)). Fixes vector-im/element-web#23952.
|
||||||
|
* Fix failure to start in firefox private browser ([\#3058](https://github.com/matrix-org/matrix-js-sdk/pull/3058)). Fixes vector-im/element-web#24216.
|
||||||
|
|
||||||
|
Changes in [1.11.17](https://github.com/vector-im/element-desktop/releases/tag/v1.11.17) (2022-12-21)
|
||||||
|
=====================================================================================================
|
||||||
|
|
||||||
|
## 🚨 BREAKING CHANGES
|
||||||
|
* This allows the update server to be entirely static, such as a CDN or object store, as defined at https ([\#461](https://github.com/vector-im/element-desktop/pull/461)).
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
* Enable threads by default ([\#9736](https://github.com/matrix-org/matrix-react-sdk/pull/9736)). Fixes vector-im/element-web#19270 vector-im/element-web#21910 and vector-im/element-web#23946.
|
||||||
|
* Add inline code formatting to rich text editor ([\#9720](https://github.com/matrix-org/matrix-react-sdk/pull/9720)).
|
||||||
|
* Add emoji handling for plain text mode of the new rich text editor ([\#9727](https://github.com/matrix-org/matrix-react-sdk/pull/9727)).
|
||||||
|
* Overlay virtual room call events into main timeline ([\#9626](https://github.com/matrix-org/matrix-react-sdk/pull/9626)). Fixes vector-im/element-web#22929.
|
||||||
|
* Adds a new section under "Room Settings" > "Roles & Permissions" which adds the possibility to multiselect users from this room and grant them more permissions. ([\#9596](https://github.com/matrix-org/matrix-react-sdk/pull/9596)). Contributed by @GoodGuyMarco.
|
||||||
|
* Add emoji handling for rich text mode ([\#9661](https://github.com/matrix-org/matrix-react-sdk/pull/9661)).
|
||||||
|
* Add setting to hide bold notifications ([\#9705](https://github.com/matrix-org/matrix-react-sdk/pull/9705)).
|
||||||
|
* Further password reset flow enhancements ([\#9662](https://github.com/matrix-org/matrix-react-sdk/pull/9662)).
|
||||||
|
* Snooze the bulk unverified sessions reminder on dismiss ([\#9706](https://github.com/matrix-org/matrix-react-sdk/pull/9706)).
|
||||||
|
* Honor advanced audio processing settings when recording voice messages ([\#9610](https://github.com/matrix-org/matrix-react-sdk/pull/9610)). Contributed by @MrAnno.
|
||||||
|
* Improve the visual balance of bubble layout ([\#9704](https://github.com/matrix-org/matrix-react-sdk/pull/9704)).
|
||||||
|
* Add config setting to disable bulk unverified sessions nag ([\#9657](https://github.com/matrix-org/matrix-react-sdk/pull/9657)).
|
||||||
|
* Only display bulk unverified sessions nag when current sessions is verified ([\#9656](https://github.com/matrix-org/matrix-react-sdk/pull/9656)).
|
||||||
|
* Separate labs and betas more clearly ([\#8969](https://github.com/matrix-org/matrix-react-sdk/pull/8969)). Fixes vector-im/element-web#22706.
|
||||||
|
* Show user an error if we fail to create a DM for verification. ([\#9624](https://github.com/matrix-org/matrix-react-sdk/pull/9624)).
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
* Prevent unnecessary m.direct updates ([\#9805](https://github.com/matrix-org/matrix-react-sdk/pull/9805)). Fixes vector-im/element-web#24059.
|
||||||
|
* Fix checkForPreJoinUISI for thread roots ([\#9803](https://github.com/matrix-org/matrix-react-sdk/pull/9803)). Fixes vector-im/element-web#24054.
|
||||||
|
* Load RTE components only when RTE labs is enabled ([\#9804](https://github.com/matrix-org/matrix-react-sdk/pull/9804)).
|
||||||
|
* Fix issue where thread panel did not update correctly ([\#9746](https://github.com/matrix-org/matrix-react-sdk/pull/9746)). Fixes vector-im/element-web#23971.
|
||||||
|
* Remove async call to get virtual room from room load ([\#9743](https://github.com/matrix-org/matrix-react-sdk/pull/9743)). Fixes vector-im/element-web#23968.
|
||||||
|
* Check each thread for unread messages. ([\#9723](https://github.com/matrix-org/matrix-react-sdk/pull/9723)).
|
||||||
|
* Device manage - handle sessions that don't support encryption ([\#9717](https://github.com/matrix-org/matrix-react-sdk/pull/9717)). Fixes vector-im/element-web#23722.
|
||||||
|
* Fix hover state for formatting buttons (Rich text editor) (fix vector-im/element-web/issues/23832) ([\#9715](https://github.com/matrix-org/matrix-react-sdk/pull/9715)).
|
||||||
|
* Don't allow group calls to be unterminated ([\#9710](https://github.com/matrix-org/matrix-react-sdk/pull/9710)).
|
||||||
|
* Fix replies to emotes not showing as inline ([\#9707](https://github.com/matrix-org/matrix-react-sdk/pull/9707)). Fixes vector-im/element-web#23903.
|
||||||
|
* Update copy of 'Change layout' button to match Element Call ([\#9703](https://github.com/matrix-org/matrix-react-sdk/pull/9703)).
|
||||||
|
* Fix call splitbrains when switching between rooms ([\#9692](https://github.com/matrix-org/matrix-react-sdk/pull/9692)).
|
||||||
|
* bugfix: fix an issue where the Notifier would incorrectly fire for non-timeline events ([\#9664](https://github.com/matrix-org/matrix-react-sdk/pull/9664)). Fixes vector-im/element-web#17263.
|
||||||
|
* Fix power selector being wrongly disabled for admins themselves ([\#9681](https://github.com/matrix-org/matrix-react-sdk/pull/9681)). Fixes vector-im/element-web#23882.
|
||||||
|
* Show day counts in call durations ([\#9641](https://github.com/matrix-org/matrix-react-sdk/pull/9641)).
|
||||||
|
|
||||||
|
Changes in [1.11.16](https://github.com/vector-im/element-desktop/releases/tag/v1.11.16) (2022-12-06)
|
||||||
|
=====================================================================================================
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
* Update to Electron 21 ([\#458](https://github.com/vector-im/element-desktop/pull/458)). Fixes vector-im/element-web#23783.
|
||||||
|
* Further improve replies ([\#6396](https://github.com/matrix-org/matrix-react-sdk/pull/6396)). Fixes vector-im/element-web#19074, vector-im/element-web#18194 vector-im/element-web#18027 and vector-im/element-web#19179.
|
||||||
|
* Enable users to join group calls from multiple devices ([\#9625](https://github.com/matrix-org/matrix-react-sdk/pull/9625)).
|
||||||
|
* fix(visual): make cursor a pointer for summaries ([\#9419](https://github.com/matrix-org/matrix-react-sdk/pull/9419)). Contributed by @r00ster91.
|
||||||
|
* Add placeholder for rich text editor ([\#9613](https://github.com/matrix-org/matrix-react-sdk/pull/9613)).
|
||||||
|
* Consolidate public room search experience ([\#9605](https://github.com/matrix-org/matrix-react-sdk/pull/9605)). Fixes vector-im/element-web#22846.
|
||||||
|
* New password reset flow ([\#9581](https://github.com/matrix-org/matrix-react-sdk/pull/9581)). Fixes vector-im/element-web#23131.
|
||||||
|
* Device manager - add tooltip to device details toggle ([\#9594](https://github.com/matrix-org/matrix-react-sdk/pull/9594)).
|
||||||
|
* sliding sync: add lazy-loading member support ([\#9530](https://github.com/matrix-org/matrix-react-sdk/pull/9530)).
|
||||||
|
* Limit formatting bar offset to top of composer ([\#9365](https://github.com/matrix-org/matrix-react-sdk/pull/9365)). Fixes vector-im/element-web#12359. Contributed by @owi92.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
* Fix issues around up arrow event edit shortcut ([\#9645](https://github.com/matrix-org/matrix-react-sdk/pull/9645)). Fixes vector-im/element-web#18497 and vector-im/element-web#18964.
|
||||||
|
* Fix search not being cleared when clicking on a result ([\#9635](https://github.com/matrix-org/matrix-react-sdk/pull/9635)). Fixes vector-im/element-web#23845.
|
||||||
|
* Fix screensharing in 1:1 calls ([\#9612](https://github.com/matrix-org/matrix-react-sdk/pull/9612)). Fixes vector-im/element-web#23808.
|
||||||
|
* Fix the background color flashing when joining a call ([\#9640](https://github.com/matrix-org/matrix-react-sdk/pull/9640)).
|
||||||
|
* Fix the size of the 'Private space' icon ([\#9638](https://github.com/matrix-org/matrix-react-sdk/pull/9638)).
|
||||||
|
* Fix reply editing in rich text editor (https ([\#9615](https://github.com/matrix-org/matrix-react-sdk/pull/9615)).
|
||||||
|
* Fix thread list jumping back down while scrolling ([\#9606](https://github.com/matrix-org/matrix-react-sdk/pull/9606)). Fixes vector-im/element-web#23727.
|
||||||
|
* Fix regression with TimelinePanel props updates not taking effect ([\#9608](https://github.com/matrix-org/matrix-react-sdk/pull/9608)). Fixes vector-im/element-web#23794.
|
||||||
|
* Fix form tooltip positioning ([\#9598](https://github.com/matrix-org/matrix-react-sdk/pull/9598)). Fixes vector-im/element-web#22861.
|
||||||
|
* Extract Search handling from RoomView into its own Component ([\#9574](https://github.com/matrix-org/matrix-react-sdk/pull/9574)). Fixes vector-im/element-web#498.
|
||||||
|
* Fix call splitbrains when switching between rooms ([\#9692](https://github.com/matrix-org/matrix-react-sdk/pull/9692)).
|
||||||
|
|
||||||
Changes in [1.11.15](https://github.com/vector-im/element-desktop/releases/tag/v1.11.15) (2022-11-22)
|
Changes in [1.11.15](https://github.com/vector-im/element-desktop/releases/tag/v1.11.15) (2022-11-22)
|
||||||
=====================================================================================================
|
=====================================================================================================
|
||||||
|
|
||||||
|
|||||||
@@ -5,21 +5,20 @@
|
|||||||
[](https://sonarcloud.io/summary/new_code?id=element-desktop)
|
[](https://sonarcloud.io/summary/new_code?id=element-desktop)
|
||||||
[](https://sonarcloud.io/summary/new_code?id=element-desktop)
|
[](https://sonarcloud.io/summary/new_code?id=element-desktop)
|
||||||
|
|
||||||
Element Desktop
|
# Element Desktop
|
||||||
===============
|
|
||||||
|
|
||||||
Element Desktop is a Matrix client for desktop platforms with Element Web at its core.
|
Element Desktop is a Matrix client for desktop platforms with Element Web at its core.
|
||||||
|
|
||||||
First Steps
|
# First Steps
|
||||||
===========
|
|
||||||
Before you do anything else, fetch the dependencies:
|
Before you do anything else, fetch the dependencies:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn install
|
yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
Fetching Element
|
# Fetching Element
|
||||||
================
|
|
||||||
Since this package is just the Electron wrapper for Element Web, it doesn't contain any of the Element Web code,
|
Since this package is just the Electron wrapper for Element Web, it doesn't contain any of the Element Web code,
|
||||||
so the first step is to get a working copy of Element Web. There are a few ways of doing this:
|
so the first step is to get a working copy of Element Web. There are a few ways of doing this:
|
||||||
|
|
||||||
@@ -31,6 +30,7 @@ yarn run fetch --noverify --cfgdir ""
|
|||||||
```
|
```
|
||||||
|
|
||||||
...or if you'd like to use GPG to verify the downloaded package:
|
...or if you'd like to use GPG to verify the downloaded package:
|
||||||
|
|
||||||
```
|
```
|
||||||
# Fetch the Element public key from the element.io web server over a secure connection and import
|
# Fetch the Element public key from the element.io web server over a secure connection and import
|
||||||
# it into your local GPG keychain (you'll need GPG installed). You only need to to do this
|
# it into your local GPG keychain (you'll need GPG installed). You only need to to do this
|
||||||
@@ -41,6 +41,7 @@ yarn run fetch --cfgdir ""
|
|||||||
```
|
```
|
||||||
|
|
||||||
...or either of the above, but fetching a specific version of Element:
|
...or either of the above, but fetching a specific version of Element:
|
||||||
|
|
||||||
```
|
```
|
||||||
# Fetch the prebuilt release Element package from the element-web GitHub releases page. The version
|
# Fetch the prebuilt release Element package from the element-web GitHub releases page. The version
|
||||||
# fetched will be the same as the local element-desktop package.
|
# fetched will be the same as the local element-desktop package.
|
||||||
@@ -49,6 +50,7 @@ yarn run fetch --noverify --cfgdir "" v1.5.6
|
|||||||
|
|
||||||
If you only want to run the app locally and don't need to build packages, you can
|
If you only want to run the app locally and don't need to build packages, you can
|
||||||
provide the `webapp` directory directly:
|
provide the `webapp` directory directly:
|
||||||
|
|
||||||
```
|
```
|
||||||
# Assuming you've checked out and built a copy of element-web in ../element-web
|
# Assuming you've checked out and built a copy of element-web in ../element-web
|
||||||
ln -s ../element-web/webapp ./
|
ln -s ../element-web/webapp ./
|
||||||
@@ -56,29 +58,32 @@ ln -s ../element-web/webapp ./
|
|||||||
|
|
||||||
[TODO: add support for fetching develop builds, arbitrary URLs and arbitrary paths]
|
[TODO: add support for fetching develop builds, arbitrary URLs and arbitrary paths]
|
||||||
|
|
||||||
Building
|
# Building
|
||||||
========
|
|
||||||
|
|
||||||
## Native Build
|
## Native Build
|
||||||
|
|
||||||
TODO: List native pre-requisites
|
TODO: List native pre-requisites
|
||||||
|
|
||||||
Optionally, [build the native modules](https://github.com/vector-im/element-desktop/blob/develop/docs/native-node-modules.md),
|
Optionally, [build the native modules](https://github.com/vector-im/element-desktop/blob/develop/docs/native-node-modules.md),
|
||||||
which include support for searching in encrypted rooms and secure storage. Skipping this step is fine, you just won't have those features.
|
which include support for searching in encrypted rooms and secure storage. Skipping this step is fine, you just won't have those features.
|
||||||
|
|
||||||
Then, run
|
Then, run
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build
|
yarn run build
|
||||||
```
|
```
|
||||||
|
|
||||||
This will do a couple of things:
|
This will do a couple of things:
|
||||||
* Run the `setversion` script to set the local package version to match whatever
|
|
||||||
version of Element you installed above.
|
- Run the `setversion` script to set the local package version to match whatever
|
||||||
* Run electron-builder to build a package. The package built will match the operating system
|
version of Element you installed above.
|
||||||
you're running the build process on.
|
- Run electron-builder to build a package. The package built will match the operating system
|
||||||
|
you're running the build process on.
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
Alternatively, you can also build using docker, which will always produce the linux package:
|
Alternatively, you can also build using docker, which will always produce the linux package:
|
||||||
|
|
||||||
```
|
```
|
||||||
# Run this once to make the docker image
|
# Run this once to make the docker image
|
||||||
yarn run docker:setup
|
yarn run docker:setup
|
||||||
@@ -91,9 +96,10 @@ yarn run docker:build
|
|||||||
|
|
||||||
After running, the packages should be in `dist/`.
|
After running, the packages should be in `dist/`.
|
||||||
|
|
||||||
Starting
|
# Starting
|
||||||
========
|
|
||||||
If you'd just like to run the electron app locally for development:
|
If you'd just like to run the electron app locally for development:
|
||||||
|
|
||||||
```
|
```
|
||||||
# Install electron - we don't normally need electron itself as it's provided
|
# Install electron - we don't normally need electron itself as it's provided
|
||||||
# by electron-builder when building packages
|
# by electron-builder when building packages
|
||||||
@@ -101,21 +107,22 @@ yarn add electron
|
|||||||
yarn start
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
Config
|
# Config
|
||||||
======
|
|
||||||
If you'd like the packaged Element to have a configuration file, you can create a
|
If you'd like the packaged Element to have a configuration file, you can create a
|
||||||
config directory and place `config.json` in there, then specify this directory
|
config directory and place `config.json` in there, then specify this directory
|
||||||
with the `--cfgdir` option to `yarn run fetch`, eg:
|
with the `--cfgdir` option to `yarn run fetch`, eg:
|
||||||
|
|
||||||
```
|
```
|
||||||
mkdir myconfig
|
mkdir myconfig
|
||||||
cp /path/to/my/config.json myconfig/
|
cp /path/to/my/config.json myconfig/
|
||||||
yarn run fetch --cfgdir myconfig
|
yarn run fetch --cfgdir myconfig
|
||||||
```
|
```
|
||||||
|
|
||||||
The config dir for the official Element app is in `element.io`. If you use this,
|
The config dir for the official Element app is in `element.io`. If you use this,
|
||||||
your app will auto-update itself using builds from element.io.
|
your app will auto-update itself using builds from element.io.
|
||||||
|
|
||||||
Profiles
|
# Profiles
|
||||||
========
|
|
||||||
|
|
||||||
To run multiple instances of the desktop app for different accounts, you can
|
To run multiple instances of the desktop app for different accounts, you can
|
||||||
launch the executable with the `--profile` argument followed by a unique
|
launch the executable with the `--profile` argument followed by a unique
|
||||||
@@ -125,20 +132,18 @@ not interfere with the default one.
|
|||||||
Alternatively, a custom location for the profile data can be specified using the
|
Alternatively, a custom location for the profile data can be specified using the
|
||||||
`--profile-dir` flag followed by the desired path.
|
`--profile-dir` flag followed by the desired path.
|
||||||
|
|
||||||
User-specified config.json
|
# User-specified config.json
|
||||||
==========================
|
|
||||||
|
|
||||||
+ `%APPDATA%\$NAME\config.json` on Windows
|
- `%APPDATA%\$NAME\config.json` on Windows
|
||||||
+ `$XDG_CONFIG_HOME/$NAME/config.json` or `~/.config/$NAME/config.json` on Linux
|
- `$XDG_CONFIG_HOME/$NAME/config.json` or `~/.config/$NAME/config.json` on Linux
|
||||||
+ `~/Library/Application Support/$NAME/config.json` on macOS
|
- `~/Library/Application Support/$NAME/config.json` on macOS
|
||||||
|
|
||||||
In the paths above, `$NAME` is typically `Element`, unless you use `--profile
|
In the paths above, `$NAME` is typically `Element`, unless you use `--profile
|
||||||
$PROFILE` in which case it becomes `Element-$PROFILE`, or it is using one of
|
$PROFILE` in which case it becomes `Element-$PROFILE`, or it is using one of
|
||||||
the above created by a pre-1.7 install, in which case it will be `Riot` or
|
the above created by a pre-1.7 install, in which case it will be `Riot` or
|
||||||
`Riot-$PROFILE`.
|
`Riot-$PROFILE`.
|
||||||
|
|
||||||
Translations
|
# Translations
|
||||||
==========================
|
|
||||||
|
|
||||||
To add a new translation, head to the [translating doc](https://github.com/vector-im/element-web/blob/develop/docs/translating.md).
|
To add a new translation, head to the [translating doc](https://github.com/vector-im/element-web/blob/develop/docs/translating.md).
|
||||||
|
|
||||||
@@ -146,9 +151,8 @@ For a developer guide, see the [translating dev doc](https://github.com/vector-i
|
|||||||
|
|
||||||
[<img src="https://translate.element.io/widgets/element-desktop/-/multi-auto.svg" alt="translationsstatus" width="340">](https://translate.element.io/engage/element-desktop/?utm_source=widget)
|
[<img src="https://translate.element.io/widgets/element-desktop/-/multi-auto.svg" alt="translationsstatus" width="340">](https://translate.element.io/engage/element-desktop/?utm_source=widget)
|
||||||
|
|
||||||
Report bugs & give feedback
|
# Report bugs & give feedback
|
||||||
==========================
|
|
||||||
|
|
||||||
If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.
|
If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.
|
||||||
|
|
||||||
To help avoid duplicate issues, please [view existing issues](https://github.com/vector-im/element-web/issues?q=is%3Aopen+is%3Aissue+sort%3Areactions-%2B1-desc) first (and add a +1) or [create a new issue](https://github.com/vector-im/element-web/issues/new/choose) if you can't find it. Please note that this issue tracker is associated with the [element-web](https://github.com/vector-im/element-web) repo, but is also applied to the code in this repo as well.
|
To help avoid duplicate issues, please [view existing issues](https://github.com/vector-im/element-web/issues?q=is%3Aopen+is%3Aissue+sort%3Areactions-%2B1-desc) first (and add a +1) or [create a new issue](https://github.com/vector-im/element-web/issues/new/choose) if you can't find it. Please note that this issue tracker is associated with the [element-web](https://github.com/vector-im/element-web) repo, but is also applied to the code in this repo as well.
|
||||||
+1
-4
@@ -1,6 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-typescript"],
|
||||||
['@babel/preset-env', { targets: { node: 'current' } }],
|
|
||||||
'@babel/preset-typescript',
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
@@ -17,13 +17,14 @@ when releasing.
|
|||||||
|
|
||||||
Install the pre-requisites for your system:
|
Install the pre-requisites for your system:
|
||||||
|
|
||||||
* [Windows pre-requisites](https://github.com/vector-im/element-desktop/blob/develop/docs/windows-requirements.md)
|
- [Windows pre-requisites](https://github.com/vector-im/element-desktop/blob/develop/docs/windows-requirements.md)
|
||||||
* Linux: TODO
|
- Linux: TODO
|
||||||
* OS X: TODO
|
- OS X: TODO
|
||||||
|
|
||||||
Then optionally, [add seshat and dependencies to support search in E2E rooms](#adding-seshat-for-search-in-e2e-encrypted-rooms).
|
Then optionally, [add seshat and dependencies to support search in E2E rooms](#adding-seshat-for-search-in-e2e-encrypted-rooms).
|
||||||
|
|
||||||
Then, to build for an architecture selected automatically based on your system (recommended), run:
|
Then, to build for an architecture selected automatically based on your system (recommended), run:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build:native
|
yarn run build:native
|
||||||
```
|
```
|
||||||
@@ -70,7 +71,7 @@ as usual using:
|
|||||||
|
|
||||||
On Windows & macOS we always statically link libsqlcipher for it is not generally available.
|
On Windows & macOS we always statically link libsqlcipher for it is not generally available.
|
||||||
On Linux by default we will use a system package, on debian & ubuntu this is `libsqlcipher0`,
|
On Linux by default we will use a system package, on debian & ubuntu this is `libsqlcipher0`,
|
||||||
but this is problematic for some other packages.
|
but this is problematic for some other packages.
|
||||||
By including `SQLCIPHER_STATIC=1` in the build environment, the build scripts will statically link sqlcipher,
|
By including `SQLCIPHER_STATIC=1` in the build environment, the build scripts will statically link sqlcipher,
|
||||||
note that this will want a `libcrypto1.1` shared library available in the system.
|
note that this will want a `libcrypto1.1` shared library available in the system.
|
||||||
|
|
||||||
@@ -82,15 +83,19 @@ and https://github.com/vector-im/element-web/issues/20926.
|
|||||||
### macOS
|
### macOS
|
||||||
|
|
||||||
On macOS, you can build universal native modules too:
|
On macOS, you can build universal native modules too:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build:native:universal
|
yarn run build:native:universal
|
||||||
```
|
```
|
||||||
|
|
||||||
...or you can build for a specific architecture:
|
...or you can build for a specific architecture:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build:native --target x86_64-apple-darwin
|
yarn run build:native --target x86_64-apple-darwin
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build:native --target aarch64-apple-darwin
|
yarn run build:native --target aarch64-apple-darwin
|
||||||
```
|
```
|
||||||
@@ -105,10 +110,13 @@ yarn run build:universal
|
|||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
If you're on Windows, you can choose to build specifically for 32 or 64 bit:
|
If you're on Windows, you can choose to build specifically for 32 or 64 bit:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build:32
|
yarn run build:32
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn run build:64
|
yarn run build:64
|
||||||
```
|
```
|
||||||
@@ -144,6 +152,6 @@ The current set of native modules are stored in `.hak/hakModules`,
|
|||||||
so you can use this to check what architecture is currently in place, eg:
|
so you can use this to check what architecture is currently in place, eg:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ lipo -info .hak/hakModules/keytar/build/Release/keytar.node
|
$ lipo -info .hak/hakModules/keytar/build/Release/keytar.node
|
||||||
Architectures in the fat file: .hak/hakModules/keytar/build/Release/keytar.node are: x86_64 arm64
|
Architectures in the fat file: .hak/hakModules/keytar/build/Release/keytar.node are: x86_64 arm64
|
||||||
```
|
```
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
The Desktop app is capable of self-updating on macOS and Windows.
|
||||||
|
The update server base url is configurable as `update_base_url` in config.json and can be served by a static file host,
|
||||||
|
CDN or object storage.
|
||||||
|
|
||||||
|
Currently all packaging & deployment is handled by https://github.com/vector-im/element-builder/
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
|
||||||
|
On Windows the update mechanism used is [Squirrel.Windows](https://github.com/Squirrel/Squirrel.Windows)
|
||||||
|
and can be served by any compatible Squirrel server, such as https://github.com/Tiliq/squirrel-server
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
|
||||||
|
On macOS the update mechanism used is [Squirrel.Mac](https://github.com/Squirrel/Squirrel.Mac)
|
||||||
|
using the newer JSON format as documented [here](https://github.com/Squirrel/Squirrel.Mac#update-file-json-format).
|
||||||
@@ -4,23 +4,24 @@
|
|||||||
|
|
||||||
If you want to build native modules, make sure that the following tools are installed on your system.
|
If you want to build native modules, make sure that the following tools are installed on your system.
|
||||||
|
|
||||||
- [Git for Windows](https://git-scm.com/download/win)
|
- [Git for Windows](https://git-scm.com/download/win)
|
||||||
- [Node 14](https://nodejs.org)
|
- [Node 14](https://nodejs.org)
|
||||||
- [Python 3](https://www.python.org/downloads/) (if you type 'python' into command prompt it will offer to install it from the windows store)
|
- [Python 3](https://www.python.org/downloads/) (if you type 'python' into command prompt it will offer to install it from the windows store)
|
||||||
- [Strawberry Perl](https://strawberryperl.com/)
|
- [Strawberry Perl](https://strawberryperl.com/)
|
||||||
- [Rustup](https://rustup.rs/)
|
- [Rustup](https://rustup.rs/)
|
||||||
- [NASM](https://www.nasm.us/)
|
- [NASM](https://www.nasm.us/)
|
||||||
- [Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019) with the following configuration:
|
- [Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019) with the following configuration:
|
||||||
- On the Workloads tab:
|
- On the Workloads tab:
|
||||||
- Desktop & Mobile -> C++ build tools
|
- Desktop & Mobile -> C++ build tools
|
||||||
- On the Individual components tab:
|
- On the Individual components tab:
|
||||||
- MSVC VS 2019 C++ build tools
|
- MSVC VS 2019 C++ build tools
|
||||||
- Windows 10 SDK (latest version available)
|
- Windows 10 SDK (latest version available)
|
||||||
- C++ CMake tools for Windows
|
- C++ CMake tools for Windows
|
||||||
|
|
||||||
Once installed make sure all those utilities are accessible in your `PATH`.
|
Once installed make sure all those utilities are accessible in your `PATH`.
|
||||||
|
|
||||||
If you want to be able to build x86 targets from an x64 host install the right toolchain:
|
If you want to be able to build x86 targets from an x64 host install the right toolchain:
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
rustup toolchain install stable-i686-pc-windows-msvc
|
rustup toolchain install stable-i686-pc-windows-msvc
|
||||||
rustup target add i686-pc-windows-msvc
|
rustup target add i686-pc-windows-msvc
|
||||||
|
|||||||
@@ -16,11 +16,7 @@
|
|||||||
"uisi_autorageshake_app": "element-auto-uisi",
|
"uisi_autorageshake_app": "element-auto-uisi",
|
||||||
"showLabsSettings": true,
|
"showLabsSettings": true,
|
||||||
"roomDirectory": {
|
"roomDirectory": {
|
||||||
"servers": [
|
"servers": ["matrix.org", "gitter.im", "libera.chat"]
|
||||||
"matrix.org",
|
|
||||||
"gitter.im",
|
|
||||||
"libera.chat"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"enable_presence_by_hs_url": {
|
"enable_presence_by_hs_url": {
|
||||||
"https://matrix.org": false,
|
"https://matrix.org": false,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Vendor: support@element.io
|
|||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
Maintainer: support@element.io
|
Maintainer: support@element.io
|
||||||
Depends: libgtk-3-0, libnotify4, libnss3, libxss1, libxtst6, xdg-utils, libatspi2.0-0, libuuid1, libsecret-1-0, libasound2, libgbm1
|
Depends: libgtk-3-0, libnotify4, libnss3, libxss1, libxtst6, xdg-utils, libatspi2.0-0, libuuid1, libsecret-1-0, libasound2, libgbm1
|
||||||
Recommends: libappindicator3-1, libsqlcipher0
|
Recommends: libsqlcipher0
|
||||||
Section: net
|
Section: net
|
||||||
Priority: extra
|
Priority: extra
|
||||||
Homepage: https://element.io/
|
Homepage: https://element.io/
|
||||||
|
|||||||
@@ -15,11 +15,7 @@
|
|||||||
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
|
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
|
||||||
"uisi_autorageshake_app": "element-auto-uisi",
|
"uisi_autorageshake_app": "element-auto-uisi",
|
||||||
"roomDirectory": {
|
"roomDirectory": {
|
||||||
"servers": [
|
"servers": ["matrix.org", "gitter.im", "libera.chat"]
|
||||||
"matrix.org",
|
|
||||||
"gitter.im",
|
|
||||||
"libera.chat"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"showLabsSettings": false,
|
"showLabsSettings": false,
|
||||||
"enable_presence_by_hs_url": {
|
"enable_presence_by_hs_url": {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Vendor: support@element.io
|
|||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
Maintainer: support@element.io
|
Maintainer: support@element.io
|
||||||
Depends: libgtk-3-0, libnotify4, libnss3, libxss1, libxtst6, xdg-utils, libatspi2.0-0, libuuid1, libsecret-1-0, libasound2, libgbm1
|
Depends: libgtk-3-0, libnotify4, libnss3, libxss1, libxtst6, xdg-utils, libatspi2.0-0, libuuid1, libsecret-1-0, libasound2, libgbm1
|
||||||
Recommends: libappindicator3-1, libsqlcipher0
|
Recommends: libsqlcipher0
|
||||||
Replaces: riot-desktop (<< 1.7.0), riot-web (<< 1.7.0)
|
Replaces: riot-desktop (<< 1.7.0), riot-web (<< 1.7.0)
|
||||||
Breaks: riot-desktop (<< 1.7.0), riot-web (<< 1.7.0)
|
Breaks: riot-desktop (<< 1.7.0), riot-web (<< 1.7.0)
|
||||||
Section: net
|
Section: net
|
||||||
|
|||||||
+8
-8
@@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
|
|
||||||
import HakEnv from '../../scripts/hak/hakEnv';
|
import HakEnv from "../../scripts/hak/hakEnv";
|
||||||
import { DependencyInfo } from '../../scripts/hak/dep';
|
import { DependencyInfo } from "../../scripts/hak/dep";
|
||||||
|
|
||||||
export default async function buildKeytar(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function buildKeytar(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
const env = hakEnv.makeGypEnv();
|
const env = hakEnv.makeGypEnv();
|
||||||
@@ -26,15 +26,15 @@ export default async function buildKeytar(hakEnv: HakEnv, moduleInfo: Dependency
|
|||||||
console.log("Running yarn with env", env);
|
console.log("Running yarn with env", env);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn(
|
||||||
path.join(moduleInfo.nodeModuleBinDir, 'node-gyp' + (hakEnv.isWin() ? '.cmd' : '')),
|
path.join(moduleInfo.nodeModuleBinDir, "node-gyp" + (hakEnv.isWin() ? ".cmd" : "")),
|
||||||
['rebuild'],
|
["rebuild"],
|
||||||
{
|
{
|
||||||
cwd: moduleInfo.moduleBuildDir,
|
cwd: moduleInfo.moduleBuildDir,
|
||||||
env,
|
env,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
proc.on('exit', (code) => {
|
proc.on("exit", (code) => {
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+7
-7
@@ -14,20 +14,20 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
|
|
||||||
import HakEnv from '../../scripts/hak/hakEnv';
|
import HakEnv from "../../scripts/hak/hakEnv";
|
||||||
import { DependencyInfo } from '../../scripts/hak/dep';
|
import { DependencyInfo } from "../../scripts/hak/dep";
|
||||||
|
|
||||||
export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
const tools = [['python', '--version']]; // node-gyp uses python for reasons beyond comprehension
|
const tools = [["python", "--version"]]; // node-gyp uses python for reasons beyond comprehension
|
||||||
|
|
||||||
for (const tool of tools) {
|
for (const tool of tools) {
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(tool[0], tool.slice(1), {
|
const proc = childProcess.spawn(tool[0], tool.slice(1), {
|
||||||
stdio: ['ignore'],
|
stdio: ["ignore"],
|
||||||
});
|
});
|
||||||
proc.on('exit', (code) => {
|
proc.on("exit", (code) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
reject("Can't find " + tool);
|
reject("Can't find " + tool);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+120
-152
@@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from "mkdirp";
|
||||||
import fsExtra from 'fs-extra';
|
import fsExtra from "fs-extra";
|
||||||
|
|
||||||
import HakEnv from '../../scripts/hak/hakEnv';
|
import HakEnv from "../../scripts/hak/hakEnv";
|
||||||
import { DependencyInfo } from '../../scripts/hak/dep';
|
import { DependencyInfo } from "../../scripts/hak/dep";
|
||||||
|
|
||||||
export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
if (hakEnv.isWin()) {
|
if (hakEnv.isWin()) {
|
||||||
await buildOpenSslWin(hakEnv, moduleInfo);
|
await buildOpenSslWin(hakEnv, moduleInfo);
|
||||||
await buildSqlCipherWin(hakEnv, moduleInfo);
|
await buildSqlCipherWin(hakEnv, moduleInfo);
|
||||||
@@ -32,165 +32,147 @@ export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promi
|
|||||||
await buildMatrixSeshat(hakEnv, moduleInfo);
|
await buildMatrixSeshat(hakEnv, moduleInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildOpenSslWin(hakEnv: HakEnv, moduleInfo: DependencyInfo) {
|
async function buildOpenSslWin(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
const version = moduleInfo.cfg.dependencies.openssl;
|
const version = moduleInfo.cfg.dependencies.openssl;
|
||||||
const openSslDir = path.join(moduleInfo.moduleTargetDotHakDir, `openssl-${version}`);
|
const openSslDir = path.join(moduleInfo.moduleTargetDotHakDir, `openssl-${version}`);
|
||||||
|
|
||||||
const openSslArch = hakEnv.getTargetArch() === 'x64' ? 'VC-WIN64A' : 'VC-WIN32';
|
const openSslArch = hakEnv.getTargetArch() === "x64" ? "VC-WIN64A" : "VC-WIN32";
|
||||||
|
|
||||||
console.log("Building openssl in " + openSslDir);
|
console.log("Building openssl in " + openSslDir);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn(
|
||||||
'perl',
|
"perl",
|
||||||
[
|
[
|
||||||
'Configure',
|
"Configure",
|
||||||
'--prefix=' + moduleInfo.depPrefix,
|
"--prefix=" + moduleInfo.depPrefix,
|
||||||
// sqlcipher only uses about a tiny part of openssl. We link statically
|
// sqlcipher only uses about a tiny part of openssl. We link statically
|
||||||
// so will only pull in the symbols we use, but we may as well turn off
|
// so will only pull in the symbols we use, but we may as well turn off
|
||||||
// as much as possible to save on build time.
|
// as much as possible to save on build time.
|
||||||
'no-afalgeng',
|
"no-afalgeng",
|
||||||
'no-capieng',
|
"no-capieng",
|
||||||
'no-cms',
|
"no-cms",
|
||||||
'no-ct',
|
"no-ct",
|
||||||
'no-deprecated',
|
"no-deprecated",
|
||||||
'no-dgram',
|
"no-dgram",
|
||||||
'no-dso',
|
"no-dso",
|
||||||
'no-ec',
|
"no-ec",
|
||||||
'no-ec2m',
|
"no-ec2m",
|
||||||
'no-gost',
|
"no-gost",
|
||||||
'no-nextprotoneg',
|
"no-nextprotoneg",
|
||||||
'no-ocsp',
|
"no-ocsp",
|
||||||
'no-sock',
|
"no-sock",
|
||||||
'no-srp',
|
"no-srp",
|
||||||
'no-srtp',
|
"no-srtp",
|
||||||
'no-tests',
|
"no-tests",
|
||||||
'no-ssl',
|
"no-ssl",
|
||||||
'no-tls',
|
"no-tls",
|
||||||
'no-dtls',
|
"no-dtls",
|
||||||
'no-shared',
|
"no-shared",
|
||||||
'no-aria',
|
"no-aria",
|
||||||
'no-camellia',
|
"no-camellia",
|
||||||
'no-cast',
|
"no-cast",
|
||||||
'no-chacha',
|
"no-chacha",
|
||||||
'no-cmac',
|
"no-cmac",
|
||||||
'no-des',
|
"no-des",
|
||||||
'no-dh',
|
"no-dh",
|
||||||
'no-dsa',
|
"no-dsa",
|
||||||
'no-ecdh',
|
"no-ecdh",
|
||||||
'no-ecdsa',
|
"no-ecdsa",
|
||||||
'no-idea',
|
"no-idea",
|
||||||
'no-md4',
|
"no-md4",
|
||||||
'no-mdc2',
|
"no-mdc2",
|
||||||
'no-ocb',
|
"no-ocb",
|
||||||
'no-poly1305',
|
"no-poly1305",
|
||||||
'no-rc2',
|
"no-rc2",
|
||||||
'no-rc4',
|
"no-rc4",
|
||||||
'no-rmd160',
|
"no-rmd160",
|
||||||
'no-scrypt',
|
"no-scrypt",
|
||||||
'no-seed',
|
"no-seed",
|
||||||
'no-siphash',
|
"no-siphash",
|
||||||
'no-sm2',
|
"no-sm2",
|
||||||
'no-sm3',
|
"no-sm3",
|
||||||
'no-sm4',
|
"no-sm4",
|
||||||
'no-whirlpool',
|
"no-whirlpool",
|
||||||
openSslArch,
|
openSslArch,
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: openSslDir,
|
cwd: openSslDir,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
proc.on('exit', (code) => {
|
proc.on("exit", (code) => {
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn("nmake", ["build_libs"], {
|
||||||
'nmake',
|
cwd: openSslDir,
|
||||||
['build_libs'],
|
stdio: "inherit",
|
||||||
{
|
});
|
||||||
cwd: openSslDir,
|
proc.on("exit", (code) => {
|
||||||
stdio: 'inherit',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn("nmake", ["install_dev"], {
|
||||||
'nmake',
|
cwd: openSslDir,
|
||||||
['install_dev'],
|
stdio: "inherit",
|
||||||
{
|
});
|
||||||
cwd: openSslDir,
|
proc.on("exit", (code) => {
|
||||||
stdio: 'inherit',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildSqlCipherWin(hakEnv: HakEnv, moduleInfo: DependencyInfo) {
|
async function buildSqlCipherWin(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
const version = moduleInfo.cfg.dependencies.sqlcipher;
|
const version = moduleInfo.cfg.dependencies.sqlcipher;
|
||||||
const sqlCipherDir = path.join(moduleInfo.moduleTargetDotHakDir, `sqlcipher-${version}`);
|
const sqlCipherDir = path.join(moduleInfo.moduleTargetDotHakDir, `sqlcipher-${version}`);
|
||||||
const buildDir = path.join(sqlCipherDir, 'bld');
|
const buildDir = path.join(sqlCipherDir, "bld");
|
||||||
|
|
||||||
await mkdirp(buildDir);
|
await mkdirp(buildDir);
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn("nmake", ["/f", path.join("..", "Makefile.msc"), "libsqlite3.lib", "TOP=.."], {
|
||||||
'nmake',
|
cwd: buildDir,
|
||||||
['/f', path.join('..', 'Makefile.msc'), 'libsqlite3.lib', 'TOP=..'],
|
stdio: "inherit",
|
||||||
{
|
env: Object.assign({}, process.env, {
|
||||||
cwd: buildDir,
|
CCOPTS: "-DSQLITE_HAS_CODEC -I" + path.join(moduleInfo.depPrefix, "include"),
|
||||||
stdio: 'inherit',
|
LTLIBPATHS: "/LIBPATH:" + path.join(moduleInfo.depPrefix, "lib"),
|
||||||
env: Object.assign({}, process.env, {
|
LTLIBS: "libcrypto.lib",
|
||||||
CCOPTS: "-DSQLITE_HAS_CODEC -I" + path.join(moduleInfo.depPrefix, 'include'),
|
}),
|
||||||
LTLIBPATHS: "/LIBPATH:" + path.join(moduleInfo.depPrefix, 'lib'),
|
});
|
||||||
LTLIBS: "libcrypto.lib",
|
proc.on("exit", (code) => {
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await fsExtra.copy(
|
await fsExtra.copy(path.join(buildDir, "libsqlite3.lib"), path.join(moduleInfo.depPrefix, "lib", "sqlcipher.lib"));
|
||||||
path.join(buildDir, 'libsqlite3.lib'),
|
|
||||||
path.join(moduleInfo.depPrefix, 'lib', 'sqlcipher.lib'),
|
|
||||||
);
|
|
||||||
|
|
||||||
await fsExtra.copy(
|
await fsExtra.copy(path.join(buildDir, "sqlite3.h"), path.join(moduleInfo.depPrefix, "include", "sqlcipher.h"));
|
||||||
path.join(buildDir, 'sqlite3.h'),
|
|
||||||
path.join(moduleInfo.depPrefix, 'include', 'sqlcipher.h'),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildSqlCipherUnix(hakEnv: HakEnv, moduleInfo: DependencyInfo) {
|
async function buildSqlCipherUnix(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
const version = moduleInfo.cfg.dependencies.sqlcipher;
|
const version = moduleInfo.cfg.dependencies.sqlcipher;
|
||||||
const sqlCipherDir = path.join(moduleInfo.moduleTargetDotHakDir, `sqlcipher-${version}`);
|
const sqlCipherDir = path.join(moduleInfo.moduleTargetDotHakDir, `sqlcipher-${version}`);
|
||||||
|
|
||||||
const args = [
|
const args = [
|
||||||
'--prefix=' + moduleInfo.depPrefix + '',
|
"--prefix=" + moduleInfo.depPrefix + "",
|
||||||
'--enable-tempstore=yes',
|
"--enable-tempstore=yes",
|
||||||
'--enable-shared=no',
|
"--enable-shared=no",
|
||||||
'--enable-tcl=no',
|
"--enable-tcl=no",
|
||||||
];
|
];
|
||||||
|
|
||||||
if (hakEnv.isMac()) {
|
if (hakEnv.isMac()) {
|
||||||
args.push('--with-crypto-lib=commoncrypto');
|
args.push("--with-crypto-lib=commoncrypto");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hakEnv.wantsStaticSqlCipherUnix()) {
|
if (hakEnv.wantsStaticSqlCipherUnix()) {
|
||||||
args.push('--enable-tcl=no');
|
args.push("--enable-tcl=no");
|
||||||
|
|
||||||
if (hakEnv.isLinux()) {
|
if (hakEnv.isLinux()) {
|
||||||
args.push('--with-pic=yes');
|
args.push("--with-pic=yes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,9 +183,7 @@ async function buildSqlCipherUnix(hakEnv: HakEnv, moduleInfo: DependencyInfo) {
|
|||||||
args.push(`--host=${hakEnv.getTargetId()}`);
|
args.push(`--host=${hakEnv.getTargetId()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cflags = [
|
const cflags = ["-DSQLITE_HAS_CODEC"];
|
||||||
'-DSQLITE_HAS_CODEC',
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!hakEnv.isHost()) {
|
if (!hakEnv.isHost()) {
|
||||||
// `clang` uses more logical option naming.
|
// `clang` uses more logical option naming.
|
||||||
@@ -211,64 +191,52 @@ async function buildSqlCipherUnix(hakEnv: HakEnv, moduleInfo: DependencyInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cflags.length) {
|
if (cflags.length) {
|
||||||
args.push(`CFLAGS=${cflags.join(' ')}`);
|
args.push(`CFLAGS=${cflags.join(" ")}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ldflags: string[] = [];
|
const ldflags: string[] = [];
|
||||||
|
|
||||||
if (hakEnv.isMac()) {
|
if (hakEnv.isMac()) {
|
||||||
ldflags.push('-framework Security');
|
ldflags.push("-framework Security");
|
||||||
ldflags.push('-framework Foundation');
|
ldflags.push("-framework Foundation");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ldflags.length) {
|
if (ldflags.length) {
|
||||||
args.push(`LDFLAGS=${ldflags.join(' ')}`);
|
args.push(`LDFLAGS=${ldflags.join(" ")}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn(path.join(sqlCipherDir, "configure"), args, {
|
||||||
path.join(sqlCipherDir, 'configure'),
|
cwd: sqlCipherDir,
|
||||||
args,
|
stdio: "inherit",
|
||||||
{
|
});
|
||||||
cwd: sqlCipherDir,
|
proc.on("exit", (code) => {
|
||||||
stdio: 'inherit',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn("make", [], {
|
||||||
'make',
|
cwd: sqlCipherDir,
|
||||||
[],
|
stdio: "inherit",
|
||||||
{
|
});
|
||||||
cwd: sqlCipherDir,
|
proc.on("exit", (code) => {
|
||||||
stdio: 'inherit',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn("make", ["install"], {
|
||||||
'make',
|
cwd: sqlCipherDir,
|
||||||
['install'],
|
stdio: "inherit",
|
||||||
{
|
});
|
||||||
cwd: sqlCipherDir,
|
proc.on("exit", (code) => {
|
||||||
stdio: 'inherit',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildMatrixSeshat(hakEnv: HakEnv, moduleInfo: DependencyInfo) {
|
async function buildMatrixSeshat(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
// seshat now uses n-api so we shouldn't need to specify a node version to
|
// seshat now uses n-api so we shouldn't need to specify a node version to
|
||||||
// build against, but it does seems to still need something in here, so leaving
|
// build against, but it does seems to still need something in here, so leaving
|
||||||
// it for now: we should confirm how much of this it still actually needs.
|
// it for now: we should confirm how much of this it still actually needs.
|
||||||
@@ -277,8 +245,8 @@ async function buildMatrixSeshat(hakEnv: HakEnv, moduleInfo: DependencyInfo) {
|
|||||||
if (!hakEnv.isLinux() || hakEnv.wantsStaticSqlCipherUnix()) {
|
if (!hakEnv.isLinux() || hakEnv.wantsStaticSqlCipherUnix()) {
|
||||||
Object.assign(env, {
|
Object.assign(env, {
|
||||||
SQLCIPHER_STATIC: 1,
|
SQLCIPHER_STATIC: 1,
|
||||||
SQLCIPHER_LIB_DIR: path.join(moduleInfo.depPrefix, 'lib'),
|
SQLCIPHER_LIB_DIR: path.join(moduleInfo.depPrefix, "lib"),
|
||||||
SQLCIPHER_INCLUDE_DIR: path.join(moduleInfo.depPrefix, 'include'),
|
SQLCIPHER_INCLUDE_DIR: path.join(moduleInfo.depPrefix, "include"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,11 +266,11 @@ async function buildMatrixSeshat(hakEnv: HakEnv, moduleInfo: DependencyInfo) {
|
|||||||
// --exclude-libs ALL
|
// --exclude-libs ALL
|
||||||
// Prevent symbols from being exported by any archive libraries.
|
// Prevent symbols from being exported by any archive libraries.
|
||||||
// Reduces output filesize and prevents being dynamically linked against.
|
// Reduces output filesize and prevents being dynamically linked against.
|
||||||
env.RUSTFLAGS = '-Clink-arg=-Wl,-Bsymbolic -Clink-arg=-Wl,--exclude-libs,ALL';
|
env.RUSTFLAGS = "-Clink-arg=-Wl,-Bsymbolic -Clink-arg=-Wl,--exclude-libs,ALL";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hakEnv.isWin()) {
|
if (hakEnv.isWin()) {
|
||||||
env.RUSTFLAGS = '-Ctarget-feature=+crt-static -Clink-args=libcrypto.lib';
|
env.RUSTFLAGS = "-Ctarget-feature=+crt-static -Clink-args=libcrypto.lib";
|
||||||
// Note that in general, you can specify targets in Rust without having to have
|
// Note that in general, you can specify targets in Rust without having to have
|
||||||
// the matching toolchain, however for this, cargo gets confused when building
|
// the matching toolchain, however for this, cargo gets confused when building
|
||||||
// the build scripts since they run on the host, but vcvarsall.bat sets the c
|
// the build scripts since they run on the host, but vcvarsall.bat sets the c
|
||||||
@@ -318,15 +286,15 @@ async function buildMatrixSeshat(hakEnv: HakEnv, moduleInfo: DependencyInfo) {
|
|||||||
console.log("Running neon with env", env);
|
console.log("Running neon with env", env);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn(
|
||||||
path.join(moduleInfo.nodeModuleBinDir, 'neon' + (hakEnv.isWin() ? '.cmd' : '')),
|
path.join(moduleInfo.nodeModuleBinDir, "neon" + (hakEnv.isWin() ? ".cmd" : "")),
|
||||||
['build', '--release'],
|
["build", "--release"],
|
||||||
{
|
{
|
||||||
cwd: moduleInfo.moduleBuildDir,
|
cwd: moduleInfo.moduleBuildDir,
|
||||||
env,
|
env,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
proc.on('exit', (code) => {
|
proc.on("exit", (code) => {
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+36
-31
@@ -14,20 +14,20 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
import fsProm from 'fs/promises';
|
import fsProm from "fs/promises";
|
||||||
|
|
||||||
import HakEnv from '../../scripts/hak/hakEnv';
|
import HakEnv from "../../scripts/hak/hakEnv";
|
||||||
import { DependencyInfo } from '../../scripts/hak/dep';
|
import { DependencyInfo } from "../../scripts/hak/dep";
|
||||||
|
|
||||||
export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
if (hakEnv.wantsStaticSqlCipher()) {
|
if (hakEnv.wantsStaticSqlCipher()) {
|
||||||
// of course tcl doesn't have a --version
|
// of course tcl doesn't have a --version
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn('tclsh', [], {
|
const proc = childProcess.spawn("tclsh", [], {
|
||||||
stdio: ['pipe', 'ignore', 'ignore'],
|
stdio: ["pipe", "ignore", "ignore"],
|
||||||
});
|
});
|
||||||
proc.on('exit', (code) => {
|
proc.on("exit", (code) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
reject("Can't find tclsh - have you installed TCL?");
|
reject("Can't find tclsh - have you installed TCL?");
|
||||||
} else {
|
} else {
|
||||||
@@ -39,24 +39,24 @@ export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promi
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tools = [
|
const tools = [
|
||||||
['rustc', '--version'],
|
["rustc", "--version"],
|
||||||
['python', '--version'], // node-gyp uses python for reasons beyond comprehension
|
["python", "--version"], // node-gyp uses python for reasons beyond comprehension
|
||||||
];
|
];
|
||||||
if (hakEnv.isWin()) {
|
if (hakEnv.isWin()) {
|
||||||
tools.push(['perl', '--version']); // for openssl configure
|
tools.push(["perl", "--version"]); // for openssl configure
|
||||||
tools.push(['nasm', '-v']); // for openssl building
|
tools.push(["nasm", "-v"]); // for openssl building
|
||||||
tools.push(['patch', '--version']); // to patch sqlcipher Makefile.msc
|
tools.push(["patch", "--version"]); // to patch sqlcipher Makefile.msc
|
||||||
tools.push(['nmake', '/?']);
|
tools.push(["nmake", "/?"]);
|
||||||
} else {
|
} else {
|
||||||
tools.push(['make', '--version']);
|
tools.push(["make", "--version"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const tool of tools) {
|
for (const tool of tools) {
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(tool[0], tool.slice(1), {
|
const proc = childProcess.spawn(tool[0], tool.slice(1), {
|
||||||
stdio: ['ignore'],
|
stdio: ["ignore"],
|
||||||
});
|
});
|
||||||
proc.on('exit', (code) => {
|
proc.on("exit", (code) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
reject("Can't find " + tool);
|
reject("Can't find " + tool);
|
||||||
} else {
|
} else {
|
||||||
@@ -68,19 +68,24 @@ export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promi
|
|||||||
|
|
||||||
// Ensure Rust target exists (nb. we avoid depending on rustup)
|
// Ensure Rust target exists (nb. we avoid depending on rustup)
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
const rustc = childProcess.execFile('rustc', [
|
const rustc = childProcess.execFile(
|
||||||
'--target', hakEnv.getTargetId(), '-o', 'tmp', '-',
|
"rustc",
|
||||||
], (err, out) => {
|
["--target", hakEnv.getTargetId(), "-o", "tmp", "-"],
|
||||||
if (err) {
|
(err, out) => {
|
||||||
reject(
|
if (err) {
|
||||||
"rustc can't build for target " + hakEnv.getTargetId() +
|
reject(
|
||||||
": ensure target is installed via `rustup target add " + hakEnv.getTargetId() + "` " +
|
"rustc can't build for target " +
|
||||||
"or your package manager if not using `rustup`",
|
hakEnv.getTargetId() +
|
||||||
);
|
": ensure target is installed via `rustup target add " +
|
||||||
}
|
hakEnv.getTargetId() +
|
||||||
fsProm.unlink('tmp').then(resolve);
|
"` " +
|
||||||
});
|
"or your package manager if not using `rustup`",
|
||||||
rustc.stdin.write('fn main() {}');
|
);
|
||||||
rustc.stdin.end();
|
}
|
||||||
|
fsProm.unlink("tmp").then(resolve);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
rustc.stdin!.write("fn main() {}");
|
||||||
|
rustc.stdin!.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -14,17 +14,25 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
import fs from 'fs';
|
import fs from "fs";
|
||||||
import fsProm from 'fs/promises';
|
import fsProm from "fs/promises";
|
||||||
import needle from 'needle';
|
import tar from "tar";
|
||||||
import tar from 'tar';
|
import fetch from "node-fetch";
|
||||||
|
import { promises as stream } from "stream";
|
||||||
|
|
||||||
import HakEnv from '../../scripts/hak/hakEnv';
|
import HakEnv from "../../scripts/hak/hakEnv";
|
||||||
import { DependencyInfo } from '../../scripts/hak/dep';
|
import { DependencyInfo } from "../../scripts/hak/dep";
|
||||||
|
|
||||||
export default async function(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
async function download(url: string, filename: string): Promise<void> {
|
||||||
|
const resp = await fetch(url);
|
||||||
|
if (!resp.ok) throw new Error(`unexpected response ${resp.statusText}`);
|
||||||
|
if (!resp.body) throw new Error(`unexpected response has no body ${resp.statusText}`);
|
||||||
|
await stream.pipeline(resp.body, fs.createWriteStream(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
if (hakEnv.wantsStaticSqlCipher()) {
|
if (hakEnv.wantsStaticSqlCipher()) {
|
||||||
await getSqlCipher(hakEnv, moduleInfo);
|
await getSqlCipher(hakEnv, moduleInfo);
|
||||||
}
|
}
|
||||||
@@ -57,11 +65,7 @@ async function getSqlCipher(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise
|
|||||||
haveSqlcipherTar = false;
|
haveSqlcipherTar = false;
|
||||||
}
|
}
|
||||||
if (!haveSqlcipherTar) {
|
if (!haveSqlcipherTar) {
|
||||||
const bob = needle('get', `https://github.com/sqlcipher/sqlcipher/archive/v${version}.tar.gz`, {
|
await download(`https://github.com/sqlcipher/sqlcipher/archive/v${version}.tar.gz`, sqlCipherTarball);
|
||||||
follow: 10,
|
|
||||||
output: sqlCipherTarball,
|
|
||||||
});
|
|
||||||
await bob;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the tarball to per-target directories, then we avoid cross-contaiminating archs
|
// Extract the tarball to per-target directories, then we avoid cross-contaiminating archs
|
||||||
@@ -79,15 +83,11 @@ async function getSqlCipher(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise
|
|||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const readStream = fs.createReadStream(patchFile);
|
const readStream = fs.createReadStream(patchFile);
|
||||||
|
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn("patch", ["-p1"], {
|
||||||
'patch',
|
cwd: sqlCipherDir,
|
||||||
['-p1'],
|
stdio: ["pipe", "inherit", "inherit"],
|
||||||
{
|
});
|
||||||
cwd: sqlCipherDir,
|
proc.on("exit", (code) => {
|
||||||
stdio: ['pipe', 'inherit', 'inherit'],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
proc.on('exit', (code) => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
readStream.pipe(proc.stdin);
|
readStream.pipe(proc.stdin);
|
||||||
@@ -118,10 +118,7 @@ async function getOpenSsl(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<v
|
|||||||
haveOpenSslTar = false;
|
haveOpenSslTar = false;
|
||||||
}
|
}
|
||||||
if (!haveOpenSslTar) {
|
if (!haveOpenSslTar) {
|
||||||
await needle('get', `https://www.openssl.org/source/openssl-${version}.tar.gz`, {
|
await download(`https://www.openssl.org/source/openssl-${version}.tar.gz`, openSslTarball);
|
||||||
follow: 10,
|
|
||||||
output: openSslTarball,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("extracting " + openSslTarball + " in " + moduleInfo.moduleTargetDotHakDir);
|
console.log("extracting " + openSslTarball + " in " + moduleInfo.moduleTargetDotHakDir);
|
||||||
|
|||||||
+12
-15
@@ -1,17 +1,14 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"target": "es2016",
|
"target": "es2016",
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"lib": [
|
"strict": true,
|
||||||
"es2019",
|
"lib": ["es2019"]
|
||||||
]
|
},
|
||||||
},
|
"include": ["../scripts/@types/*.d.ts", "./**/*.ts"],
|
||||||
"include": [
|
"ts-node": {
|
||||||
"./**/*.ts"
|
"transpileOnly": true
|
||||||
],
|
}
|
||||||
"ts-node": {
|
|
||||||
"transpileOnly": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+22
-19
@@ -2,7 +2,7 @@
|
|||||||
"name": "element-desktop",
|
"name": "element-desktop",
|
||||||
"productName": "Element",
|
"productName": "Element",
|
||||||
"main": "lib/electron-main.js",
|
"main": "lib/electron-main.js",
|
||||||
"version": "1.11.15",
|
"version": "1.11.22",
|
||||||
"description": "A feature-rich client for Matrix.org",
|
"description": "A feature-rich client for Matrix.org",
|
||||||
"author": "Element",
|
"author": "Element",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -16,13 +16,13 @@
|
|||||||
"prunei18n": "matrix-prune-i18n",
|
"prunei18n": "matrix-prune-i18n",
|
||||||
"diff-i18n": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && matrix-gen-i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
|
"diff-i18n": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && matrix-gen-i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
|
||||||
"mkdirs": "mkdirp packages deploys",
|
"mkdirs": "mkdirp packages deploys",
|
||||||
"fetch": "yarn run mkdirs && node scripts/fetch-package.js",
|
"fetch": "yarn run mkdirs && ts-node scripts/fetch-package.ts",
|
||||||
"asar-webapp": "asar p webapp webapp.asar",
|
"asar-webapp": "asar p webapp webapp.asar",
|
||||||
"start": "yarn run build:ts && yarn run build:res && electron .",
|
"start": "yarn run build:ts && yarn run build:res && electron .",
|
||||||
"lint": "yarn lint:types && yarn lint:js",
|
"lint": "yarn lint:types && yarn lint:js",
|
||||||
"lint:js": "eslint --max-warnings 0 src scripts hak",
|
"lint:js": "eslint --max-warnings 0 src scripts hak",
|
||||||
"lint:js-fix": "eslint --fix src scripts hak",
|
"lint:js-fix": "eslint --fix src scripts hak",
|
||||||
"lint:types": "tsc --noEmit && tsc -p scripts/hak/tsconfig.json --noEmit && tsc -p hak/tsconfig.json --noEmit",
|
"lint:types": "tsc --noEmit && tsc -p scripts/tsconfig.json --noEmit && tsc -p hak/tsconfig.json --noEmit",
|
||||||
"build:native": "yarn run hak",
|
"build:native": "yarn run hak",
|
||||||
"build:native:universal": "yarn run hak --target x86_64-apple-darwin fetchandbuild && yarn run hak --target aarch64-apple-darwin fetchandbuild && yarn run hak --target x86_64-apple-darwin --target aarch64-apple-darwin copyandlink",
|
"build:native:universal": "yarn run hak --target x86_64-apple-darwin fetchandbuild && yarn run hak --target aarch64-apple-darwin fetchandbuild && yarn run hak --target x86_64-apple-darwin --target aarch64-apple-darwin copyandlink",
|
||||||
"build:32": "yarn run build:ts && yarn run build:res && electron-builder --ia32",
|
"build:32": "yarn run build:ts && yarn run build:res && electron-builder --ia32",
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"build:universal": "yarn run build:ts && yarn run build:res && electron-builder --universal",
|
"build:universal": "yarn run build:ts && yarn run build:res && electron-builder --universal",
|
||||||
"build": "yarn run build:ts && yarn run build:res && electron-builder",
|
"build": "yarn run build:ts && yarn run build:res && electron-builder",
|
||||||
"build:ts": "tsc",
|
"build:ts": "tsc",
|
||||||
"build:res": "node scripts/copy-res.js",
|
"build:res": "ts-node scripts/copy-res.ts",
|
||||||
"docker:setup": "docker build -t element-desktop-dockerbuild dockerbuild",
|
"docker:setup": "docker build -t element-desktop-dockerbuild dockerbuild",
|
||||||
"docker:build:native": "scripts/in-docker.sh yarn run hak",
|
"docker:build:native": "scripts/in-docker.sh yarn run hak",
|
||||||
"docker:build": "scripts/in-docker.sh yarn run build",
|
"docker:build": "scripts/in-docker.sh yarn run build",
|
||||||
@@ -54,55 +54,58 @@
|
|||||||
"@babel/core": "^7.18.10",
|
"@babel/core": "^7.18.10",
|
||||||
"@babel/preset-env": "^7.18.10",
|
"@babel/preset-env": "^7.18.10",
|
||||||
"@babel/preset-typescript": "^7.18.6",
|
"@babel/preset-typescript": "^7.18.6",
|
||||||
|
"@electron/asar": "^3.2.0",
|
||||||
"@electron/notarize": "^1.2.3",
|
"@electron/notarize": "^1.2.3",
|
||||||
"@types/auto-launch": "^5.0.1",
|
"@types/auto-launch": "^5.0.1",
|
||||||
"@types/counterpart": "^0.18.1",
|
"@types/counterpart": "^0.18.1",
|
||||||
"@types/detect-libc": "^1.0.0",
|
"@types/detect-libc": "^1.0.0",
|
||||||
"@types/jest": "^28",
|
"@types/jest": "^29.0.0",
|
||||||
"@types/minimist": "^1.2.1",
|
"@types/minimist": "^1.2.1",
|
||||||
"@types/mkdirp": "^1.0.2",
|
"@types/mkdirp": "^1.0.2",
|
||||||
|
"@types/node": "16.18.8",
|
||||||
"@types/pacote": "^11.1.1",
|
"@types/pacote": "^11.1.1",
|
||||||
"@types/rimraf": "^3.0.2",
|
"@types/rimraf": "^3.0.2",
|
||||||
|
"@types/tar": "^6.1.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.42.0",
|
"@typescript-eslint/eslint-plugin": "^5.42.0",
|
||||||
"@typescript-eslint/parser": "^5.42.0",
|
"@typescript-eslint/parser": "^5.42.0",
|
||||||
"allchange": "^1.0.6",
|
"allchange": "^1.0.6",
|
||||||
"app-builder-lib": "^22.14.10",
|
"app-builder-lib": "^22.14.10",
|
||||||
"asar": "^2.0.1",
|
"babel-jest": "^29.0.0",
|
||||||
"babel-jest": "^28.1.3",
|
|
||||||
"chokidar": "^3.5.2",
|
"chokidar": "^3.5.2",
|
||||||
"detect-libc": "^1.0.3",
|
"detect-libc": "^1.0.3",
|
||||||
"electron": "^21",
|
"electron": "^22.0.0",
|
||||||
"electron-builder": "^23.6.0",
|
"electron-builder": "^23.6.0",
|
||||||
"electron-builder-squirrel-windows": "^23.6.0",
|
"electron-builder-squirrel-windows": "^23.6.0",
|
||||||
"electron-devtools-installer": "^3.1.1",
|
"electron-devtools-installer": "^3.1.1",
|
||||||
"eslint": "^8.26.0",
|
"eslint": "^8.26.0",
|
||||||
"eslint-config-google": "^0.14.0",
|
"eslint-config-google": "^0.14.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-import": "^2.25.4",
|
"eslint-plugin-import": "^2.25.4",
|
||||||
"eslint-plugin-matrix-org": "^0.7.0",
|
"eslint-plugin-matrix-org": "^0.9.0",
|
||||||
"eslint-plugin-unicorn": "^44.0.2",
|
"eslint-plugin-unicorn": "^45.0.0",
|
||||||
"expect-playwright": "^0.8.0",
|
"expect-playwright": "^0.8.0",
|
||||||
"find-npm-prefix": "^1.0.2",
|
"find-npm-prefix": "^1.0.2",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^11.0.0",
|
||||||
"glob": "^7.1.6",
|
"glob": "^8.0.0",
|
||||||
"jest": "^28",
|
"jest": "^29.0.0",
|
||||||
"matrix-web-i18n": "^1.3.0",
|
"matrix-web-i18n": "^1.3.0",
|
||||||
"mkdirp": "^1.0.3",
|
"mkdirp": "^1.0.3",
|
||||||
"needle": "^2.5.0",
|
"node-pre-gyp": "^0.17.0",
|
||||||
"node-pre-gyp": "^0.15.0",
|
"pacote": "^15.0.0",
|
||||||
"pacote": "^11.3.5",
|
|
||||||
"playwright": "^1.25.0",
|
"playwright": "^1.25.0",
|
||||||
|
"prettier": "^2.8.1",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"tar": "^6.1.2",
|
"tar": "^6.1.2",
|
||||||
"ts-jest": "^28.0.8",
|
"ts-jest": "^29.0.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "4.5.5"
|
"typescript": "4.9.4"
|
||||||
},
|
},
|
||||||
"hakDependencies": {
|
"hakDependencies": {
|
||||||
"matrix-seshat": "^2.3.3",
|
"matrix-seshat": "^2.3.3",
|
||||||
"keytar": "^7.9.0"
|
"keytar": "^7.9.0"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/node": "16.11.38"
|
"@types/node": "16.18.8"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "im.riot.app",
|
"appId": "im.riot.app",
|
||||||
|
|||||||
+108
-124
@@ -1,154 +1,139 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Thin.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Thin.woff2?v=3.19") format("woff2"), url("Inter-Thin.woff?v=3.19") format("woff");
|
||||||
url("Inter-Thin.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-ThinItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-ThinItalic.woff2?v=3.19") format("woff2"), url("Inter-ThinItalic.woff?v=3.19") format("woff");
|
||||||
url("Inter-ThinItalic.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-ExtraLight.woff2?v=3.19") format("woff2"),
|
src: url("Inter-ExtraLight.woff2?v=3.19") format("woff2"), url("Inter-ExtraLight.woff?v=3.19") format("woff");
|
||||||
url("Inter-ExtraLight.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-ExtraLightItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-ExtraLightItalic.woff2?v=3.19") format("woff2"),
|
||||||
url("Inter-ExtraLightItalic.woff?v=3.19") format("woff");
|
url("Inter-ExtraLightItalic.woff?v=3.19") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Light.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Light.woff2?v=3.19") format("woff2"), url("Inter-Light.woff?v=3.19") format("woff");
|
||||||
url("Inter-Light.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-LightItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-LightItalic.woff2?v=3.19") format("woff2"), url("Inter-LightItalic.woff?v=3.19") format("woff");
|
||||||
url("Inter-LightItalic.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Regular.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Regular.woff2?v=3.19") format("woff2"), url("Inter-Regular.woff?v=3.19") format("woff");
|
||||||
url("Inter-Regular.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Italic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Italic.woff2?v=3.19") format("woff2"), url("Inter-Italic.woff?v=3.19") format("woff");
|
||||||
url("Inter-Italic.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Medium.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Medium.woff2?v=3.19") format("woff2"), url("Inter-Medium.woff?v=3.19") format("woff");
|
||||||
url("Inter-Medium.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-MediumItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-MediumItalic.woff2?v=3.19") format("woff2"), url("Inter-MediumItalic.woff?v=3.19") format("woff");
|
||||||
url("Inter-MediumItalic.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-SemiBold.woff2?v=3.19") format("woff2"),
|
src: url("Inter-SemiBold.woff2?v=3.19") format("woff2"), url("Inter-SemiBold.woff?v=3.19") format("woff");
|
||||||
url("Inter-SemiBold.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"),
|
||||||
url("Inter-SemiBoldItalic.woff?v=3.19") format("woff");
|
url("Inter-SemiBoldItalic.woff?v=3.19") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Bold.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Bold.woff2?v=3.19") format("woff2"), url("Inter-Bold.woff?v=3.19") format("woff");
|
||||||
url("Inter-Bold.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"), url("Inter-BoldItalic.woff?v=3.19") format("woff");
|
||||||
url("Inter-BoldItalic.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-ExtraBold.woff2?v=3.19") format("woff2"),
|
src: url("Inter-ExtraBold.woff2?v=3.19") format("woff2"), url("Inter-ExtraBold.woff?v=3.19") format("woff");
|
||||||
url("Inter-ExtraBold.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-ExtraBoldItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-ExtraBoldItalic.woff2?v=3.19") format("woff2"),
|
||||||
url("Inter-ExtraBoldItalic.woff?v=3.19") format("woff");
|
url("Inter-ExtraBoldItalic.woff?v=3.19") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-Black.woff2?v=3.19") format("woff2"),
|
src: url("Inter-Black.woff2?v=3.19") format("woff2"), url("Inter-Black.woff?v=3.19") format("woff");
|
||||||
url("Inter-Black.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: "Inter";
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("Inter-BlackItalic.woff2?v=3.19") format("woff2"),
|
src: url("Inter-BlackItalic.woff2?v=3.19") format("woff2"), url("Inter-BlackItalic.woff?v=3.19") format("woff");
|
||||||
url("Inter-BlackItalic.woff?v=3.19") format("woff");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------
|
/* -------------------------------------------------------
|
||||||
@@ -161,21 +146,20 @@ Usage:
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter var';
|
font-family: "Inter var";
|
||||||
font-weight: 100 900;
|
font-weight: 100 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
src: url("Inter-roman.var.woff2?v=3.19") format("woff2");
|
src: url("Inter-roman.var.woff2?v=3.19") format("woff2");
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter var';
|
font-family: "Inter var";
|
||||||
font-weight: 100 900;
|
font-weight: 100 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
src: url("Inter-italic.var.woff2?v=3.19") format("woff2");
|
src: url("Inter-italic.var.woff2?v=3.19") format("woff2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
[EXPERIMENTAL] Multi-axis, single variable font.
|
[EXPERIMENTAL] Multi-axis, single variable font.
|
||||||
|
|
||||||
@@ -190,9 +174,9 @@ explicitly, e.g.
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter var experimental';
|
font-family: "Inter var experimental";
|
||||||
font-weight: 100 900;
|
font-weight: 100 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
font-style: oblique 0deg 10deg;
|
font-style: oblique 0deg 10deg;
|
||||||
src: url("Inter.var.woff2?v=3.19") format("woff2");
|
src: url("Inter.var.woff2?v=3.19") format("woff2");
|
||||||
}
|
}
|
||||||
@@ -5,13 +5,16 @@
|
|||||||
* hosted on GitHub, https://GitHub.com/Naereen/Nginx-Fancyindex-Theme
|
* hosted on GitHub, https://GitHub.com/Naereen/Nginx-Fancyindex-Theme
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import url('./fonts/inter.css');
|
@import url("./fonts/inter.css");
|
||||||
|
|
||||||
* { font-family: 'Inter', sans-serif; }
|
* {
|
||||||
@supports (font-variation-settings: normal) {
|
font-family: "Inter", sans-serif;
|
||||||
* { font-family: 'Inter var', sans-serif; }
|
}
|
||||||
|
@supports (font-variation-settings: normal) {
|
||||||
|
* {
|
||||||
|
font-family: "Inter var", sans-serif;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -22,14 +25,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
color: #17191C;
|
color: #17191c;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
line-height: 1.6em;
|
line-height: 1.6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: #F4F6FA;
|
background-color: #f4f6fa;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 100px 20px 20px;
|
padding: 100px 20px 20px;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
@@ -52,9 +55,11 @@ a {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
a:hover {
|
a:hover {
|
||||||
color: #0DBD8B;
|
color: #0dbd8b;
|
||||||
}
|
}
|
||||||
a.clear, a.clear:link, a.clear:visited {
|
a.clear,
|
||||||
|
a.clear:link,
|
||||||
|
a.clear:visited {
|
||||||
color: #666;
|
color: #666;
|
||||||
padding: 2px 0;
|
padding: 2px 0;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@@ -74,7 +79,7 @@ a.clear, a.clear:link, a.clear:visited {
|
|||||||
input {
|
input {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
*overflow: visible;
|
*overflow: visible;
|
||||||
font-family: 'Open Sans', sans-serif;
|
font-family: "Open Sans", sans-serif;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@@ -89,20 +94,20 @@ input {
|
|||||||
width: 196px;
|
width: 196px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
|
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
-webkit-transition: border linear .2s,box-shadow linear .2s;
|
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||||
-moz-transition: border linear .2s,box-shadow linear .2s;
|
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||||
-o-transition: border linear .2s,box-shadow linear .2s;
|
-o-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||||
transition: border linear .2s,box-shadow linear .2s;
|
transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||||
}
|
}
|
||||||
input:focus {
|
input:focus {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
border-color: rgba(0,0,0,0.8);
|
border-color: rgba(0, 0, 0, 0.8);
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(0,0,0,0.6);
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(0, 0, 0, 0.6);
|
||||||
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(0,0,0,0.6);
|
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(0, 0, 0, 0.6);
|
||||||
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(0,0,0,0.6);
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(0, 0, 0, 0.6);
|
||||||
}
|
}
|
||||||
input::-moz-focus-inner {
|
input::-moz-focus-inner {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -128,13 +133,12 @@ tr td:first-of-type {
|
|||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
tr.parent a {
|
tr.parent a {
|
||||||
color: #9099A3;
|
color: #9099a3;
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
th {
|
||||||
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: .75em;
|
font-size: 0.75em;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
th + th {
|
th + th {
|
||||||
@@ -157,7 +161,8 @@ td {
|
|||||||
-o-transition: background 300ms ease-in;
|
-o-transition: background 300ms ease-in;
|
||||||
transition: background 300ms ease-in;
|
transition: background 300ms ease-in;
|
||||||
}
|
}
|
||||||
td:last-child,th:last-child {
|
td:last-child,
|
||||||
|
th:last-child {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
@@ -187,11 +192,11 @@ td a {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: #FFF;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav li a {
|
.nav li a {
|
||||||
color: #17191C;
|
color: #17191c;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 20px 20px;
|
padding: 20px 20px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@@ -199,7 +204,7 @@ td a {
|
|||||||
|
|
||||||
.nav li a:hover,
|
.nav li a:hover,
|
||||||
.nav .menu-btn:hover {
|
.nav .menu-btn:hover {
|
||||||
color: #0DBD8B;
|
color: #0dbd8b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav .logo {
|
.nav .logo {
|
||||||
@@ -216,7 +221,7 @@ td a {
|
|||||||
.nav .menu {
|
.nav .menu {
|
||||||
clear: both;
|
clear: both;
|
||||||
max-height: 0;
|
max-height: 0;
|
||||||
transition: max-height .2s ease-out;
|
transition: max-height 0.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* menu icon */
|
/* menu icon */
|
||||||
@@ -237,18 +242,18 @@ td a {
|
|||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: background .2s ease-out;
|
transition: background 0.2s ease-out;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav .menu-icon .navicon:before,
|
.nav .menu-icon .navicon:before,
|
||||||
.nav .menu-icon .navicon:after {
|
.nav .menu-icon .navicon:after {
|
||||||
background: #17191C;
|
background: #17191c;
|
||||||
content: '';
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transition: all .2s ease-out;
|
transition: all 0.2s ease-out;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,13 +308,13 @@ td a {
|
|||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
padding: 12px 20px;
|
padding: 12px 20px;
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
background-color: #0DBD8B;
|
background-color: #0dbd8b;
|
||||||
color:#FFF;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav .primary:hover {
|
.nav .primary:hover {
|
||||||
background-color: #099970;
|
background-color: #099970;
|
||||||
color:#FFF;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav .menu {
|
.nav .menu {
|
||||||
@@ -324,10 +329,10 @@ td a {
|
|||||||
|
|
||||||
footer {
|
footer {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
font-size:0.8em;
|
font-size: 0.8em;
|
||||||
text-align:center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer a {
|
footer a {
|
||||||
color:#03b381;
|
color: #03b381;
|
||||||
}
|
}
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 New Vector Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module "find-npm-prefix" {
|
||||||
|
export default function findPrefix(dir: string): Promise<string>;
|
||||||
|
}
|
||||||
Vendored
+20
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 New Vector Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module "node-pre-gyp/lib/util/versioning" {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
export function get_runtime_abi(runtime: string, version: string): string;
|
||||||
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env -S npx ts-node
|
||||||
|
|
||||||
// copies resources into the lib directory.
|
// copies resources into the lib directory.
|
||||||
|
|
||||||
const parseArgs = require('minimist');
|
import parseArgs from "minimist";
|
||||||
const chokidar = require('chokidar');
|
import * as chokidar from "chokidar";
|
||||||
const path = require('path');
|
import * as path from "path";
|
||||||
const fs = require('fs');
|
import * as fs from "fs";
|
||||||
|
|
||||||
const argv = parseArgs(process.argv.slice(2), {});
|
const argv = parseArgs(process.argv.slice(2), {});
|
||||||
|
|
||||||
const watch = argv.w;
|
const watch = argv.w;
|
||||||
const verbose = argv.v;
|
const verbose = argv.v;
|
||||||
|
|
||||||
function errCheck(err) {
|
function errCheck(err?: Error): void {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err.message);
|
console.error(err.message);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -20,20 +20,19 @@ function errCheck(err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const I18N_BASE_PATH = "src/i18n/strings/";
|
const I18N_BASE_PATH = "src/i18n/strings/";
|
||||||
const INCLUDE_LANGS = fs.readdirSync(I18N_BASE_PATH).filter(fn => fn.endsWith(".json"));
|
const INCLUDE_LANGS = fs.readdirSync(I18N_BASE_PATH).filter((fn) => fn.endsWith(".json"));
|
||||||
|
|
||||||
// Ensure lib, lib/i18n and lib/i18n/strings all exist
|
// Ensure lib, lib/i18n and lib/i18n/strings all exist
|
||||||
fs.mkdirSync('lib/i18n/strings', { recursive: true });
|
fs.mkdirSync("lib/i18n/strings", { recursive: true });
|
||||||
|
|
||||||
function genLangFile(file, dest) {
|
type Translations = Record<string, Record<string, string> | string>;
|
||||||
let translations = {};
|
|
||||||
[file].forEach(function(f) {
|
function genLangFile(file: string, dest: string): void {
|
||||||
|
const inTrs: Record<string, string> = {};
|
||||||
|
[file].forEach(function (f) {
|
||||||
if (fs.existsSync(f)) {
|
if (fs.existsSync(f)) {
|
||||||
try {
|
try {
|
||||||
Object.assign(
|
Object.assign(inTrs, JSON.parse(fs.readFileSync(f).toString()));
|
||||||
translations,
|
|
||||||
JSON.parse(fs.readFileSync(f).toString()),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed: " + f, e);
|
console.error("Failed: " + f, e);
|
||||||
throw e;
|
throw e;
|
||||||
@@ -41,8 +40,7 @@ function genLangFile(file, dest) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
translations = weblateToCounterpart(translations);
|
const translations = weblateToCounterpart(inTrs);
|
||||||
|
|
||||||
const json = JSON.stringify(translations, null, 4);
|
const json = JSON.stringify(translations, null, 4);
|
||||||
const filename = path.basename(file);
|
const filename = path.basename(file);
|
||||||
|
|
||||||
@@ -66,11 +64,11 @@ function genLangFile(file, dest) {
|
|||||||
* "other": "%(count)s badgers"
|
* "other": "%(count)s badgers"
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
function weblateToCounterpart(inTrs) {
|
function weblateToCounterpart(inTrs: Record<string, string>): Translations {
|
||||||
const outTrs = {};
|
const outTrs: Translations = {};
|
||||||
|
|
||||||
for (const key of Object.keys(inTrs)) {
|
for (const key of Object.keys(inTrs)) {
|
||||||
const keyParts = key.split('|', 2);
|
const keyParts = key.split("|", 2);
|
||||||
if (keyParts.length === 2) {
|
if (keyParts.length === 2) {
|
||||||
let obj = outTrs[keyParts[0]];
|
let obj = outTrs[keyParts[0]];
|
||||||
if (obj === undefined) {
|
if (obj === undefined) {
|
||||||
@@ -79,7 +77,7 @@ function weblateToCounterpart(inTrs) {
|
|||||||
// This is a transitional edge case if a string went from singular to pluralised and both still remain
|
// This is a transitional edge case if a string went from singular to pluralised and both still remain
|
||||||
// in the translation json file. Use the singular translation as `other` and merge pluralisation atop.
|
// in the translation json file. Use the singular translation as `other` and merge pluralisation atop.
|
||||||
obj = outTrs[keyParts[0]] = {
|
obj = outTrs[keyParts[0]] = {
|
||||||
"other": inTrs[key],
|
other: inTrs[key],
|
||||||
};
|
};
|
||||||
console.warn("Found entry in i18n file in both singular and pluralised form", keyParts[0]);
|
console.warn("Found entry in i18n file in both singular and pluralised form", keyParts[0]);
|
||||||
}
|
}
|
||||||
@@ -96,12 +94,12 @@ function weblateToCounterpart(inTrs) {
|
|||||||
watch the input files for a given language,
|
watch the input files for a given language,
|
||||||
regenerate the file, and regenerating languages.json with the new filename
|
regenerate the file, and regenerating languages.json with the new filename
|
||||||
*/
|
*/
|
||||||
function watchLanguage(file, dest) {
|
function watchLanguage(file: string, dest: string): void {
|
||||||
// XXX: Use a debounce because for some reason if we read the language
|
// XXX: Use a debounce because for some reason if we read the language
|
||||||
// file immediately after the FS event is received, the file contents
|
// file immediately after the FS event is received, the file contents
|
||||||
// appears empty. Possibly https://github.com/nodejs/node/issues/6112
|
// appears empty. Possibly https://github.com/nodejs/node/issues/6112
|
||||||
let makeLangDebouncer;
|
let makeLangDebouncer: NodeJS.Timeout | undefined;
|
||||||
const makeLang = () => {
|
const makeLang = (): void => {
|
||||||
if (makeLangDebouncer) {
|
if (makeLangDebouncer) {
|
||||||
clearTimeout(makeLangDebouncer);
|
clearTimeout(makeLangDebouncer);
|
||||||
}
|
}
|
||||||
@@ -110,18 +108,15 @@ function watchLanguage(file, dest) {
|
|||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
chokidar.watch(file)
|
chokidar.watch(file).on("add", makeLang).on("change", makeLang).on("error", errCheck);
|
||||||
.on('add', makeLang)
|
|
||||||
.on('change', makeLang)
|
|
||||||
.on('error', errCheck);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// language resources
|
// language resources
|
||||||
const I18N_DEST = "lib/i18n/strings/";
|
const I18N_DEST = "lib/i18n/strings/";
|
||||||
INCLUDE_LANGS.forEach((file) => {
|
INCLUDE_LANGS.forEach((file): void => {
|
||||||
genLangFile(I18N_BASE_PATH + file, I18N_DEST);
|
genLangFile(I18N_BASE_PATH + file, I18N_DEST);
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
if (watch) {
|
if (watch) {
|
||||||
INCLUDE_LANGS.forEach(file => watchLanguage(I18N_BASE_PATH + file, I18N_DEST));
|
INCLUDE_LANGS.forEach((file) => watchLanguage(I18N_BASE_PATH + file, I18N_DEST));
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
const fsProm = require('fs').promises;
|
const fsProm = require("fs").promises;
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
|
|
||||||
exports.default = async function(context) {
|
exports.default = async function (context) {
|
||||||
const { electronPlatformName, appOutDir } = context;
|
const { electronPlatformName, appOutDir } = context;
|
||||||
|
|
||||||
// Squirrel windows will try to relaunch the app using an executable of the same name as
|
// Squirrel windows will try to relaunch the app using an executable of the same name as
|
||||||
@@ -9,7 +9,7 @@ exports.default = async function(context) {
|
|||||||
// We add a fake Riot.exe that it can run which runs the real one.
|
// We add a fake Riot.exe that it can run which runs the real one.
|
||||||
// This also gets signed automatically, presumably because electron-build just looks for all
|
// This also gets signed automatically, presumably because electron-build just looks for all
|
||||||
// exe files and signs them all...
|
// exe files and signs them all...
|
||||||
if (electronPlatformName === 'win32') {
|
if (electronPlatformName === "win32") {
|
||||||
await fsProm.copyFile('build/rebrand_stub/rebrand_stub.exe', path.join(appOutDir, "Riot.exe"));
|
await fsProm.copyFile("build/rebrand_stub/rebrand_stub.exe", path.join(appOutDir, "Riot.exe"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
const { notarize } = require('@electron/notarize');
|
const { notarize } = require("@electron/notarize");
|
||||||
|
|
||||||
let warned = false;
|
let warned = false;
|
||||||
exports.default = async function(context) {
|
exports.default = async function (context) {
|
||||||
const { electronPlatformName, appOutDir } = context;
|
const { electronPlatformName, appOutDir } = context;
|
||||||
const appId = context.packager.info.appInfo.id;
|
const appId = context.packager.info.appInfo.id;
|
||||||
|
|
||||||
if (electronPlatformName === 'darwin') {
|
if (electronPlatformName === "darwin") {
|
||||||
const appName = context.packager.appInfo.productFilename;
|
const appName = context.packager.appInfo.productFilename;
|
||||||
|
|
||||||
const keychainProfile = process.env.NOTARIZE_KEYCHAIN_PROFILE;
|
const keychainProfile = process.env.NOTARIZE_KEYCHAIN_PROFILE;
|
||||||
|
|||||||
+14
-14
@@ -1,4 +1,4 @@
|
|||||||
const { execFile } = require('child_process');
|
const { execFile } = require("child_process");
|
||||||
|
|
||||||
// Loosely based on computeSignToolArgs from app-builder-lib/src/codeSign/windowsCodeSign.ts
|
// Loosely based on computeSignToolArgs from app-builder-lib/src/codeSign/windowsCodeSign.ts
|
||||||
function computeSignToolArgs(options, keyContainer) {
|
function computeSignToolArgs(options, keyContainer) {
|
||||||
@@ -8,15 +8,15 @@ function computeSignToolArgs(options, keyContainer) {
|
|||||||
const timestampingServiceUrl = options.options.timeStampServer || "http://timestamp.digicert.com";
|
const timestampingServiceUrl = options.options.timeStampServer || "http://timestamp.digicert.com";
|
||||||
args.push(
|
args.push(
|
||||||
options.isNest || options.hash === "sha256" ? "/tr" : "/t",
|
options.isNest || options.hash === "sha256" ? "/tr" : "/t",
|
||||||
options.isNest || options.hash === "sha256" ? (
|
options.isNest || options.hash === "sha256"
|
||||||
options.options.rfc3161TimeStampServer || "http://timestamp.comodoca.com/rfc3161"
|
? options.options.rfc3161TimeStampServer || "http://timestamp.comodoca.com/rfc3161"
|
||||||
) : timestampingServiceUrl,
|
: timestampingServiceUrl,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
args.push('/kc', keyContainer);
|
args.push("/kc", keyContainer);
|
||||||
// To use the hardware token (this should probably be less hardcoded)
|
// To use the hardware token (this should probably be less hardcoded)
|
||||||
args.push('/csp', 'eToken Base Cryptographic Provider');
|
args.push("/csp", "eToken Base Cryptographic Provider");
|
||||||
// The certificate file. Somehow this appears to be the only way to specify
|
// The certificate file. Somehow this appears to be the only way to specify
|
||||||
// the cert that works. If you specify the subject name or hash, it will
|
// the cert that works. If you specify the subject name or hash, it will
|
||||||
// say it can't associate the private key to the certificate.
|
// say it can't associate the private key to the certificate.
|
||||||
@@ -24,7 +24,7 @@ function computeSignToolArgs(options, keyContainer) {
|
|||||||
// so we don't have to hard-code this here
|
// so we don't have to hard-code this here
|
||||||
// fwiw https://stackoverflow.com/questions/17927895/automate-extended-validation-ev-code-signing
|
// fwiw https://stackoverflow.com/questions/17927895/automate-extended-validation-ev-code-signing
|
||||||
// is about the most useful resource on automating code signing...
|
// is about the most useful resource on automating code signing...
|
||||||
args.push('/f', 'element.io\\New_Vector_Ltd.pem');
|
args.push("/f", "element.io\\New_Vector_Ltd.pem");
|
||||||
|
|
||||||
if (options.hash !== "sha1") {
|
if (options.hash !== "sha1") {
|
||||||
args.push("/fd", options.hash);
|
args.push("/fd", options.hash);
|
||||||
@@ -35,7 +35,7 @@ function computeSignToolArgs(options, keyContainer) {
|
|||||||
|
|
||||||
// msi does not support dual-signing
|
// msi does not support dual-signing
|
||||||
if (options.isNest) {
|
if (options.isNest) {
|
||||||
args.push("/as");
|
args.push("/as");
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/electron-userland/electron-builder/issues/2875#issuecomment-387233610
|
// https://github.com/electron-userland/electron-builder/issues/2875#issuecomment-387233610
|
||||||
@@ -47,15 +47,15 @@ function computeSignToolArgs(options, keyContainer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let warned = false;
|
let warned = false;
|
||||||
exports.default = async function(options) {
|
exports.default = async function (options) {
|
||||||
const keyContainer = process.env.SIGNING_KEY_CONTAINER;
|
const keyContainer = process.env.SIGNING_KEY_CONTAINER;
|
||||||
if (keyContainer === undefined) {
|
if (keyContainer === undefined) {
|
||||||
if (!warned) {
|
if (!warned) {
|
||||||
console.warn(
|
console.warn(
|
||||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" +
|
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" +
|
||||||
"! Skipping Windows signing. !\n" +
|
"! Skipping Windows signing. !\n" +
|
||||||
"! SIGNING_KEY_CONTAINER not defined. !\n" +
|
"! SIGNING_KEY_CONTAINER not defined. !\n" +
|
||||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
|
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
|
||||||
);
|
);
|
||||||
warned = true;
|
warned = true;
|
||||||
}
|
}
|
||||||
@@ -63,9 +63,9 @@ exports.default = async function(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const args = ['sign'].concat(computeSignToolArgs(options, keyContainer));
|
const args = ["sign"].concat(computeSignToolArgs(options, keyContainer));
|
||||||
|
|
||||||
execFile('signtool', args, {}, (error, stdout) => {
|
execFile("signtool", args, {}, (error, stdout) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error("signtool failed with code " + error);
|
console.error("signtool failed with code " + error);
|
||||||
reject("signtool failed with code " + error);
|
reject("signtool failed with code " + error);
|
||||||
|
|||||||
@@ -1,43 +1,41 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env -S npx ts-node --resolveJsonModule
|
||||||
|
|
||||||
const process = require('process');
|
import * as path from "path";
|
||||||
const path = require('path');
|
import { createWriteStream, promises as fs } from "fs";
|
||||||
const fs = require('fs');
|
import * as childProcess from "child_process";
|
||||||
const fsPromises = require('fs').promises;
|
import tar from "tar";
|
||||||
const childProcess = require('child_process');
|
import * as asar from "asar";
|
||||||
const tar = require('tar');
|
import fetch from "node-fetch";
|
||||||
const asar = require('asar');
|
import { promises as stream } from "stream";
|
||||||
const needle = require('needle');
|
|
||||||
|
|
||||||
const riotDesktopPackageJson = require('../package.json');
|
import riotDesktopPackageJson from "../package.json";
|
||||||
const { setPackageVersion } = require('./set-version.js');
|
import { setPackageVersion } from "./set-version";
|
||||||
|
|
||||||
const PUB_KEY_URL = "https://packages.riot.im/element-release-key.asc";
|
const PUB_KEY_URL = "https://packages.riot.im/element-release-key.asc";
|
||||||
const PACKAGE_URL_PREFIX = "https://github.com/vector-im/element-web/releases/download/";
|
const PACKAGE_URL_PREFIX = "https://github.com/vector-im/element-web/releases/download/";
|
||||||
const DEVELOP_TGZ_URL = "https://develop.element.io/develop.tar.gz";
|
const DEVELOP_TGZ_URL = "https://develop.element.io/develop.tar.gz";
|
||||||
const ASAR_PATH = 'webapp.asar';
|
const ASAR_PATH = "webapp.asar";
|
||||||
|
|
||||||
async function downloadToFile(url, filename) {
|
async function downloadToFile(url: string, filename: string): Promise<void> {
|
||||||
console.log("Downloading " + url + "...");
|
console.log("Downloading " + url + "...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await needle('get', url, null,
|
const resp = await fetch(url);
|
||||||
{
|
if (!resp.ok) throw new Error(`unexpected response ${resp.statusText}`);
|
||||||
follow_max: 5,
|
if (!resp.body) throw new Error(`unexpected response has no body ${resp.statusText}`);
|
||||||
output: filename,
|
await stream.pipeline(resp.body, createWriteStream(filename));
|
||||||
},
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
try {
|
try {
|
||||||
await fsPromises.unlink(filename);
|
await fs.unlink(filename);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function verifyFile(filename) {
|
async function verifyFile(filename: string): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
childProcess.execFile('gpg', ['--verify', filename + '.asc', filename], (error) => {
|
childProcess.execFile("gpg", ["--verify", filename + ".asc", filename], (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
@@ -47,35 +45,35 @@ async function verifyFile(filename) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main(): Promise<number | undefined> {
|
||||||
let verify = true;
|
let verify = true;
|
||||||
let importkey = false;
|
let importkey = false;
|
||||||
let pkgDir = 'packages';
|
let pkgDir = "packages";
|
||||||
let deployDir = 'deploys';
|
let deployDir = "deploys";
|
||||||
let cfgDir;
|
let cfgDir: string | undefined;
|
||||||
let targetVersion;
|
let targetVersion: string | undefined;
|
||||||
let filename;
|
let filename: string | undefined;
|
||||||
let url;
|
let url: string | undefined;
|
||||||
let setVersion = false;
|
let setVersion = false;
|
||||||
|
|
||||||
while (process.argv.length > 2) {
|
while (process.argv.length > 2) {
|
||||||
switch (process.argv[2]) {
|
switch (process.argv[2]) {
|
||||||
case '--noverify':
|
case "--noverify":
|
||||||
verify = false;
|
verify = false;
|
||||||
break;
|
break;
|
||||||
case '--importkey':
|
case "--importkey":
|
||||||
importkey = true;
|
importkey = true;
|
||||||
break;
|
break;
|
||||||
case '--packages':
|
case "--packages":
|
||||||
process.argv.shift();
|
process.argv.shift();
|
||||||
pkgDir = process.argv[2];
|
pkgDir = process.argv[2];
|
||||||
break;
|
break;
|
||||||
case '--deploys':
|
case "--deploys":
|
||||||
process.argv.shift();
|
process.argv.shift();
|
||||||
deployDir = process.argv[2];
|
deployDir = process.argv[2];
|
||||||
break;
|
break;
|
||||||
case '--cfgdir':
|
case "--cfgdir":
|
||||||
case '-d':
|
case "-d":
|
||||||
process.argv.shift();
|
process.argv.shift();
|
||||||
cfgDir = process.argv[2];
|
cfgDir = process.argv[2];
|
||||||
break;
|
break;
|
||||||
@@ -86,13 +84,13 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (targetVersion === undefined) {
|
if (targetVersion === undefined) {
|
||||||
targetVersion = 'v' + riotDesktopPackageJson.version;
|
targetVersion = "v" + riotDesktopPackageJson.version;
|
||||||
} else if (targetVersion !== 'develop') {
|
} else if (targetVersion !== "develop") {
|
||||||
setVersion = true; // version was specified
|
setVersion = true; // version was specified
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetVersion === 'develop') {
|
if (targetVersion === "develop") {
|
||||||
filename = 'develop.tar.gz';
|
filename = "develop.tar.gz";
|
||||||
url = DEVELOP_TGZ_URL;
|
url = DEVELOP_TGZ_URL;
|
||||||
verify = false; // develop builds aren't signed
|
verify = false; // develop builds aren't signed
|
||||||
} else if (targetVersion.includes("://")) {
|
} else if (targetVersion.includes("://")) {
|
||||||
@@ -101,11 +99,11 @@ async function main() {
|
|||||||
verify = false; // manually verified
|
verify = false; // manually verified
|
||||||
} else {
|
} else {
|
||||||
filename = `element-${targetVersion}.tar.gz`;
|
filename = `element-${targetVersion}.tar.gz`;
|
||||||
url = PACKAGE_URL_PREFIX + targetVersion + '/' + filename;
|
url = PACKAGE_URL_PREFIX + targetVersion + "/" + filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
const haveGpg = await new Promise((resolve) => {
|
const haveGpg = await new Promise<boolean>((resolve) => {
|
||||||
childProcess.execFile('gpg', ['--version'], (error) => {
|
childProcess.execFile("gpg", ["--version"], (error) => {
|
||||||
resolve(!error);
|
resolve(!error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -116,8 +114,8 @@ async function main() {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
await new Promise<boolean>((resolve) => {
|
||||||
const gpgProc = childProcess.execFile('gpg', ['--import'], (error) => {
|
const gpgProc = childProcess.execFile("gpg", ["--import"], (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log("Failed to import key", error);
|
console.log("Failed to import key", error);
|
||||||
} else {
|
} else {
|
||||||
@@ -125,7 +123,9 @@ async function main() {
|
|||||||
}
|
}
|
||||||
resolve(!error);
|
resolve(!error);
|
||||||
});
|
});
|
||||||
needle.get(PUB_KEY_URL).pipe(gpgProc.stdin);
|
fetch(PUB_KEY_URL).then((resp) => {
|
||||||
|
stream.pipeline(resp.body, gpgProc.stdin!);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -143,18 +143,17 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let haveDeploy = false;
|
let haveDeploy = false;
|
||||||
let expectedDeployDir = path.join(deployDir, path.basename(filename).replace(/\.tar\.gz/, ''));
|
let expectedDeployDir = path.join(deployDir, path.basename(filename).replace(/\.tar\.gz/, ""));
|
||||||
try {
|
try {
|
||||||
await fs.opendir(expectedDeployDir);
|
await fs.opendir(expectedDeployDir);
|
||||||
console.log(expectedDeployDir + "already exists");
|
console.log(expectedDeployDir + "already exists");
|
||||||
haveDeploy = true;
|
haveDeploy = true;
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
|
|
||||||
if (!haveDeploy) {
|
if (!haveDeploy) {
|
||||||
const outPath = path.join(pkgDir, filename);
|
const outPath = path.join(pkgDir, filename);
|
||||||
try {
|
try {
|
||||||
await fsPromises.stat(outPath);
|
await fs.stat(outPath);
|
||||||
console.log("Already have " + filename + ": not redownloading");
|
console.log("Already have " + filename + ": not redownloading");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
@@ -167,11 +166,11 @@ async function main() {
|
|||||||
|
|
||||||
if (verify) {
|
if (verify) {
|
||||||
try {
|
try {
|
||||||
await fsPromises.stat(outPath+'.asc');
|
await fs.stat(outPath + ".asc");
|
||||||
console.log("Already have " + filename + ".asc: not redownloading");
|
console.log("Already have " + filename + ".asc: not redownloading");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
await downloadToFile(url + '.asc', outPath + '.asc');
|
await downloadToFile(url + ".asc", outPath + ".asc");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Failed to download " + url, e);
|
console.log("Failed to download " + url, e);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -192,7 +191,7 @@ async function main() {
|
|||||||
await tar.x({
|
await tar.x({
|
||||||
file: outPath,
|
file: outPath,
|
||||||
cwd: deployDir,
|
cwd: deployDir,
|
||||||
onentry: entry => {
|
onentry: (entry) => {
|
||||||
// Find the appropriate extraction path, only needed for `develop` where the dir name is unknown
|
// Find the appropriate extraction path, only needed for `develop` where the dir name is unknown
|
||||||
if (entry.type === "Directory" && !path.join(deployDir, entry.path).startsWith(expectedDeployDir)) {
|
if (entry.type === "Directory" && !path.join(deployDir, entry.path).startsWith(expectedDeployDir)) {
|
||||||
expectedDeployDir = path.join(deployDir, entry.path);
|
expectedDeployDir = path.join(deployDir, entry.path);
|
||||||
@@ -202,17 +201,16 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fsPromises.stat(ASAR_PATH);
|
await fs.stat(ASAR_PATH);
|
||||||
console.log(ASAR_PATH + " already present: removing");
|
console.log(ASAR_PATH + " already present: removing");
|
||||||
await fsPromises.unlink(ASAR_PATH);
|
await fs.unlink(ASAR_PATH);
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
|
|
||||||
if (cfgDir.length) {
|
if (cfgDir.length) {
|
||||||
const configJsonSource = path.join(cfgDir, 'config.json');
|
const configJsonSource = path.join(cfgDir, "config.json");
|
||||||
const configJsonDest = path.join(expectedDeployDir, 'config.json');
|
const configJsonDest = path.join(expectedDeployDir, "config.json");
|
||||||
console.log(configJsonSource + ' -> ' + configJsonDest);
|
console.log(configJsonSource + " -> " + configJsonDest);
|
||||||
await fsPromises.copyFile(configJsonSource, configJsonDest);
|
await fs.copyFile(configJsonSource, configJsonDest);
|
||||||
} else {
|
} else {
|
||||||
console.log("Skipping config file");
|
console.log("Skipping config file");
|
||||||
}
|
}
|
||||||
@@ -221,7 +219,7 @@ async function main() {
|
|||||||
await asar.createPackage(expectedDeployDir, ASAR_PATH);
|
await asar.createPackage(expectedDeployDir, ASAR_PATH);
|
||||||
|
|
||||||
if (setVersion) {
|
if (setVersion) {
|
||||||
const semVer = fs.readFileSync(path.join(expectedDeployDir, "version"), "utf-8").trim();
|
const semVer = (await fs.readFile(path.join(expectedDeployDir, "version"), "utf-8")).trim();
|
||||||
console.log("Updating version to " + semVer);
|
console.log("Updating version to " + semVer);
|
||||||
await setPackageVersion(semVer);
|
await setPackageVersion(semVer);
|
||||||
}
|
}
|
||||||
@@ -229,9 +227,11 @@ async function main() {
|
|||||||
console.log("Done!");
|
console.log("Done!");
|
||||||
}
|
}
|
||||||
|
|
||||||
main().then((ret) => {
|
main()
|
||||||
process.exit(ret);
|
.then((ret) => {
|
||||||
}).catch(e => {
|
process.exit(ret);
|
||||||
console.error(e);
|
})
|
||||||
process.exit(1);
|
.catch((e) => {
|
||||||
});
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -29,7 +29,7 @@ const client = new S3Client({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const templateLayout = (content: string) => `
|
const templateLayout = (content: string): string => `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
@@ -80,25 +80,25 @@ const templateLayout = (content: string) => `
|
|||||||
*
|
*
|
||||||
* @return Formatted string.
|
* @return Formatted string.
|
||||||
*/
|
*/
|
||||||
function humanFileSize(bytes: number, si = false, dp = 1) {
|
function humanFileSize(bytes: number, si = false, dp = 1): string {
|
||||||
const thresh = si ? 1000 : 1024;
|
const thresh = si ? 1000 : 1024;
|
||||||
|
|
||||||
if (Math.abs(bytes) < thresh) {
|
if (Math.abs(bytes) < thresh) {
|
||||||
return bytes + ' B';
|
return bytes + " B";
|
||||||
}
|
}
|
||||||
|
|
||||||
const units = si
|
const units = si
|
||||||
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
? ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
|
||||||
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
: ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
|
||||||
let u = -1;
|
let u = -1;
|
||||||
const r = 10**dp;
|
const r = 10 ** dp;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
bytes /= thresh;
|
bytes /= thresh;
|
||||||
++u;
|
++u;
|
||||||
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
|
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
|
||||||
|
|
||||||
return bytes.toFixed(dp) + ' ' + units[u];
|
return bytes.toFixed(dp) + " " + units[u];
|
||||||
}
|
}
|
||||||
|
|
||||||
const dateTimeOptions: Intl.DateTimeFormatOptions = {
|
const dateTimeOptions: Intl.DateTimeFormatOptions = {
|
||||||
@@ -122,7 +122,8 @@ function indexLayout(prefix: string, files: _Object[], dirs: string[]): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (!file.Key ||
|
if (
|
||||||
|
!file.Key ||
|
||||||
HIDDEN_FILES.includes(`/${file.Key}`) ||
|
HIDDEN_FILES.includes(`/${file.Key}`) ||
|
||||||
HIDDEN_FILES.includes(file.Key.slice(file.Key.lastIndexOf("/") + 1))
|
HIDDEN_FILES.includes(file.Key.slice(file.Key.lastIndexOf("/") + 1))
|
||||||
) {
|
) {
|
||||||
@@ -143,11 +144,15 @@ function indexLayout(prefix: string, files: _Object[], dirs: string[]): string {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
${rows.map(([link, name, size, date]) => `<tr>
|
${rows
|
||||||
|
.map(
|
||||||
|
([link, name, size, date]) => `<tr>
|
||||||
<td class="link"><a href="${link}">${name}</a></td>
|
<td class="link"><a href="${link}">${name}</a></td>
|
||||||
<td class="size">${size ? humanFileSize(size) : "-"}</td>
|
<td class="size">${size ? humanFileSize(size) : "-"}</td>
|
||||||
<td class="date">${date?.toLocaleString("en-GB", dateTimeOptions) ?? "-"}</td>
|
<td class="date">${date?.toLocaleString("en-GB", dateTimeOptions) ?? "-"}</td>
|
||||||
</tr>`).join("")}
|
</tr>`,
|
||||||
|
)
|
||||||
|
.join("")}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
`);
|
`);
|
||||||
@@ -166,17 +171,20 @@ async function generateIndex(Prefix: string): Promise<{
|
|||||||
|
|
||||||
const listResponse = await client.send(command);
|
const listResponse = await client.send(command);
|
||||||
const files = listResponse.Contents ?? [];
|
const files = listResponse.Contents ?? [];
|
||||||
const dirs = listResponse.CommonPrefixes
|
const dirs =
|
||||||
?.map(p => p.Prefix?.slice(Prefix.length).split("/", 2)[0])
|
(listResponse.CommonPrefixes?.map((p) => p.Prefix?.slice(Prefix.length).split("/", 2)[0]).filter(
|
||||||
.filter(Boolean) as string[] ?? [];
|
Boolean,
|
||||||
|
) as string[]) ?? [];
|
||||||
const Body = indexLayout(Prefix, files, dirs);
|
const Body = indexLayout(Prefix, files, dirs);
|
||||||
|
|
||||||
await client.send(new PutObjectCommand({
|
await client.send(
|
||||||
Body,
|
new PutObjectCommand({
|
||||||
Bucket,
|
Body,
|
||||||
ContentType: "text/html",
|
Bucket,
|
||||||
Key: Prefix + "index.html",
|
ContentType: "text/html",
|
||||||
}));
|
Key: Prefix + "index.html",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return { files, dirs };
|
return { files, dirs };
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-35
@@ -1,40 +1,40 @@
|
|||||||
hak
|
# hak
|
||||||
===
|
|
||||||
|
|
||||||
This tool builds native dependencies for element-desktop. Here follows some very minimal
|
This tool builds native dependencies for element-desktop. Here follows some very minimal
|
||||||
documentation for it.
|
documentation for it.
|
||||||
|
|
||||||
Goals:
|
Goals:
|
||||||
* Must build compiled native node modules in a shippable state
|
|
||||||
(ie. only dynamically linked against libraries that will be on the
|
- Must build compiled native node modules in a shippable state
|
||||||
target system, all unnecessary files removed).
|
(ie. only dynamically linked against libraries that will be on the
|
||||||
* Must be able to build any native module, no matter what build system
|
target system, all unnecessary files removed).
|
||||||
it uses (electron-rebuild is supposed to do this job but only works
|
- Must be able to build any native module, no matter what build system
|
||||||
for modules that use gyp).
|
it uses (electron-rebuild is supposed to do this job but only works
|
||||||
|
for modules that use gyp).
|
||||||
|
|
||||||
It's also loosely designed to be a general tool and agnostic to what it's
|
It's also loosely designed to be a general tool and agnostic to what it's
|
||||||
actually building. It's used here to build modules for the electron app
|
actually building. It's used here to build modules for the electron app
|
||||||
but should work equally well for building modules for normal node.
|
but should work equally well for building modules for normal node.
|
||||||
|
|
||||||
Running
|
# Running
|
||||||
=======
|
|
||||||
Hak is invoked with a command and a dependency, eg. `yarn run hak fetch matrix-seshat`.
|
Hak is invoked with a command and a dependency, eg. `yarn run hak fetch matrix-seshat`.
|
||||||
If no dependencies are given, hak runs the command on all dependencies.
|
If no dependencies are given, hak runs the command on all dependencies.
|
||||||
|
|
||||||
Files
|
# Files
|
||||||
=====
|
|
||||||
There are a lot of files involved:
|
There are a lot of files involved:
|
||||||
|
|
||||||
* scripts/hak/... - The tool itself
|
- scripts/hak/... - The tool itself
|
||||||
* hak/[dependency] - Files provided by the app that tell hak how to build each of its native dependencies.
|
- hak/[dependency] - Files provided by the app that tell hak how to build each of its native dependencies.
|
||||||
Contains a hak.json file and also some script files, each of which must be referenced in hak.json.
|
Contains a hak.json file and also some script files, each of which must be referenced in hak.json.
|
||||||
* .hak/ - Files generated by hak in the course of doing its job. Includes the dependency module itself and
|
- .hak/ - Files generated by hak in the course of doing its job. Includes the dependency module itself and
|
||||||
any of the native dependency's native dependencies.
|
any of the native dependency's native dependencies.
|
||||||
* .hak/[dependency]/build - An extracted copy of the dependency's node module used to build it.
|
- .hak/[dependency]/build - An extracted copy of the dependency's node module used to build it.
|
||||||
* .hak/[dependency]/out - Another extracted copy of the dependency, this one contains only what will be shipped.
|
- .hak/[dependency]/out - Another extracted copy of the dependency, this one contains only what will be shipped.
|
||||||
|
|
||||||
|
# Workings
|
||||||
|
|
||||||
Workings
|
|
||||||
========
|
|
||||||
Hak works around native node modules that try to fetch or build their native component in
|
Hak works around native node modules that try to fetch or build their native component in
|
||||||
the npm 'install' phase - modules that do this will typically end up with native components
|
the npm 'install' phase - modules that do this will typically end up with native components
|
||||||
targeted to the build platform and the node that npm/yarn is using, which is no good for an
|
targeted to the build platform and the node that npm/yarn is using, which is no good for an
|
||||||
@@ -49,33 +49,34 @@ This also means that the dependencies cannot be listed in `dependencies` or
|
|||||||
try to fetch their native parts. Instead, they are listed in `hakDependencies` which
|
try to fetch their native parts. Instead, they are listed in `hakDependencies` which
|
||||||
hak reads to install them for you.
|
hak reads to install them for you.
|
||||||
|
|
||||||
Hak will *not* install dependencies for the copy of the module it links into your
|
Hak will _not_ install dependencies for the copy of the module it links into your
|
||||||
project, so if your native module has javascript dependencies that are actually needed at
|
project, so if your native module has javascript dependencies that are actually needed at
|
||||||
runtime (and not just to fetch / build the native parts), it won't work.
|
runtime (and not just to fetch / build the native parts), it won't work.
|
||||||
|
|
||||||
Hak will generate a `.yarnrc` in the project directory to set the link directory to its
|
Hak will generate a `.yarnrc` in the project directory to set the link directory to its
|
||||||
own in the .hak directory (unless one already exists, in which case this is your problem).
|
own in the .hak directory (unless one already exists, in which case this is your problem).
|
||||||
|
|
||||||
Lifecycle
|
# Lifecycle
|
||||||
=========
|
|
||||||
Hak is divided into lifecycle stages, in order:
|
Hak is divided into lifecycle stages, in order:
|
||||||
* fetch - Download and extract the source of the dependency
|
|
||||||
* link - Link the copy of the dependency into your node_modules directory
|
- fetch - Download and extract the source of the dependency
|
||||||
* fetchDeps - Fetch & extract any native dependencies required to build the module.
|
- link - Link the copy of the dependency into your node_modules directory
|
||||||
* build - The Good Stuff. Configure and build any native dependencies, then the module itself.
|
- fetchDeps - Fetch & extract any native dependencies required to build the module.
|
||||||
* copy - Copy the built artifact from the module build directory to the module output directory.
|
- build - The Good Stuff. Configure and build any native dependencies, then the module itself.
|
||||||
|
- copy - Copy the built artifact from the module build directory to the module output directory.
|
||||||
|
|
||||||
|
# hak.json
|
||||||
|
|
||||||
hak.json
|
|
||||||
========
|
|
||||||
The scripts section contains scripts used for lifecycle stages that need them (fetch, fetchDeps, build).
|
The scripts section contains scripts used for lifecycle stages that need them (fetch, fetchDeps, build).
|
||||||
It also contains 'prune' and 'copy' which are globs of files to delete from the output module directory
|
It also contains 'prune' and 'copy' which are globs of files to delete from the output module directory
|
||||||
and copy over from the module build directory to the output module directory, respectively.
|
and copy over from the module build directory to the output module directory, respectively.
|
||||||
|
|
||||||
Shortcomings
|
# Shortcomings
|
||||||
============
|
|
||||||
Hak doesn't know about dependencies between lifecycle stages, ie. it doesn't know that you need to
|
Hak doesn't know about dependencies between lifecycle stages, ie. it doesn't know that you need to
|
||||||
'fetch' and 'fetchDeps' before you can 'build', etc. You get to run each individually, and remember
|
'fetch' and 'fetchDeps' before you can 'build', etc. You get to run each individually, and remember
|
||||||
the right order.
|
the right order.
|
||||||
|
|
||||||
There is also a *lot* of duplication in the command execution: we should abstract away
|
There is also a _lot_ of duplication in the command execution: we should abstract away
|
||||||
some of the boilerplate required to run commands & so forth.
|
some of the boilerplate required to run commands & so forth.
|
||||||
@@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import rimraf from 'rimraf';
|
import rimraf from "rimraf";
|
||||||
|
|
||||||
import { DependencyInfo } from './dep';
|
import { DependencyInfo } from "./dep";
|
||||||
import HakEnv from './hakEnv';
|
import HakEnv from "./hakEnv";
|
||||||
|
|
||||||
export default async function clean(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function clean(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
rimraf(moduleInfo.moduleDotHakDir, (err: Error) => {
|
rimraf(moduleInfo.moduleDotHakDir, (err?: Error | null) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@@ -32,7 +32,7 @@ export default async function clean(hakEnv: HakEnv, moduleInfo: DependencyInfo):
|
|||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
rimraf(path.join(hakEnv.dotHakDir, 'links', moduleInfo.name), (err: Error) => {
|
rimraf(path.join(hakEnv.dotHakDir, "links", moduleInfo.name), (err?: Error | null) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@@ -42,7 +42,7 @@ export default async function clean(hakEnv: HakEnv, moduleInfo: DependencyInfo):
|
|||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
rimraf(path.join(hakEnv.projectRoot, 'node_modules', moduleInfo.name), (err: Error) => {
|
rimraf(path.join(hakEnv.projectRoot, "node_modules", moduleInfo.name), (err?: Error | null) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+30
-32
@@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import fsProm from 'fs/promises';
|
import fsProm from "fs/promises";
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
import rimraf from 'rimraf';
|
import rimraf from "rimraf";
|
||||||
import glob from 'glob';
|
import glob from "glob";
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from "mkdirp";
|
||||||
|
|
||||||
import HakEnv from './hakEnv';
|
import HakEnv from "./hakEnv";
|
||||||
import { DependencyInfo } from './dep';
|
import { DependencyInfo } from "./dep";
|
||||||
|
|
||||||
export default async function copy(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function copy(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
if (moduleInfo.cfg.prune) {
|
if (moduleInfo.cfg.prune) {
|
||||||
@@ -34,7 +34,7 @@ export default async function copy(hakEnv: HakEnv, moduleInfo: DependencyInfo):
|
|||||||
await mkdirp(moduleInfo.moduleOutDir);
|
await mkdirp(moduleInfo.moduleOutDir);
|
||||||
process.chdir(moduleInfo.moduleOutDir);
|
process.chdir(moduleInfo.moduleOutDir);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
rimraf(moduleInfo.cfg.prune, {}, err => {
|
rimraf(moduleInfo.cfg.prune, {}, (err) => {
|
||||||
err ? reject(err) : resolve();
|
err ? reject(err) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -48,46 +48,44 @@ export default async function copy(hakEnv: HakEnv, moduleInfo: DependencyInfo):
|
|||||||
// is the same as moduleBuildDirs[0], so we're just listing the contents
|
// is the same as moduleBuildDirs[0], so we're just listing the contents
|
||||||
// of the first one.
|
// of the first one.
|
||||||
const files = await new Promise<string[]>((resolve, reject) => {
|
const files = await new Promise<string[]>((resolve, reject) => {
|
||||||
glob(moduleInfo.cfg.copy, {
|
glob(
|
||||||
nosort: true,
|
moduleInfo.cfg.copy,
|
||||||
silent: true,
|
{
|
||||||
cwd: moduleInfo.moduleBuildDir,
|
nosort: true,
|
||||||
}, (err, files) => {
|
silent: true,
|
||||||
err ? reject(err) : resolve(files);
|
cwd: moduleInfo.moduleBuildDir,
|
||||||
});
|
},
|
||||||
|
(err, files) => {
|
||||||
|
err ? reject(err) : resolve(files);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (moduleInfo.moduleBuildDirs.length > 1) {
|
if (moduleInfo.moduleBuildDirs.length > 1) {
|
||||||
if (!hakEnv.isMac()) {
|
if (!hakEnv.isMac()) {
|
||||||
console.error(
|
console.error(
|
||||||
"You asked me to copy multiple targets but I've only been taught " +
|
"You asked me to copy multiple targets but I've only been taught " + "how to do that on macOS.",
|
||||||
"how to do that on macOS.",
|
|
||||||
);
|
);
|
||||||
throw new Error("Can't copy multiple targets on this platform");
|
throw new Error("Can't copy multiple targets on this platform");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const f of files) {
|
for (const f of files) {
|
||||||
const components = moduleInfo.moduleBuildDirs.map(dir => path.join(dir, f));
|
const components = moduleInfo.moduleBuildDirs.map((dir) => path.join(dir, f));
|
||||||
const dst = path.join(moduleInfo.moduleOutDir, f);
|
const dst = path.join(moduleInfo.moduleOutDir, f);
|
||||||
|
|
||||||
await mkdirp(path.dirname(dst));
|
await mkdirp(path.dirname(dst));
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
childProcess.execFile('lipo',
|
childProcess.execFile("lipo", ["-create", "-output", dst, ...components], (err) => {
|
||||||
['-create', '-output', dst, ...components], (err) => {
|
if (err) {
|
||||||
if (err) {
|
reject(err);
|
||||||
reject(err);
|
} else {
|
||||||
} else {
|
resolve();
|
||||||
resolve();
|
}
|
||||||
}
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
console.log("Copying files from " + moduleInfo.moduleBuildDir + " to " + moduleInfo.moduleOutDir);
|
||||||
"Copying files from " +
|
|
||||||
moduleInfo.moduleBuildDir + " to " + moduleInfo.moduleOutDir,
|
|
||||||
);
|
|
||||||
for (const f of files) {
|
for (const f of files) {
|
||||||
console.log("\t" + f);
|
console.log("\t" + f);
|
||||||
const src = path.join(moduleInfo.moduleBuildDir, f);
|
const src = path.join(moduleInfo.moduleBuildDir, f);
|
||||||
|
|||||||
+1
-1
@@ -28,5 +28,5 @@ export interface DependencyInfo {
|
|||||||
moduleOutDir: string;
|
moduleOutDir: string;
|
||||||
nodeModuleBinDir: string;
|
nodeModuleBinDir: string;
|
||||||
depPrefix: string;
|
depPrefix: string;
|
||||||
scripts: Record<string, (hakEnv: HakEnv, moduleInfo: DependencyInfo) => Promise<void> >;
|
scripts: Record<string, (hakEnv: HakEnv, moduleInfo: DependencyInfo) => Promise<void>>;
|
||||||
}
|
}
|
||||||
+10
-14
@@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import fsProm from 'fs/promises';
|
import fsProm from "fs/promises";
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
import pacote from 'pacote';
|
import pacote from "pacote";
|
||||||
|
|
||||||
import HakEnv from './hakEnv';
|
import HakEnv from "./hakEnv";
|
||||||
import { DependencyInfo } from './dep';
|
import { DependencyInfo } from "./dep";
|
||||||
|
|
||||||
export default async function fetch(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function fetch(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
let haveModuleBuildDir;
|
let haveModuleBuildDir;
|
||||||
@@ -41,15 +41,11 @@ export default async function fetch(hakEnv: HakEnv, moduleInfo: DependencyInfo):
|
|||||||
|
|
||||||
console.log("Running yarn install in " + moduleInfo.moduleBuildDir);
|
console.log("Running yarn install in " + moduleInfo.moduleBuildDir);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(
|
const proc = childProcess.spawn(hakEnv.isWin() ? "yarn.cmd" : "yarn", ["install", "--ignore-scripts"], {
|
||||||
hakEnv.isWin() ? 'yarn.cmd' : 'yarn',
|
stdio: "inherit",
|
||||||
['install', '--ignore-scripts'],
|
cwd: moduleInfo.moduleBuildDir,
|
||||||
{
|
});
|
||||||
stdio: 'inherit',
|
proc.on("exit", (code) => {
|
||||||
cwd: moduleInfo.moduleBuildDir,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
proc.on('exit', code => {
|
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from "mkdirp";
|
||||||
|
|
||||||
import { DependencyInfo } from './dep';
|
import { DependencyInfo } from "./dep";
|
||||||
import HakEnv from './hakEnv';
|
import HakEnv from "./hakEnv";
|
||||||
|
|
||||||
export default async function fetchDeps(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function fetchDeps(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
await mkdirp(moduleInfo.moduleDotHakDir);
|
await mkdirp(moduleInfo.moduleDotHakDir);
|
||||||
|
|||||||
+20
-30
@@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import os from 'os';
|
import os from "os";
|
||||||
import nodePreGypVersioning from "node-pre-gyp/lib/util/versioning";
|
import nodePreGypVersioning from "node-pre-gyp/lib/util/versioning";
|
||||||
import { getElectronVersion } from "app-builder-lib/out/electron/electronVersion";
|
import { getElectronVersion } from "app-builder-lib/out/electron/electronVersion";
|
||||||
|
|
||||||
import { Arch, Target, TARGETS, getHost, isHostId, TargetId } from './target';
|
import { Arch, Target, TARGETS, getHost, isHostId, TargetId } from "./target";
|
||||||
|
|
||||||
async function getRuntime(projectRoot: string): Promise<string> {
|
async function getRuntime(projectRoot: string): Promise<string> {
|
||||||
const electronVersion = await getElectronVersion(projectRoot);
|
const electronVersion = await getElectronVersion(projectRoot);
|
||||||
return electronVersion ? 'electron' : 'node-webkit';
|
return electronVersion ? "electron" : "node-webkit";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getRuntimeVersion(projectRoot: string): Promise<string> {
|
async function getRuntimeVersion(projectRoot: string): Promise<string> {
|
||||||
@@ -37,38 +37,32 @@ async function getRuntimeVersion(projectRoot: string): Promise<string> {
|
|||||||
|
|
||||||
export default class HakEnv {
|
export default class HakEnv {
|
||||||
public readonly target: Target;
|
public readonly target: Target;
|
||||||
public runtime: string;
|
public runtime?: string;
|
||||||
public runtimeVersion: string;
|
public runtimeVersion?: string;
|
||||||
public dotHakDir: string;
|
public dotHakDir: string;
|
||||||
|
|
||||||
constructor(public readonly projectRoot: string, targetId: TargetId | null) {
|
public constructor(public readonly projectRoot: string, targetId: TargetId | null) {
|
||||||
if (targetId) {
|
const target = targetId ? TARGETS[targetId] : getHost();
|
||||||
this.target = TARGETS[targetId];
|
|
||||||
} else {
|
|
||||||
this.target = getHost();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.target) {
|
if (!target) {
|
||||||
throw new Error(`Unknown target ${targetId}!`);
|
throw new Error(`Unknown target ${targetId}!`);
|
||||||
}
|
}
|
||||||
this.dotHakDir = path.join(this.projectRoot, '.hak');
|
this.target = target;
|
||||||
|
this.dotHakDir = path.join(this.projectRoot, ".hak");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init() {
|
public async init(): Promise<void> {
|
||||||
this.runtime = await getRuntime(this.projectRoot);
|
this.runtime = await getRuntime(this.projectRoot);
|
||||||
this.runtimeVersion = await getRuntimeVersion(this.projectRoot);
|
this.runtimeVersion = await getRuntimeVersion(this.projectRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getRuntimeAbi(): string {
|
public getRuntimeAbi(): string {
|
||||||
return nodePreGypVersioning.get_runtime_abi(
|
return nodePreGypVersioning.get_runtime_abi(this.runtime!, this.runtimeVersion!);
|
||||||
this.runtime,
|
|
||||||
this.runtimeVersion,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// {node_abi}-{platform}-{arch}
|
// {node_abi}-{platform}-{arch}
|
||||||
public getNodeTriple(): string {
|
public getNodeTriple(): string {
|
||||||
return this.getRuntimeAbi() + '-' + this.target.platform + '-' + this.target.arch;
|
return this.getRuntimeAbi() + "-" + this.target.platform + "-" + this.target.arch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTargetId(): TargetId {
|
public getTargetId(): TargetId {
|
||||||
@@ -76,15 +70,15 @@ export default class HakEnv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isWin(): boolean {
|
public isWin(): boolean {
|
||||||
return this.target.platform === 'win32';
|
return this.target.platform === "win32";
|
||||||
}
|
}
|
||||||
|
|
||||||
public isMac(): boolean {
|
public isMac(): boolean {
|
||||||
return this.target.platform === 'darwin';
|
return this.target.platform === "darwin";
|
||||||
}
|
}
|
||||||
|
|
||||||
public isLinux(): boolean {
|
public isLinux(): boolean {
|
||||||
return this.target.platform === 'linux';
|
return this.target.platform === "linux";
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTargetArch(): Arch {
|
public getTargetArch(): Arch {
|
||||||
@@ -95,11 +89,11 @@ export default class HakEnv {
|
|||||||
return isHostId(this.target.id);
|
return isHostId(this.target.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public makeGypEnv(): Record<string, string> {
|
public makeGypEnv(): Record<string, string | undefined> {
|
||||||
return Object.assign({}, process.env, {
|
return Object.assign({}, process.env, {
|
||||||
npm_config_arch: this.target.arch,
|
npm_config_arch: this.target.arch,
|
||||||
npm_config_target_arch: this.target.arch,
|
npm_config_target_arch: this.target.arch,
|
||||||
npm_config_disturl: 'https://electronjs.org/headers',
|
npm_config_disturl: "https://electronjs.org/headers",
|
||||||
npm_config_runtime: this.runtime,
|
npm_config_runtime: this.runtime,
|
||||||
npm_config_target: this.runtimeVersion,
|
npm_config_target: this.runtimeVersion,
|
||||||
npm_config_build_from_source: true,
|
npm_config_build_from_source: true,
|
||||||
@@ -107,12 +101,8 @@ export default class HakEnv {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getNodeModuleBin(name: string): string {
|
|
||||||
return path.join(this.projectRoot, 'node_modules', '.bin', name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public wantsStaticSqlCipherUnix(): boolean {
|
public wantsStaticSqlCipherUnix(): boolean {
|
||||||
return this.isMac() || process.env.SQLCIPHER_STATIC == '1';
|
return this.isMac() || process.env.SQLCIPHER_STATIC == "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
public wantsStaticSqlCipher(): boolean {
|
public wantsStaticSqlCipher(): boolean {
|
||||||
|
|||||||
+31
-48
@@ -14,44 +14,29 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import findNpmPrefix from 'find-npm-prefix';
|
import findNpmPrefix from "find-npm-prefix";
|
||||||
|
|
||||||
import HakEnv from './hakEnv';
|
import HakEnv from "./hakEnv";
|
||||||
import { TargetId } from './target';
|
import { TargetId } from "./target";
|
||||||
import { DependencyInfo } from './dep';
|
import { DependencyInfo } from "./dep";
|
||||||
|
|
||||||
const GENERALCOMMANDS = [
|
const GENERALCOMMANDS = ["target"];
|
||||||
'target',
|
|
||||||
];
|
|
||||||
|
|
||||||
// These can only be run on specific modules
|
// These can only be run on specific modules
|
||||||
const MODULECOMMANDS = [
|
const MODULECOMMANDS = ["check", "fetch", "link", "fetchDeps", "build", "copy", "clean"];
|
||||||
'check',
|
|
||||||
'fetch',
|
|
||||||
'link',
|
|
||||||
'fetchDeps',
|
|
||||||
'build',
|
|
||||||
'copy',
|
|
||||||
'clean',
|
|
||||||
];
|
|
||||||
|
|
||||||
// Shortcuts for multiple commands at once (useful for building universal binaries
|
// Shortcuts for multiple commands at once (useful for building universal binaries
|
||||||
// because you can run the fetch/fetchDeps/build for each arch and then copy/link once)
|
// because you can run the fetch/fetchDeps/build for each arch and then copy/link once)
|
||||||
const METACOMMANDS = {
|
const METACOMMANDS: Record<string, string[]> = {
|
||||||
'fetchandbuild': ['check', 'fetch', 'fetchDeps', 'build'],
|
fetchandbuild: ["check", "fetch", "fetchDeps", "build"],
|
||||||
'copyandlink': ['copy', 'link'],
|
copyandlink: ["copy", "link"],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Scripts valid in a hak.json 'scripts' section
|
// Scripts valid in a hak.json 'scripts' section
|
||||||
const HAKSCRIPTS = [
|
const HAKSCRIPTS = ["check", "fetch", "fetchDeps", "build"];
|
||||||
'check',
|
|
||||||
'fetch',
|
|
||||||
'fetchDeps',
|
|
||||||
'build',
|
|
||||||
];
|
|
||||||
|
|
||||||
async function main() {
|
async function main(): Promise<void> {
|
||||||
const prefix = await findNpmPrefix(process.cwd());
|
const prefix = await findNpmPrefix(process.cwd());
|
||||||
let packageJson;
|
let packageJson;
|
||||||
try {
|
try {
|
||||||
@@ -65,11 +50,12 @@ async function main() {
|
|||||||
// Apply `--target <target>` option if specified
|
// Apply `--target <target>` option if specified
|
||||||
// Can be specified multiple times for the copy command to bundle
|
// Can be specified multiple times for the copy command to bundle
|
||||||
// multiple archs into a single universal output module)
|
// multiple archs into a single universal output module)
|
||||||
while (true) { // eslint-disable-line no-constant-condition
|
for (;;) {
|
||||||
const targetIndex = process.argv.indexOf('--target');
|
// eslint-disable-line no-constant-condition
|
||||||
|
const targetIndex = process.argv.indexOf("--target");
|
||||||
if (targetIndex === -1) break;
|
if (targetIndex === -1) break;
|
||||||
|
|
||||||
if ((targetIndex + 1) >= process.argv.length) {
|
if (targetIndex + 1 >= process.argv.length) {
|
||||||
console.error("--target option specified without a target");
|
console.error("--target option specified without a target");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@@ -77,7 +63,7 @@ async function main() {
|
|||||||
targetIds.push(process.argv.splice(targetIndex, 2)[1] as TargetId);
|
targetIds.push(process.argv.splice(targetIndex, 2)[1] as TargetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hakEnvs = targetIds.map(tid => new HakEnv(prefix, tid));
|
const hakEnvs = targetIds.map((tid) => new HakEnv(prefix, tid));
|
||||||
if (hakEnvs.length == 0) hakEnvs.push(new HakEnv(prefix, null));
|
if (hakEnvs.length == 0) hakEnvs.push(new HakEnv(prefix, null));
|
||||||
for (const h of hakEnvs) {
|
for (const h of hakEnvs) {
|
||||||
await h.init();
|
await h.init();
|
||||||
@@ -89,7 +75,7 @@ async function main() {
|
|||||||
const hakDepsCfg = packageJson.hakDependencies || {};
|
const hakDepsCfg = packageJson.hakDependencies || {};
|
||||||
|
|
||||||
for (const dep of Object.keys(hakDepsCfg)) {
|
for (const dep of Object.keys(hakDepsCfg)) {
|
||||||
const hakJsonPath = path.join(prefix, 'hak', dep, 'hak.json');
|
const hakJsonPath = path.join(prefix, "hak", dep, "hak.json");
|
||||||
let hakJson: Record<string, any>;
|
let hakJson: Record<string, any>;
|
||||||
try {
|
try {
|
||||||
hakJson = await require(hakJsonPath);
|
hakJson = await require(hakJsonPath);
|
||||||
@@ -102,20 +88,20 @@ async function main() {
|
|||||||
name: dep,
|
name: dep,
|
||||||
version: hakDepsCfg[dep],
|
version: hakDepsCfg[dep],
|
||||||
cfg: hakJson,
|
cfg: hakJson,
|
||||||
moduleHakDir: path.join(prefix, 'hak', dep),
|
moduleHakDir: path.join(prefix, "hak", dep),
|
||||||
moduleDotHakDir: path.join(hakEnv.dotHakDir, dep),
|
moduleDotHakDir: path.join(hakEnv.dotHakDir, dep),
|
||||||
moduleTargetDotHakDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId()),
|
moduleTargetDotHakDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId()),
|
||||||
moduleBuildDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), 'build'),
|
moduleBuildDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), "build"),
|
||||||
moduleBuildDirs: hakEnvs.map(h => path.join(h.dotHakDir, dep, h.getTargetId(), 'build')),
|
moduleBuildDirs: hakEnvs.map((h) => path.join(h.dotHakDir, dep, h.getTargetId(), "build")),
|
||||||
moduleOutDir: path.join(hakEnv.dotHakDir, 'hakModules', dep),
|
moduleOutDir: path.join(hakEnv.dotHakDir, "hakModules", dep),
|
||||||
nodeModuleBinDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), 'build', 'node_modules', '.bin'),
|
nodeModuleBinDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), "build", "node_modules", ".bin"),
|
||||||
depPrefix: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), 'opt'),
|
depPrefix: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), "opt"),
|
||||||
scripts: {},
|
scripts: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const s of HAKSCRIPTS) {
|
for (const s of HAKSCRIPTS) {
|
||||||
if (hakJson.scripts && hakJson.scripts[s]) {
|
if (hakJson.scripts && hakJson.scripts[s]) {
|
||||||
const scriptModule = await import(path.join(prefix, 'hak', dep, hakJson.scripts[s]));
|
const scriptModule = await import(path.join(prefix, "hak", dep, hakJson.scripts[s]));
|
||||||
if (scriptModule.__esModule) {
|
if (scriptModule.__esModule) {
|
||||||
deps[dep].scripts[s] = scriptModule.default;
|
deps[dep].scripts[s] = scriptModule.default;
|
||||||
} else {
|
} else {
|
||||||
@@ -127,14 +113,14 @@ async function main() {
|
|||||||
|
|
||||||
let cmds: string[];
|
let cmds: string[];
|
||||||
if (process.argv.length < 3) {
|
if (process.argv.length < 3) {
|
||||||
cmds = ['check', 'fetch', 'fetchDeps', 'build', 'copy', 'link'];
|
cmds = ["check", "fetch", "fetchDeps", "build", "copy", "link"];
|
||||||
} else if (METACOMMANDS[process.argv[2]]) {
|
} else if (METACOMMANDS[process.argv[2]]) {
|
||||||
cmds = METACOMMANDS[process.argv[2]];
|
cmds = METACOMMANDS[process.argv[2]];
|
||||||
} else {
|
} else {
|
||||||
cmds = [process.argv[2]];
|
cmds = [process.argv[2]];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hakEnvs.length > 1 && cmds.some(c => !['copy', 'link'].includes(c))) {
|
if (hakEnvs.length > 1 && cmds.some((c) => !["copy", "link"].includes(c))) {
|
||||||
// We allow link here too for convenience because it's completely arch independent
|
// We allow link here too for convenience because it's completely arch independent
|
||||||
console.error("Multiple targets only supported with the copy command");
|
console.error("Multiple targets only supported with the copy command");
|
||||||
return;
|
return;
|
||||||
@@ -145,7 +131,7 @@ async function main() {
|
|||||||
|
|
||||||
for (const cmd of cmds) {
|
for (const cmd of cmds) {
|
||||||
if (GENERALCOMMANDS.includes(cmd)) {
|
if (GENERALCOMMANDS.includes(cmd)) {
|
||||||
if (cmd === 'target') {
|
if (cmd === "target") {
|
||||||
console.log(hakEnv.getNodeTriple());
|
console.log(hakEnv.getNodeTriple());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -160,15 +146,12 @@ async function main() {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cmdFunc = (await import('./' + cmd)).default;
|
const cmdFunc = (await import("./" + cmd)).default;
|
||||||
|
|
||||||
for (const mod of modules) {
|
for (const mod of modules) {
|
||||||
const depInfo = deps[mod];
|
const depInfo = deps[mod];
|
||||||
if (depInfo === undefined) {
|
if (depInfo === undefined) {
|
||||||
console.log(
|
console.log("Module " + mod + " not found - is it in hakDependencies " + "in your package.json?");
|
||||||
"Module " + mod + " not found - is it in hakDependencies " +
|
|
||||||
"in your package.json?",
|
|
||||||
);
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
console.log("hak " + cmd + ": " + mod);
|
console.log("hak " + cmd + ": " + mod);
|
||||||
@@ -177,7 +160,7 @@ async function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch(err => {
|
main().catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
+15
-15
@@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import os from 'os';
|
import os from "os";
|
||||||
import fsProm from 'fs/promises';
|
import fsProm from "fs/promises";
|
||||||
import childProcess from 'child_process';
|
import childProcess from "child_process";
|
||||||
|
|
||||||
import HakEnv from './hakEnv';
|
import HakEnv from "./hakEnv";
|
||||||
import { DependencyInfo } from './dep';
|
import { DependencyInfo } from "./dep";
|
||||||
|
|
||||||
export default async function link(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
export default async function link(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
|
||||||
const yarnrc = path.join(hakEnv.projectRoot, '.yarnrc');
|
const yarnrc = path.join(hakEnv.projectRoot, ".yarnrc");
|
||||||
// this is fairly terrible but it's reasonably clunky to either parse a yarnrc
|
// this is fairly terrible but it's reasonably clunky to either parse a yarnrc
|
||||||
// properly or get yarn to do it, so this will probably suffice for now.
|
// properly or get yarn to do it, so this will probably suffice for now.
|
||||||
// We just check to see if there is a local .yarnrc at all, and assume that
|
// We just check to see if there is a local .yarnrc at all, and assume that
|
||||||
@@ -43,28 +43,28 @@ export default async function link(hakEnv: HakEnv, moduleInfo: DependencyInfo):
|
|||||||
// (ie. Windows absolute paths) but strings in quotes get parsed as
|
// (ie. Windows absolute paths) but strings in quotes get parsed as
|
||||||
// JSON so need to be valid JSON encoded strings (ie. have the
|
// JSON so need to be valid JSON encoded strings (ie. have the
|
||||||
// backslashes escaped). JSON.stringify will add quotes and escape.
|
// backslashes escaped). JSON.stringify will add quotes and escape.
|
||||||
'--link-folder ' + JSON.stringify(path.join(hakEnv.dotHakDir, 'links')) + os.EOL,
|
"--link-folder " + JSON.stringify(path.join(hakEnv.dotHakDir, "links")) + os.EOL,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const yarnCmd = 'yarn' + (hakEnv.isWin() ? '.cmd' : '');
|
const yarnCmd = "yarn" + (hakEnv.isWin() ? ".cmd" : "");
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(yarnCmd, ['link'], {
|
const proc = childProcess.spawn(yarnCmd, ["link"], {
|
||||||
cwd: moduleInfo.moduleOutDir,
|
cwd: moduleInfo.moduleOutDir,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
});
|
});
|
||||||
proc.on('exit', code => {
|
proc.on("exit", (code) => {
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const proc = childProcess.spawn(yarnCmd, ['link', moduleInfo.name], {
|
const proc = childProcess.spawn(yarnCmd, ["link", moduleInfo.name], {
|
||||||
cwd: hakEnv.projectRoot,
|
cwd: hakEnv.projectRoot,
|
||||||
stdio: 'inherit',
|
stdio: "inherit",
|
||||||
});
|
});
|
||||||
proc.on('exit', code => {
|
proc.on("exit", (code) => {
|
||||||
code ? reject(code) : resolve();
|
code ? reject(code) : resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+80
-85
@@ -20,29 +20,29 @@ import { GLIBC, MUSL, family as processLibC } from "detect-libc";
|
|||||||
// details in a single string.
|
// details in a single string.
|
||||||
// See https://doc.rust-lang.org/rustc/platform-support.html.
|
// See https://doc.rust-lang.org/rustc/platform-support.html.
|
||||||
export type TargetId =
|
export type TargetId =
|
||||||
'aarch64-apple-darwin' |
|
| "aarch64-apple-darwin"
|
||||||
'x86_64-apple-darwin' |
|
| "x86_64-apple-darwin"
|
||||||
'universal-apple-darwin' |
|
| "universal-apple-darwin"
|
||||||
'i686-pc-windows-msvc' |
|
| "i686-pc-windows-msvc"
|
||||||
'x86_64-pc-windows-msvc' |
|
| "x86_64-pc-windows-msvc"
|
||||||
'i686-unknown-linux-musl' |
|
| "i686-unknown-linux-musl"
|
||||||
'i686-unknown-linux-gnu' |
|
| "i686-unknown-linux-gnu"
|
||||||
'x86_64-unknown-linux-musl' |
|
| "x86_64-unknown-linux-musl"
|
||||||
'x86_64-unknown-linux-gnu' |
|
| "x86_64-unknown-linux-gnu"
|
||||||
'aarch64-unknown-linux-musl' |
|
| "aarch64-unknown-linux-musl"
|
||||||
'aarch64-unknown-linux-gnu' |
|
| "aarch64-unknown-linux-gnu"
|
||||||
'powerpc64le-unknown-linux-musl' |
|
| "powerpc64le-unknown-linux-musl"
|
||||||
'powerpc64le-unknown-linux-gnu';
|
| "powerpc64le-unknown-linux-gnu";
|
||||||
|
|
||||||
// Values are expected to match those used in `process.platform`.
|
// Values are expected to match those used in `process.platform`.
|
||||||
export type Platform = 'darwin' | 'linux' | 'win32';
|
export type Platform = "darwin" | "linux" | "win32";
|
||||||
|
|
||||||
// Values are expected to match those used in `process.arch`.
|
// Values are expected to match those used in `process.arch`.
|
||||||
export type Arch = 'arm64' | 'ia32' | 'x64' | 'ppc64' | 'universal';
|
export type Arch = "arm64" | "ia32" | "x64" | "ppc64" | "universal";
|
||||||
|
|
||||||
// Values are expected to match those used by Visual Studio's `vcvarsall.bat`.
|
// Values are expected to match those used by Visual Studio's `vcvarsall.bat`.
|
||||||
// See https://docs.microsoft.com/cpp/build/building-on-the-command-line?view=msvc-160#vcvarsall-syntax
|
// See https://docs.microsoft.com/cpp/build/building-on-the-command-line?view=msvc-160#vcvarsall-syntax
|
||||||
export type VcVarsArch = 'amd64' | 'arm64' | 'x86';
|
export type VcVarsArch = "amd64" | "arm64" | "x86";
|
||||||
|
|
||||||
export type Target = {
|
export type Target = {
|
||||||
id: TargetId;
|
id: TargetId;
|
||||||
@@ -51,140 +51,135 @@ export type Target = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type WindowsTarget = Target & {
|
export type WindowsTarget = Target & {
|
||||||
platform: 'win32';
|
platform: "win32";
|
||||||
vcVarsArch: VcVarsArch;
|
vcVarsArch: VcVarsArch;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LinuxTarget = Target & {
|
export type LinuxTarget = Target & {
|
||||||
platform: 'linux';
|
platform: "linux";
|
||||||
libC: typeof processLibC;
|
libC: typeof processLibC;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UniversalTarget = Target & {
|
export type UniversalTarget = Target & {
|
||||||
arch: 'universal';
|
arch: "universal";
|
||||||
subtargets: Target[];
|
subtargets: Target[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const aarch64AppleDarwin: Target = {
|
const aarch64AppleDarwin: Target = {
|
||||||
id: 'aarch64-apple-darwin',
|
id: "aarch64-apple-darwin",
|
||||||
platform: 'darwin',
|
platform: "darwin",
|
||||||
arch: 'arm64',
|
arch: "arm64",
|
||||||
};
|
};
|
||||||
|
|
||||||
const x8664AppleDarwin: Target = {
|
const x8664AppleDarwin: Target = {
|
||||||
id: 'x86_64-apple-darwin',
|
id: "x86_64-apple-darwin",
|
||||||
platform: 'darwin',
|
platform: "darwin",
|
||||||
arch: 'x64',
|
arch: "x64",
|
||||||
};
|
};
|
||||||
|
|
||||||
const universalAppleDarwin: UniversalTarget = {
|
const universalAppleDarwin: UniversalTarget = {
|
||||||
id: 'universal-apple-darwin',
|
id: "universal-apple-darwin",
|
||||||
platform: 'darwin',
|
platform: "darwin",
|
||||||
arch: 'universal',
|
arch: "universal",
|
||||||
subtargets: [
|
subtargets: [aarch64AppleDarwin, x8664AppleDarwin],
|
||||||
aarch64AppleDarwin,
|
|
||||||
x8664AppleDarwin,
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const i686PcWindowsMsvc: WindowsTarget = {
|
const i686PcWindowsMsvc: WindowsTarget = {
|
||||||
id: 'i686-pc-windows-msvc',
|
id: "i686-pc-windows-msvc",
|
||||||
platform: 'win32',
|
platform: "win32",
|
||||||
arch: 'ia32',
|
arch: "ia32",
|
||||||
vcVarsArch: 'x86',
|
vcVarsArch: "x86",
|
||||||
};
|
};
|
||||||
|
|
||||||
const x8664PcWindowsMsvc: WindowsTarget = {
|
const x8664PcWindowsMsvc: WindowsTarget = {
|
||||||
id: 'x86_64-pc-windows-msvc',
|
id: "x86_64-pc-windows-msvc",
|
||||||
platform: 'win32',
|
platform: "win32",
|
||||||
arch: 'x64',
|
arch: "x64",
|
||||||
vcVarsArch: 'amd64',
|
vcVarsArch: "amd64",
|
||||||
};
|
};
|
||||||
|
|
||||||
const x8664UnknownLinuxGnu: LinuxTarget = {
|
const x8664UnknownLinuxGnu: LinuxTarget = {
|
||||||
id: 'x86_64-unknown-linux-gnu',
|
id: "x86_64-unknown-linux-gnu",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'x64',
|
arch: "x64",
|
||||||
libC: GLIBC,
|
libC: GLIBC,
|
||||||
};
|
};
|
||||||
|
|
||||||
const x8664UnknownLinuxMusl: LinuxTarget = {
|
const x8664UnknownLinuxMusl: LinuxTarget = {
|
||||||
id: 'x86_64-unknown-linux-musl',
|
id: "x86_64-unknown-linux-musl",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'x64',
|
arch: "x64",
|
||||||
libC: MUSL,
|
libC: MUSL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const i686UnknownLinuxGnu: LinuxTarget = {
|
const i686UnknownLinuxGnu: LinuxTarget = {
|
||||||
id: 'i686-unknown-linux-gnu',
|
id: "i686-unknown-linux-gnu",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'ia32',
|
arch: "ia32",
|
||||||
libC: GLIBC,
|
libC: GLIBC,
|
||||||
};
|
};
|
||||||
|
|
||||||
const i686UnknownLinuxMusl: LinuxTarget = {
|
const i686UnknownLinuxMusl: LinuxTarget = {
|
||||||
id: 'i686-unknown-linux-musl',
|
id: "i686-unknown-linux-musl",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'ia32',
|
arch: "ia32",
|
||||||
libC: MUSL,
|
libC: MUSL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const aarch64UnknownLinuxGnu: LinuxTarget = {
|
const aarch64UnknownLinuxGnu: LinuxTarget = {
|
||||||
id: 'aarch64-unknown-linux-gnu',
|
id: "aarch64-unknown-linux-gnu",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'arm64',
|
arch: "arm64",
|
||||||
libC: GLIBC,
|
libC: GLIBC,
|
||||||
};
|
};
|
||||||
|
|
||||||
const aarch64UnknownLinuxMusl: LinuxTarget = {
|
const aarch64UnknownLinuxMusl: LinuxTarget = {
|
||||||
id: 'aarch64-unknown-linux-musl',
|
id: "aarch64-unknown-linux-musl",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'arm64',
|
arch: "arm64",
|
||||||
libC: MUSL,
|
libC: MUSL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const powerpc64leUnknownLinuxGnu: LinuxTarget = {
|
const powerpc64leUnknownLinuxGnu: LinuxTarget = {
|
||||||
id: 'powerpc64le-unknown-linux-gnu',
|
id: "powerpc64le-unknown-linux-gnu",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'ppc64',
|
arch: "ppc64",
|
||||||
libC: GLIBC,
|
libC: GLIBC,
|
||||||
};
|
};
|
||||||
|
|
||||||
const powerpc64leUnknownLinuxMusl: LinuxTarget = {
|
const powerpc64leUnknownLinuxMusl: LinuxTarget = {
|
||||||
id: 'powerpc64le-unknown-linux-musl',
|
id: "powerpc64le-unknown-linux-musl",
|
||||||
platform: 'linux',
|
platform: "linux",
|
||||||
arch: 'ppc64',
|
arch: "ppc64",
|
||||||
libC: MUSL,
|
libC: MUSL,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TARGETS: Record<TargetId, Target> = {
|
export const TARGETS: Record<TargetId, Target> = {
|
||||||
// macOS
|
// macOS
|
||||||
'aarch64-apple-darwin': aarch64AppleDarwin,
|
"aarch64-apple-darwin": aarch64AppleDarwin,
|
||||||
'x86_64-apple-darwin': x8664AppleDarwin,
|
"x86_64-apple-darwin": x8664AppleDarwin,
|
||||||
'universal-apple-darwin': universalAppleDarwin,
|
"universal-apple-darwin": universalAppleDarwin,
|
||||||
// Windows
|
// Windows
|
||||||
'i686-pc-windows-msvc': i686PcWindowsMsvc,
|
"i686-pc-windows-msvc": i686PcWindowsMsvc,
|
||||||
'x86_64-pc-windows-msvc': x8664PcWindowsMsvc,
|
"x86_64-pc-windows-msvc": x8664PcWindowsMsvc,
|
||||||
// Linux
|
// Linux
|
||||||
'i686-unknown-linux-musl': i686UnknownLinuxMusl,
|
"i686-unknown-linux-musl": i686UnknownLinuxMusl,
|
||||||
'i686-unknown-linux-gnu': i686UnknownLinuxGnu,
|
"i686-unknown-linux-gnu": i686UnknownLinuxGnu,
|
||||||
'x86_64-unknown-linux-musl': x8664UnknownLinuxMusl,
|
"x86_64-unknown-linux-musl": x8664UnknownLinuxMusl,
|
||||||
'x86_64-unknown-linux-gnu': x8664UnknownLinuxGnu,
|
"x86_64-unknown-linux-gnu": x8664UnknownLinuxGnu,
|
||||||
'aarch64-unknown-linux-musl': aarch64UnknownLinuxMusl,
|
"aarch64-unknown-linux-musl": aarch64UnknownLinuxMusl,
|
||||||
'aarch64-unknown-linux-gnu': aarch64UnknownLinuxGnu,
|
"aarch64-unknown-linux-gnu": aarch64UnknownLinuxGnu,
|
||||||
'powerpc64le-unknown-linux-musl': powerpc64leUnknownLinuxMusl,
|
"powerpc64le-unknown-linux-musl": powerpc64leUnknownLinuxMusl,
|
||||||
'powerpc64le-unknown-linux-gnu': powerpc64leUnknownLinuxGnu,
|
"powerpc64le-unknown-linux-gnu": powerpc64leUnknownLinuxGnu,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getHost(): Target | undefined {
|
export function getHost(): Target | undefined {
|
||||||
return Object.values(TARGETS).find(target => (
|
return Object.values(TARGETS).find(
|
||||||
target.platform === process.platform &&
|
(target) =>
|
||||||
target.arch === process.arch &&
|
target.platform === process.platform &&
|
||||||
(
|
target.arch === process.arch &&
|
||||||
process.platform !== 'linux' ||
|
(process.platform !== "linux" || (target as LinuxTarget).libC === processLibC),
|
||||||
(target as LinuxTarget).libC === processLibC
|
);
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isHostId(id: TargetId): boolean {
|
export function isHostId(id: TargetId): boolean {
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"target": "es2017",
|
|
||||||
"module": "commonjs",
|
|
||||||
"sourceMap": false,
|
|
||||||
"lib": [
|
|
||||||
"es2019",
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"./**/*.ts"
|
|
||||||
],
|
|
||||||
"ts-node": {
|
|
||||||
"transpileOnly": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Checks for the presence of a webapp, inspects its version and sets the
|
|
||||||
* version metadata of the package to match.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const fs = require('fs').promises;
|
|
||||||
const asar = require('asar');
|
|
||||||
const childProcess = require('child_process');
|
|
||||||
|
|
||||||
async function versionFromAsar() {
|
|
||||||
try {
|
|
||||||
await fs.stat('webapp.asar');
|
|
||||||
} catch (e) {
|
|
||||||
console.log("No 'webapp.asar' found. Run 'yarn run fetch'");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return asar.extractFile('webapp.asar', 'version').toString().trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setPackageVersion(ver) {
|
|
||||||
// set version in package.json: electron-builder will use this to populate
|
|
||||||
// all the various version fields
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
childProcess.execFile(process.platform === 'win32' ? 'yarn.cmd' : 'yarn', [
|
|
||||||
'version',
|
|
||||||
'-s',
|
|
||||||
'--no-git-tag-version', // This also means "don't commit to git" as it turns out
|
|
||||||
'--new-version',
|
|
||||||
ver,
|
|
||||||
], (err) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main(args) {
|
|
||||||
let version = args[0];
|
|
||||||
|
|
||||||
if (version === undefined) version = await versionFromAsar();
|
|
||||||
|
|
||||||
await setPackageVersion(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (require.main === module) {
|
|
||||||
main(process.argv.slice(2)).then((ret) => process.exit(ret));
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { versionFromAsar, setPackageVersion };
|
|
||||||
Executable
+64
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env -S npx ts-node
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks for the presence of a webapp, inspects its version and sets the
|
||||||
|
* version metadata of the package to match.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { promises as fs } from "fs";
|
||||||
|
import * as asar from "asar";
|
||||||
|
import * as childProcess from "child_process";
|
||||||
|
|
||||||
|
export async function versionFromAsar(): Promise<string> {
|
||||||
|
try {
|
||||||
|
await fs.stat("webapp.asar");
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error("No 'webapp.asar' found. Run 'yarn run fetch'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return asar.extractFile("webapp.asar", "version").toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setPackageVersion(ver: string): Promise<void> {
|
||||||
|
// set version in package.json: electron-builder will use this to populate
|
||||||
|
// all the various version fields
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
childProcess.execFile(
|
||||||
|
process.platform === "win32" ? "yarn.cmd" : "yarn",
|
||||||
|
[
|
||||||
|
"version",
|
||||||
|
"-s",
|
||||||
|
"--no-git-tag-version", // This also means "don't commit to git" as it turns out
|
||||||
|
"--new-version",
|
||||||
|
ver,
|
||||||
|
],
|
||||||
|
(err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main(args: string[]): Promise<number> {
|
||||||
|
let version = args[0];
|
||||||
|
|
||||||
|
if (version === undefined) version = await versionFromAsar();
|
||||||
|
|
||||||
|
await setPackageVersion(version);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
main(process.argv.slice(2))
|
||||||
|
.then((ret) => {
|
||||||
|
process.exit(ret);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"target": "es2017",
|
||||||
|
"module": "commonjs",
|
||||||
|
"sourceMap": false,
|
||||||
|
"strict": true,
|
||||||
|
"lib": ["es2019", "dom"]
|
||||||
|
},
|
||||||
|
"include": ["./**/*.ts"],
|
||||||
|
"ts-node": {
|
||||||
|
"transpileOnly": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+1
-1
@@ -50,5 +50,5 @@ declare module "keytar" {
|
|||||||
*
|
*
|
||||||
* @returns A promise for the array of found credentials.
|
* @returns A promise for the array of found credentials.
|
||||||
*/
|
*/
|
||||||
export function findCredentials(service: string): Promise<Array<{ account: string, password: string}>>;
|
export function findCredentials(service: string): Promise<Array<{ account: string; password: string }>>;
|
||||||
}
|
}
|
||||||
Vendored
+3
-3
@@ -86,7 +86,7 @@ declare module "matrix-seshat" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Seshat {
|
export class Seshat {
|
||||||
constructor(path: string, config?: IConfig);
|
public constructor(path: string, config?: IConfig);
|
||||||
public addEvent(matrixEvent: IMatrixEvent, profile?: IMatrixProfile): void;
|
public addEvent(matrixEvent: IMatrixEvent, profile?: IMatrixProfile): void;
|
||||||
public deleteEvent(eventId: string): Promise<boolean>;
|
public deleteEvent(eventId: string): Promise<boolean>;
|
||||||
public commit(force?: boolean): Promise<number>;
|
public commit(force?: boolean): Promise<number>;
|
||||||
@@ -132,7 +132,7 @@ declare module "matrix-seshat" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SeshatRecovery {
|
export class SeshatRecovery {
|
||||||
constructor(path: string, config?: IConfig);
|
public constructor(path: string, config?: IConfig);
|
||||||
public info(): IRecoveryInfo;
|
public info(): IRecoveryInfo;
|
||||||
public getUserVersion(): Promise<number>;
|
public getUserVersion(): Promise<number>;
|
||||||
public shutdown(): Promise<void>;
|
public shutdown(): Promise<void>;
|
||||||
@@ -140,6 +140,6 @@ declare module "matrix-seshat" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ReindexError extends Error {
|
export class ReindexError extends Error {
|
||||||
constructor(message?: string);
|
public constructor(message?: string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+124
-129
@@ -19,18 +19,11 @@ limitations under the License.
|
|||||||
|
|
||||||
// Squirrel on windows starts the app with various flags as hooks to tell us when we've been installed/uninstalled etc.
|
// Squirrel on windows starts the app with various flags as hooks to tell us when we've been installed/uninstalled etc.
|
||||||
import "./squirrelhooks";
|
import "./squirrelhooks";
|
||||||
import {
|
import { app, BrowserWindow, Menu, autoUpdater, protocol, dialog } from "electron";
|
||||||
app,
|
|
||||||
BrowserWindow,
|
|
||||||
Menu,
|
|
||||||
autoUpdater,
|
|
||||||
protocol,
|
|
||||||
dialog,
|
|
||||||
} from "electron";
|
|
||||||
import AutoLaunch from "auto-launch";
|
import AutoLaunch from "auto-launch";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import windowStateKeeper from 'electron-window-state';
|
import windowStateKeeper from "electron-window-state";
|
||||||
import Store from 'electron-store';
|
import Store from "electron-store";
|
||||||
import fs, { promises as afs } from "fs";
|
import fs, { promises as afs } from "fs";
|
||||||
import { URL } from "url";
|
import { URL } from "url";
|
||||||
import minimist from "minimist";
|
import minimist from "minimist";
|
||||||
@@ -40,11 +33,11 @@ import "./keytar";
|
|||||||
import "./seshat";
|
import "./seshat";
|
||||||
import "./settings";
|
import "./settings";
|
||||||
import * as tray from "./tray";
|
import * as tray from "./tray";
|
||||||
import { buildMenuTemplate } from './vectormenu';
|
import { buildMenuTemplate } from "./vectormenu";
|
||||||
import webContentsHandler from './webcontents-handler';
|
import webContentsHandler from "./webcontents-handler";
|
||||||
import * as updater from './updater';
|
import * as updater from "./updater";
|
||||||
import { getProfileFromDeeplink, protocolInit } from './protocol';
|
import { getProfileFromDeeplink, protocolInit } from "./protocol";
|
||||||
import { _t, AppLocalization } from './language-helper';
|
import { _t, AppLocalization } from "./language-helper";
|
||||||
import Input = Electron.Input;
|
import Input = Electron.Input;
|
||||||
|
|
||||||
const argv = minimist(process.argv, {
|
const argv = minimist(process.argv, {
|
||||||
@@ -65,8 +58,7 @@ if (argv["help"]) {
|
|||||||
console.log(" --no-update: Disable automatic updating.");
|
console.log(" --no-update: Disable automatic updating.");
|
||||||
console.log(" --hidden: Start the application hidden in the system tray.");
|
console.log(" --hidden: Start the application hidden in the system tray.");
|
||||||
console.log(" --help: Displays this help message.");
|
console.log(" --help: Displays this help message.");
|
||||||
console.log("And more such as --proxy, see:" +
|
console.log("And more such as --proxy, see:" + "https://electronjs.org/docs/api/command-line-switches");
|
||||||
"https://electronjs.org/docs/api/command-line-switches");
|
|
||||||
app.exit();
|
app.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +66,7 @@ if (argv["help"]) {
|
|||||||
// as soon as the app path is set, so pick a random path in it that must exist if it's a
|
// as soon as the app path is set, so pick a random path in it that must exist if it's a
|
||||||
// real user data directory.
|
// real user data directory.
|
||||||
function isRealUserDataDir(d: string): boolean {
|
function isRealUserDataDir(d: string): boolean {
|
||||||
return fs.existsSync(path.join(d, 'IndexedDB'));
|
return fs.existsSync(path.join(d, "IndexedDB"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we are passed a profile in the SSO callback url
|
// check if we are passed a profile in the SSO callback url
|
||||||
@@ -83,22 +75,22 @@ let userDataPath: string;
|
|||||||
const userDataPathInProtocol = getProfileFromDeeplink(argv["_"]);
|
const userDataPathInProtocol = getProfileFromDeeplink(argv["_"]);
|
||||||
if (userDataPathInProtocol) {
|
if (userDataPathInProtocol) {
|
||||||
userDataPath = userDataPathInProtocol;
|
userDataPath = userDataPathInProtocol;
|
||||||
} else if (argv['profile-dir']) {
|
} else if (argv["profile-dir"]) {
|
||||||
userDataPath = argv['profile-dir'];
|
userDataPath = argv["profile-dir"];
|
||||||
} else {
|
} else {
|
||||||
let newUserDataPath = app.getPath('userData');
|
let newUserDataPath = app.getPath("userData");
|
||||||
if (argv['profile']) {
|
if (argv["profile"]) {
|
||||||
newUserDataPath += '-' + argv['profile'];
|
newUserDataPath += "-" + argv["profile"];
|
||||||
}
|
}
|
||||||
const newUserDataPathExists = isRealUserDataDir(newUserDataPath);
|
const newUserDataPathExists = isRealUserDataDir(newUserDataPath);
|
||||||
let oldUserDataPath = path.join(app.getPath('appData'), app.getName().replace('Element', 'Riot'));
|
let oldUserDataPath = path.join(app.getPath("appData"), app.getName().replace("Element", "Riot"));
|
||||||
if (argv['profile']) {
|
if (argv["profile"]) {
|
||||||
oldUserDataPath += '-' + argv['profile'];
|
oldUserDataPath += "-" + argv["profile"];
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldUserDataPathExists = isRealUserDataDir(oldUserDataPath);
|
const oldUserDataPathExists = isRealUserDataDir(oldUserDataPath);
|
||||||
console.log(newUserDataPath + " exists: " + (newUserDataPathExists ? 'yes' : 'no'));
|
console.log(newUserDataPath + " exists: " + (newUserDataPathExists ? "yes" : "no"));
|
||||||
console.log(oldUserDataPath + " exists: " + (oldUserDataPathExists ? 'yes' : 'no'));
|
console.log(oldUserDataPath + " exists: " + (oldUserDataPathExists ? "yes" : "no"));
|
||||||
if (!newUserDataPathExists && oldUserDataPathExists) {
|
if (!newUserDataPathExists && oldUserDataPathExists) {
|
||||||
console.log("Using legacy user data path: " + oldUserDataPath);
|
console.log("Using legacy user data path: " + oldUserDataPath);
|
||||||
userDataPath = oldUserDataPath;
|
userDataPath = oldUserDataPath;
|
||||||
@@ -106,54 +98,53 @@ if (userDataPathInProtocol) {
|
|||||||
userDataPath = newUserDataPath;
|
userDataPath = newUserDataPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.setPath('userData', userDataPath);
|
app.setPath("userData", userDataPath);
|
||||||
|
|
||||||
async function tryPaths(name: string, root: string, rawPaths: string[]): Promise<string> {
|
async function tryPaths(name: string, root: string, rawPaths: string[]): Promise<string> {
|
||||||
// Make everything relative to root
|
// Make everything relative to root
|
||||||
const paths = rawPaths.map(p => path.join(root, p));
|
const paths = rawPaths.map((p) => path.join(root, p));
|
||||||
|
|
||||||
for (const p of paths) {
|
for (const p of paths) {
|
||||||
try {
|
try {
|
||||||
await afs.stat(p);
|
await afs.stat(p);
|
||||||
return p + '/';
|
return p + "/";
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
console.log(`Couldn't find ${name} files in any of: `);
|
console.log(`Couldn't find ${name} files in any of: `);
|
||||||
for (const p of paths) {
|
for (const p of paths) {
|
||||||
console.log("\t"+path.resolve(p));
|
console.log("\t" + path.resolve(p));
|
||||||
}
|
}
|
||||||
throw new Error(`Failed to find ${name} files`);
|
throw new Error(`Failed to find ${name} files`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const homeserverProps = ['default_is_url', 'default_hs_url', 'default_server_name', 'default_server_config'] as const;
|
const homeserverProps = ["default_is_url", "default_hs_url", "default_server_name", "default_server_config"] as const;
|
||||||
|
|
||||||
// Find the webapp resources and set up things that require them
|
// Find the webapp resources and set up things that require them
|
||||||
async function setupGlobals(): Promise<void> {
|
async function setupGlobals(): Promise<void> {
|
||||||
// find the webapp asar.
|
// find the webapp asar.
|
||||||
asarPath = await tryPaths("webapp", __dirname, [
|
asarPath = await tryPaths("webapp", __dirname, [
|
||||||
// If run from the source checkout, this will be in the directory above
|
// If run from the source checkout, this will be in the directory above
|
||||||
'../webapp.asar',
|
"../webapp.asar",
|
||||||
// but if run from a packaged application, electron-main.js will be in
|
// but if run from a packaged application, electron-main.js will be in
|
||||||
// a different asar file so it will be two levels above
|
// a different asar file so it will be two levels above
|
||||||
'../../webapp.asar',
|
"../../webapp.asar",
|
||||||
// also try without the 'asar' suffix to allow symlinking in a directory
|
// also try without the 'asar' suffix to allow symlinking in a directory
|
||||||
'../webapp',
|
"../webapp",
|
||||||
// from a packaged application
|
// from a packaged application
|
||||||
'../../webapp',
|
"../../webapp",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// we assume the resources path is in the same place as the asar
|
// we assume the resources path is in the same place as the asar
|
||||||
resPath = await tryPaths("res", path.dirname(asarPath), [
|
resPath = await tryPaths("res", path.dirname(asarPath), [
|
||||||
// If run from the source checkout
|
// If run from the source checkout
|
||||||
'res',
|
"res",
|
||||||
// if run from packaged application
|
// if run from packaged application
|
||||||
'',
|
"",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
global.vectorConfig = require(asarPath + 'config.json');
|
global.vectorConfig = require(asarPath + "config.json");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// it would be nice to check the error code here and bail if the config
|
// it would be nice to check the error code here and bail if the config
|
||||||
// is unparsable, but we get MODULE_NOT_FOUND in the case of a missing
|
// is unparsable, but we get MODULE_NOT_FOUND in the case of a missing
|
||||||
@@ -165,15 +156,15 @@ async function setupGlobals(): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
// Load local config and use it to override values from the one baked with the build
|
// Load local config and use it to override values from the one baked with the build
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const localConfig = require(path.join(app.getPath('userData'), 'config.json'));
|
const localConfig = require(path.join(app.getPath("userData"), "config.json"));
|
||||||
|
|
||||||
// If the local config has a homeserver defined, don't use the homeserver from the build
|
// If the local config has a homeserver defined, don't use the homeserver from the build
|
||||||
// config. This is to avoid a problem where Riot thinks there are multiple homeservers
|
// config. This is to avoid a problem where Riot thinks there are multiple homeservers
|
||||||
// defined, and panics as a result.
|
// defined, and panics as a result.
|
||||||
if (Object.keys(localConfig).find(k => homeserverProps.includes(<any>k))) {
|
if (Object.keys(localConfig).find((k) => homeserverProps.includes(<any>k))) {
|
||||||
// Rip out all the homeserver options from the vector config
|
// Rip out all the homeserver options from the vector config
|
||||||
global.vectorConfig = Object.keys(global.vectorConfig)
|
global.vectorConfig = Object.keys(global.vectorConfig)
|
||||||
.filter(k => !homeserverProps.includes(<any>k))
|
.filter((k) => !homeserverProps.includes(<any>k))
|
||||||
.reduce((obj, key) => {
|
.reduce((obj, key) => {
|
||||||
obj[key] = global.vectorConfig[key];
|
obj[key] = global.vectorConfig[key];
|
||||||
return obj;
|
return obj;
|
||||||
@@ -185,9 +176,10 @@ async function setupGlobals(): Promise<void> {
|
|||||||
if (e instanceof SyntaxError) {
|
if (e instanceof SyntaxError) {
|
||||||
dialog.showMessageBox({
|
dialog.showMessageBox({
|
||||||
type: "error",
|
type: "error",
|
||||||
title: `Your ${global.vectorConfig.brand || 'Element'} is misconfigured`,
|
title: `Your ${global.vectorConfig.brand || "Element"} is misconfigured`,
|
||||||
message: `Your custom ${global.vectorConfig.brand || 'Element'} configuration contains invalid JSON. ` +
|
message:
|
||||||
`Please correct the problem and reopen ${global.vectorConfig.brand || 'Element'}.`,
|
`Your custom ${global.vectorConfig.brand || "Element"} configuration contains invalid JSON. ` +
|
||||||
|
`Please correct the problem and reopen ${global.vectorConfig.brand || "Element"}.`,
|
||||||
detail: e.message || "",
|
detail: e.message || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -197,16 +189,16 @@ async function setupGlobals(): Promise<void> {
|
|||||||
|
|
||||||
// The tray icon
|
// The tray icon
|
||||||
// It's important to call `path.join` so we don't end up with the packaged asar in the final path.
|
// It's important to call `path.join` so we don't end up with the packaged asar in the final path.
|
||||||
const iconFile = `element.${process.platform === 'win32' ? 'ico' : 'png'}`;
|
const iconFile = `element.${process.platform === "win32" ? "ico" : "png"}`;
|
||||||
iconPath = path.join(resPath, "img", iconFile);
|
iconPath = path.join(resPath, "img", iconFile);
|
||||||
global.trayConfig = {
|
global.trayConfig = {
|
||||||
icon_path: iconPath,
|
icon_path: iconPath,
|
||||||
brand: global.vectorConfig.brand || 'Element',
|
brand: global.vectorConfig.brand || "Element",
|
||||||
};
|
};
|
||||||
|
|
||||||
// launcher
|
// launcher
|
||||||
global.launcher = new AutoLaunch({
|
global.launcher = new AutoLaunch({
|
||||||
name: global.vectorConfig.brand || 'Element',
|
name: global.vectorConfig.brand || "Element",
|
||||||
isHidden: true,
|
isHidden: true,
|
||||||
mac: {
|
mac: {
|
||||||
useLaunchAgent: true,
|
useLaunchAgent: true,
|
||||||
@@ -217,9 +209,9 @@ async function setupGlobals(): Promise<void> {
|
|||||||
async function moveAutoLauncher(): Promise<void> {
|
async function moveAutoLauncher(): Promise<void> {
|
||||||
// Look for an auto-launcher under 'Riot' and if we find one, port it's
|
// Look for an auto-launcher under 'Riot' and if we find one, port it's
|
||||||
// enabled/disabled-ness over to the new 'Element' launcher
|
// enabled/disabled-ness over to the new 'Element' launcher
|
||||||
if (!global.vectorConfig.brand || global.vectorConfig.brand === 'Element') {
|
if (!global.vectorConfig.brand || global.vectorConfig.brand === "Element") {
|
||||||
const oldLauncher = new AutoLaunch({
|
const oldLauncher = new AutoLaunch({
|
||||||
name: 'Riot',
|
name: "Riot",
|
||||||
isHidden: true,
|
isHidden: true,
|
||||||
mac: {
|
mac: {
|
||||||
useLaunchAgent: true,
|
useLaunchAgent: true,
|
||||||
@@ -238,26 +230,30 @@ global.store = new Store({ name: "electron-config" });
|
|||||||
global.appQuitting = false;
|
global.appQuitting = false;
|
||||||
|
|
||||||
const exitShortcuts: Array<(input: Input, platform: string) => boolean> = [
|
const exitShortcuts: Array<(input: Input, platform: string) => boolean> = [
|
||||||
(input, platform) => platform !== 'darwin' && input.alt && input.key.toUpperCase() === 'F4',
|
(input, platform): boolean => platform !== "darwin" && input.alt && input.key.toUpperCase() === "F4",
|
||||||
(input, platform) => platform !== 'darwin' && input.control && input.key.toUpperCase() === 'Q',
|
(input, platform): boolean => platform !== "darwin" && input.control && input.key.toUpperCase() === "Q",
|
||||||
(input, platform) => platform === 'darwin' && input.meta && input.key.toUpperCase() === 'Q',
|
(input, platform): boolean => platform === "darwin" && input.meta && input.key.toUpperCase() === "Q",
|
||||||
];
|
];
|
||||||
|
|
||||||
const warnBeforeExit = (event: Event, input: Input): void => {
|
const warnBeforeExit = (event: Event, input: Input): void => {
|
||||||
const shouldWarnBeforeExit = global.store.get('warnBeforeExit', true);
|
const shouldWarnBeforeExit = global.store.get("warnBeforeExit", true);
|
||||||
const exitShortcutPressed =
|
const exitShortcutPressed =
|
||||||
input.type === 'keyDown' && exitShortcuts.some(shortcutFn => shortcutFn(input, process.platform));
|
input.type === "keyDown" && exitShortcuts.some((shortcutFn) => shortcutFn(input, process.platform));
|
||||||
|
|
||||||
if (shouldWarnBeforeExit && exitShortcutPressed && global.mainWindow) {
|
if (shouldWarnBeforeExit && exitShortcutPressed && global.mainWindow) {
|
||||||
const shouldCancelCloseRequest = dialog.showMessageBoxSync(global.mainWindow, {
|
const shouldCancelCloseRequest =
|
||||||
type: "question",
|
dialog.showMessageBoxSync(global.mainWindow, {
|
||||||
buttons: [_t("Cancel"), _t("Close %(brand)s", {
|
type: "question",
|
||||||
brand: global.vectorConfig.brand || 'Element',
|
buttons: [
|
||||||
})],
|
_t("Cancel"),
|
||||||
message: _t("Are you sure you want to quit?"),
|
_t("Close %(brand)s", {
|
||||||
defaultId: 1,
|
brand: global.vectorConfig.brand || "Element",
|
||||||
cancelId: 0,
|
}),
|
||||||
}) === 0;
|
],
|
||||||
|
message: _t("Are you sure you want to quit?"),
|
||||||
|
defaultId: 1,
|
||||||
|
cancelId: 0,
|
||||||
|
}) === 0;
|
||||||
|
|
||||||
if (shouldCancelCloseRequest) {
|
if (shouldCancelCloseRequest) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -271,18 +267,18 @@ const warnBeforeExit = (event: Event, input: Input): void => {
|
|||||||
// no other way to catch this error).
|
// no other way to catch this error).
|
||||||
// Assuming we generally run from the console when developing,
|
// Assuming we generally run from the console when developing,
|
||||||
// this is far preferable.
|
// this is far preferable.
|
||||||
process.on('uncaughtException', function(error: Error): void {
|
process.on("uncaughtException", function (error: Error): void {
|
||||||
console.log('Unhandled exception', error);
|
console.log("Unhandled exception", error);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.commandLine.appendSwitch('--enable-usermedia-screen-capturing');
|
app.commandLine.appendSwitch("--enable-usermedia-screen-capturing");
|
||||||
if (!app.commandLine.hasSwitch('enable-features')) {
|
if (!app.commandLine.hasSwitch("enable-features")) {
|
||||||
app.commandLine.appendSwitch('enable-features', 'WebRTCPipeWireCapturer');
|
app.commandLine.appendSwitch("enable-features", "WebRTCPipeWireCapturer");
|
||||||
}
|
}
|
||||||
|
|
||||||
const gotLock = app.requestSingleInstanceLock();
|
const gotLock = app.requestSingleInstanceLock();
|
||||||
if (!gotLock) {
|
if (!gotLock) {
|
||||||
console.log('Other instance detected: exiting');
|
console.log("Other instance detected: exiting");
|
||||||
app.exit();
|
app.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,14 +290,16 @@ protocolInit();
|
|||||||
// work.
|
// work.
|
||||||
// Also mark it as secure (ie. accessing resources from this
|
// Also mark it as secure (ie. accessing resources from this
|
||||||
// protocol and HTTPS won't trigger mixed content warnings).
|
// protocol and HTTPS won't trigger mixed content warnings).
|
||||||
protocol.registerSchemesAsPrivileged([{
|
protocol.registerSchemesAsPrivileged([
|
||||||
scheme: 'vector',
|
{
|
||||||
privileges: {
|
scheme: "vector",
|
||||||
standard: true,
|
privileges: {
|
||||||
secure: true,
|
standard: true,
|
||||||
supportFetchAPI: true,
|
secure: true,
|
||||||
|
supportFetchAPI: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}]);
|
]);
|
||||||
|
|
||||||
// Turn the sandbox on for *all* windows we might generate. Doing this means we don't
|
// Turn the sandbox on for *all* windows we might generate. Doing this means we don't
|
||||||
// have to specify a `sandbox: true` to each BrowserWindow.
|
// have to specify a `sandbox: true` to each BrowserWindow.
|
||||||
@@ -315,15 +313,15 @@ protocol.registerSchemesAsPrivileged([{
|
|||||||
app.enableSandbox();
|
app.enableSandbox();
|
||||||
|
|
||||||
// We disable media controls here. We do this because calls use audio and video elements and they sometimes capture the media keys. See https://github.com/vector-im/element-web/issues/15704
|
// We disable media controls here. We do this because calls use audio and video elements and they sometimes capture the media keys. See https://github.com/vector-im/element-web/issues/15704
|
||||||
app.commandLine.appendSwitch('disable-features', 'HardwareMediaKeyHandling,MediaSessionService');
|
app.commandLine.appendSwitch("disable-features", "HardwareMediaKeyHandling,MediaSessionService");
|
||||||
|
|
||||||
// Disable hardware acceleration if the setting has been set.
|
// Disable hardware acceleration if the setting has been set.
|
||||||
if (global.store.get('disableHardwareAcceleration', false) === true) {
|
if (global.store.get("disableHardwareAcceleration", false) === true) {
|
||||||
console.log("Disabling hardware acceleration.");
|
console.log("Disabling hardware acceleration.");
|
||||||
app.disableHardwareAcceleration();
|
app.disableHardwareAcceleration();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('ready', async () => {
|
app.on("ready", async () => {
|
||||||
try {
|
try {
|
||||||
await setupGlobals();
|
await setupGlobals();
|
||||||
await moveAutoLauncher();
|
await moveAutoLauncher();
|
||||||
@@ -337,51 +335,51 @@ app.on('ready', async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argv['devtools']) {
|
if (argv["devtools"]) {
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const { default: installExt, REACT_DEVELOPER_TOOLS, REACT_PERF } = require('electron-devtools-installer');
|
const { default: installExt, REACT_DEVELOPER_TOOLS, REACT_PERF } = require("electron-devtools-installer");
|
||||||
installExt(REACT_DEVELOPER_TOOLS)
|
installExt(REACT_DEVELOPER_TOOLS)
|
||||||
.then((name: string) => console.log(`Added Extension: ${name}`))
|
.then((name: string) => console.log(`Added Extension: ${name}`))
|
||||||
.catch((err: unknown) => console.log('An error occurred: ', err));
|
.catch((err: unknown) => console.log("An error occurred: ", err));
|
||||||
installExt(REACT_PERF)
|
installExt(REACT_PERF)
|
||||||
.then((name: string) => console.log(`Added Extension: ${name}`))
|
.then((name: string) => console.log(`Added Extension: ${name}`))
|
||||||
.catch((err: unknown) => console.log('An error occurred: ', err));
|
.catch((err: unknown) => console.log("An error occurred: ", err));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol.registerFileProtocol('vector', (request, callback) => {
|
protocol.registerFileProtocol("vector", (request, callback) => {
|
||||||
if (request.method !== 'GET') {
|
if (request.method !== "GET") {
|
||||||
callback({ error: -322 }); // METHOD_NOT_SUPPORTED from chromium/src/net/base/net_error_list.h
|
callback({ error: -322 }); // METHOD_NOT_SUPPORTED from chromium/src/net/base/net_error_list.h
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedUrl = new URL(request.url);
|
const parsedUrl = new URL(request.url);
|
||||||
if (parsedUrl.protocol !== 'vector:') {
|
if (parsedUrl.protocol !== "vector:") {
|
||||||
callback({ error: -302 }); // UNKNOWN_URL_SCHEME
|
callback({ error: -302 }); // UNKNOWN_URL_SCHEME
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (parsedUrl.host !== 'vector') {
|
if (parsedUrl.host !== "vector") {
|
||||||
callback({ error: -105 }); // NAME_NOT_RESOLVED
|
callback({ error: -105 }); // NAME_NOT_RESOLVED
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = parsedUrl.pathname.split('/');
|
const target = parsedUrl.pathname.split("/");
|
||||||
|
|
||||||
// path starts with a '/'
|
// path starts with a '/'
|
||||||
if (target[0] !== '') {
|
if (target[0] !== "") {
|
||||||
callback({ error: -6 }); // FILE_NOT_FOUND
|
callback({ error: -6 }); // FILE_NOT_FOUND
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target[target.length - 1] == '') {
|
if (target[target.length - 1] == "") {
|
||||||
target[target.length - 1] = 'index.html';
|
target[target.length - 1] = "index.html";
|
||||||
}
|
}
|
||||||
|
|
||||||
let baseDir: string;
|
let baseDir: string;
|
||||||
if (target[1] === 'webapp') {
|
if (target[1] === "webapp") {
|
||||||
baseDir = asarPath;
|
baseDir = asarPath;
|
||||||
} else {
|
} else {
|
||||||
callback({ error: -6 }); // FILE_NOT_FOUND
|
callback({ error: -6 }); // FILE_NOT_FOUND
|
||||||
@@ -393,7 +391,7 @@ app.on('ready', async () => {
|
|||||||
baseDir = path.normalize(baseDir);
|
baseDir = path.normalize(baseDir);
|
||||||
|
|
||||||
const relTarget = path.normalize(path.join(...target.slice(2)));
|
const relTarget = path.normalize(path.join(...target.slice(2)));
|
||||||
if (relTarget.startsWith('..')) {
|
if (relTarget.startsWith("..")) {
|
||||||
callback({ error: -6 }); // FILE_NOT_FOUND
|
callback({ error: -6 }); // FILE_NOT_FOUND
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -404,13 +402,13 @@ app.on('ready', async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (argv['no-update']) {
|
if (argv["no-update"]) {
|
||||||
console.log('Auto update disabled via command line flag "--no-update"');
|
console.log('Auto update disabled via command line flag "--no-update"');
|
||||||
} else if (global.vectorConfig['update_base_url']) {
|
} else if (global.vectorConfig["update_base_url"]) {
|
||||||
console.log(`Starting auto update with base URL: ${global.vectorConfig['update_base_url']}`);
|
console.log(`Starting auto update with base URL: ${global.vectorConfig["update_base_url"]}`);
|
||||||
updater.start(global.vectorConfig['update_base_url']);
|
updater.start(global.vectorConfig["update_base_url"]);
|
||||||
} else {
|
} else {
|
||||||
console.log('No update_base_url is defined: auto update is disabled');
|
console.log("No update_base_url is defined: auto update is disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the previous window state with fallback to defaults
|
// Load the previous window state with fallback to defaults
|
||||||
@@ -422,11 +420,11 @@ app.on('ready', async () => {
|
|||||||
const preloadScript = path.normalize(`${__dirname}/preload.js`);
|
const preloadScript = path.normalize(`${__dirname}/preload.js`);
|
||||||
global.mainWindow = new BrowserWindow({
|
global.mainWindow = new BrowserWindow({
|
||||||
// https://www.electronjs.org/docs/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do
|
// https://www.electronjs.org/docs/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do
|
||||||
backgroundColor: '#fff',
|
backgroundColor: "#fff",
|
||||||
|
|
||||||
icon: iconPath,
|
icon: iconPath,
|
||||||
show: false,
|
show: false,
|
||||||
autoHideMenuBar: global.store.get('autoHideMenuBar', true),
|
autoHideMenuBar: global.store.get("autoHideMenuBar", true),
|
||||||
|
|
||||||
x: mainWindowState.x,
|
x: mainWindowState.x,
|
||||||
y: mainWindowState.y,
|
y: mainWindowState.y,
|
||||||
@@ -440,20 +438,20 @@ app.on('ready', async () => {
|
|||||||
webgl: true,
|
webgl: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
global.mainWindow.loadURL('vector://vector/webapp/');
|
global.mainWindow.loadURL("vector://vector/webapp/");
|
||||||
|
|
||||||
// Handle spellchecker
|
// Handle spellchecker
|
||||||
// For some reason spellCheckerEnabled isn't persisted, so we have to use the store here
|
// For some reason spellCheckerEnabled isn't persisted, so we have to use the store here
|
||||||
global.mainWindow.webContents.session.setSpellCheckerEnabled(global.store.get("spellCheckerEnabled", true));
|
global.mainWindow.webContents.session.setSpellCheckerEnabled(global.store.get("spellCheckerEnabled", true));
|
||||||
|
|
||||||
// Create trayIcon icon
|
// Create trayIcon icon
|
||||||
if (global.store.get('minimizeToTray', true)) tray.create(global.trayConfig);
|
if (global.store.get("minimizeToTray", true)) tray.create(global.trayConfig);
|
||||||
|
|
||||||
global.mainWindow.once('ready-to-show', () => {
|
global.mainWindow.once("ready-to-show", () => {
|
||||||
if (!global.mainWindow) return;
|
if (!global.mainWindow) return;
|
||||||
mainWindowState.manage(global.mainWindow);
|
mainWindowState.manage(global.mainWindow);
|
||||||
|
|
||||||
if (!argv['hidden']) {
|
if (!argv["hidden"]) {
|
||||||
global.mainWindow.show();
|
global.mainWindow.show();
|
||||||
} else {
|
} else {
|
||||||
// hide here explicitly because window manage above sometimes shows it
|
// hide here explicitly because window manage above sometimes shows it
|
||||||
@@ -461,21 +459,21 @@ app.on('ready', async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
global.mainWindow.webContents.on('before-input-event', warnBeforeExit);
|
global.mainWindow.webContents.on("before-input-event", warnBeforeExit);
|
||||||
|
|
||||||
global.mainWindow.on('closed', () => {
|
global.mainWindow.on("closed", () => {
|
||||||
global.mainWindow = null;
|
global.mainWindow = null;
|
||||||
});
|
});
|
||||||
global.mainWindow.on('close', async (e) => {
|
global.mainWindow.on("close", async (e) => {
|
||||||
// If we are not quitting and have a tray icon then minimize to tray
|
// If we are not quitting and have a tray icon then minimize to tray
|
||||||
if (!global.appQuitting && (tray.hasTray() || process.platform === 'darwin')) {
|
if (!global.appQuitting && (tray.hasTray() || process.platform === "darwin")) {
|
||||||
// On Mac, closing the window just hides it
|
// On Mac, closing the window just hides it
|
||||||
// (this is generally how single-window Mac apps
|
// (this is generally how single-window Mac apps
|
||||||
// behave, eg. Mail.app)
|
// behave, eg. Mail.app)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (global.mainWindow?.isFullScreen()) {
|
if (global.mainWindow?.isFullScreen()) {
|
||||||
global.mainWindow.once('leave-full-screen', () => global.mainWindow?.hide());
|
global.mainWindow.once("leave-full-screen", () => global.mainWindow?.hide());
|
||||||
|
|
||||||
global.mainWindow.setFullScreen(false);
|
global.mainWindow.setFullScreen(false);
|
||||||
} else {
|
} else {
|
||||||
@@ -486,12 +484,12 @@ app.on('ready', async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === "win32") {
|
||||||
// Handle forward/backward mouse buttons in Windows
|
// Handle forward/backward mouse buttons in Windows
|
||||||
global.mainWindow.on('app-command', (e, cmd) => {
|
global.mainWindow.on("app-command", (e, cmd) => {
|
||||||
if (cmd === 'browser-backward' && global.mainWindow?.webContents.canGoBack()) {
|
if (cmd === "browser-backward" && global.mainWindow?.webContents.canGoBack()) {
|
||||||
global.mainWindow.webContents.goBack();
|
global.mainWindow.webContents.goBack();
|
||||||
} else if (cmd === 'browser-forward' && global.mainWindow?.webContents.canGoForward()) {
|
} else if (cmd === "browser-forward" && global.mainWindow?.webContents.canGoForward()) {
|
||||||
global.mainWindow.webContents.goForward();
|
global.mainWindow.webContents.goForward();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -501,32 +499,29 @@ app.on('ready', async () => {
|
|||||||
|
|
||||||
global.appLocalization = new AppLocalization({
|
global.appLocalization = new AppLocalization({
|
||||||
store: global.store,
|
store: global.store,
|
||||||
components: [
|
components: [(): void => tray.initApplicationMenu(), (): void => Menu.setApplicationMenu(buildMenuTemplate())],
|
||||||
() => tray.initApplicationMenu(),
|
|
||||||
() => Menu.setApplicationMenu(buildMenuTemplate()),
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on("window-all-closed", () => {
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('activate', () => {
|
app.on("activate", () => {
|
||||||
global.mainWindow?.show();
|
global.mainWindow?.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
function beforeQuit(): void {
|
function beforeQuit(): void {
|
||||||
global.appQuitting = true;
|
global.appQuitting = true;
|
||||||
global.mainWindow?.webContents.send('before-quit');
|
global.mainWindow?.webContents.send("before-quit");
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('before-quit', beforeQuit);
|
app.on("before-quit", beforeQuit);
|
||||||
autoUpdater.on('before-quit-for-update', beforeQuit);
|
autoUpdater.on("before-quit-for-update", beforeQuit);
|
||||||
|
|
||||||
app.on('second-instance', (ev, commandLine, workingDirectory) => {
|
app.on("second-instance", (ev, commandLine, workingDirectory) => {
|
||||||
// If other instance launched with --hidden then skip showing window
|
// If other instance launched with --hidden then skip showing window
|
||||||
if (commandLine.includes('--hidden')) return;
|
if (commandLine.includes("--hidden")) return;
|
||||||
|
|
||||||
// Someone tried to run a second instance, we should focus our window.
|
// Someone tried to run a second instance, we should focus our window.
|
||||||
if (global.mainWindow) {
|
if (global.mainWindow) {
|
||||||
@@ -540,4 +535,4 @@ app.on('second-instance', (ev, commandLine, workingDirectory) => {
|
|||||||
// installer uses for the shortcut icon.
|
// installer uses for the shortcut icon.
|
||||||
// This makes notifications work on windows 8.1 (and is
|
// This makes notifications work on windows 8.1 (and is
|
||||||
// a noop on other platforms).
|
// a noop on other platforms).
|
||||||
app.setAppUserModelId('com.squirrel.element-desktop.Element');
|
app.setAppUserModelId("com.squirrel.element-desktop.Element");
|
||||||
@@ -17,5 +17,18 @@
|
|||||||
"Undo": "Malfari",
|
"Undo": "Malfari",
|
||||||
"Edit": "Redakti",
|
"Edit": "Redakti",
|
||||||
"Show/Hide": "Montri/Kaŝi",
|
"Show/Hide": "Montri/Kaŝi",
|
||||||
"Cancel": "Nuligi"
|
"Cancel": "Nuligi",
|
||||||
|
"Copy link address": "Kopiu ligilon de la bildo",
|
||||||
|
"Copy image address": "Kopiu adreson de la bildo",
|
||||||
|
"Copy email address": "Kopiu retadreson",
|
||||||
|
"Copy image": "Kopiu bildon",
|
||||||
|
"File": "Dosiero",
|
||||||
|
"Minimize": "Minimumigi",
|
||||||
|
"Window": "Fenestro",
|
||||||
|
"Select All": "Elekti Ĉiujn",
|
||||||
|
"Paste": "Enmeti",
|
||||||
|
"Copy": "Kopiu",
|
||||||
|
"Cut": "Tranĉi",
|
||||||
|
"Are you sure you want to quit?": "Ĉu vi certas, ke vi volas ĉesi?",
|
||||||
|
"Close %(brand)s": "Fermu %(brand)s"
|
||||||
}
|
}
|
||||||
@@ -32,5 +32,7 @@
|
|||||||
"Are you sure you want to quit?": "Er du sikker på at du vil slutte?",
|
"Are you sure you want to quit?": "Er du sikker på at du vil slutte?",
|
||||||
"Cancel": "Avbryt",
|
"Cancel": "Avbryt",
|
||||||
"Services": "Tjenester",
|
"Services": "Tjenester",
|
||||||
"Hide Others": "Skjul Andre"
|
"Hide Others": "Skjul Andre",
|
||||||
|
"Bring All to Front": "Flytt Alt Frem",
|
||||||
|
"Toggle Full Screen": "Veksle Fullskjerm"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"Hide": "Itago",
|
||||||
|
"Delete": "Alisin",
|
||||||
|
"Paste and Match Style": "I-paste at Tumugma ng Style",
|
||||||
|
"Paste": "I-paste",
|
||||||
|
"Copy": "I-copy",
|
||||||
|
"Cut": "I-cut",
|
||||||
|
"Redo": "Redo",
|
||||||
|
"Undo": "Undo",
|
||||||
|
"Edit": "I-edit",
|
||||||
|
"Quit": "Magsara",
|
||||||
|
"Show/Hide": "Ipakita/itago",
|
||||||
|
"Are you sure you want to quit?": "Sigurado ka ba na gusto mong magsara?",
|
||||||
|
"Close %(brand)s": "Isara ang %(brand)s",
|
||||||
|
"Cancel": "Kanselahin"
|
||||||
|
}
|
||||||
+33
-34
@@ -22,8 +22,8 @@ import { randomArray } from "./utils";
|
|||||||
import { Settings } from "./settings";
|
import { Settings } from "./settings";
|
||||||
import { keytar } from "./keytar";
|
import { keytar } from "./keytar";
|
||||||
|
|
||||||
ipcMain.on('setBadgeCount', function(_ev: IpcMainEvent, count: number): void {
|
ipcMain.on("setBadgeCount", function (_ev: IpcMainEvent, count: number): void {
|
||||||
if (process.platform !== 'win32') {
|
if (process.platform !== "win32") {
|
||||||
// only set badgeCount on Mac/Linux, the docs say that only those platforms support it but turns out Electron
|
// only set badgeCount on Mac/Linux, the docs say that only those platforms support it but turns out Electron
|
||||||
// has some Windows support too, and in some Windows environments this leads to two badges rendering atop
|
// has some Windows support too, and in some Windows environments this leads to two badges rendering atop
|
||||||
// each other. See https://github.com/vector-im/element-web/issues/16942
|
// each other. See https://github.com/vector-im/element-web/issues/16942
|
||||||
@@ -35,10 +35,10 @@ ipcMain.on('setBadgeCount', function(_ev: IpcMainEvent, count: number): void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let focusHandlerAttached = false;
|
let focusHandlerAttached = false;
|
||||||
ipcMain.on('loudNotification', function(): void {
|
ipcMain.on("loudNotification", function (): void {
|
||||||
if (process.platform === 'win32' && global.mainWindow && !global.mainWindow.isFocused() && !focusHandlerAttached) {
|
if (process.platform === "win32" && global.mainWindow && !global.mainWindow.isFocused() && !focusHandlerAttached) {
|
||||||
global.mainWindow.flashFrame(true);
|
global.mainWindow.flashFrame(true);
|
||||||
global.mainWindow.once('focus', () => {
|
global.mainWindow.once("focus", () => {
|
||||||
global.mainWindow?.flashFrame(false);
|
global.mainWindow?.flashFrame(false);
|
||||||
focusHandlerAttached = false;
|
focusHandlerAttached = false;
|
||||||
});
|
});
|
||||||
@@ -47,17 +47,17 @@ ipcMain.on('loudNotification', function(): void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let powerSaveBlockerId: number | null = null;
|
let powerSaveBlockerId: number | null = null;
|
||||||
ipcMain.on('app_onAction', function(_ev: IpcMainEvent, payload) {
|
ipcMain.on("app_onAction", function (_ev: IpcMainEvent, payload) {
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case 'call_state': {
|
case "call_state": {
|
||||||
if (powerSaveBlockerId !== null && powerSaveBlocker.isStarted(powerSaveBlockerId)) {
|
if (powerSaveBlockerId !== null && powerSaveBlocker.isStarted(powerSaveBlockerId)) {
|
||||||
if (payload.state === 'ended') {
|
if (payload.state === "ended") {
|
||||||
powerSaveBlocker.stop(powerSaveBlockerId);
|
powerSaveBlocker.stop(powerSaveBlockerId);
|
||||||
powerSaveBlockerId = null;
|
powerSaveBlockerId = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (powerSaveBlockerId === null && payload.state === 'connected') {
|
if (powerSaveBlockerId === null && payload.state === "connected") {
|
||||||
powerSaveBlockerId = powerSaveBlocker.start('prevent-display-sleep');
|
powerSaveBlockerId = powerSaveBlocker.start("prevent-display-sleep");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -65,35 +65,35 @@ ipcMain.on('app_onAction', function(_ev: IpcMainEvent, payload) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
|
||||||
if (!global.mainWindow) return;
|
if (!global.mainWindow) return;
|
||||||
|
|
||||||
const args = payload.args || [];
|
const args = payload.args || [];
|
||||||
let ret: any;
|
let ret: any;
|
||||||
|
|
||||||
switch (payload.name) {
|
switch (payload.name) {
|
||||||
case 'getUpdateFeedUrl':
|
case "getUpdateFeedUrl":
|
||||||
ret = autoUpdater.getFeedURL();
|
ret = autoUpdater.getFeedURL();
|
||||||
break;
|
break;
|
||||||
case 'getSettingValue': {
|
case "getSettingValue": {
|
||||||
const [settingName] = args;
|
const [settingName] = args;
|
||||||
const setting = Settings[settingName];
|
const setting = Settings[settingName];
|
||||||
ret = await setting.read();
|
ret = await setting.read();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'setSettingValue': {
|
case "setSettingValue": {
|
||||||
const [settingName, value] = args;
|
const [settingName, value] = args;
|
||||||
const setting = Settings[settingName];
|
const setting = Settings[settingName];
|
||||||
await setting.write(value);
|
await setting.write(value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'setLanguage':
|
case "setLanguage":
|
||||||
global.appLocalization.setAppLocale(args[0]);
|
global.appLocalization.setAppLocale(args[0]);
|
||||||
break;
|
break;
|
||||||
case 'getAppVersion':
|
case "getAppVersion":
|
||||||
ret = app.getVersion();
|
ret = app.getVersion();
|
||||||
break;
|
break;
|
||||||
case 'focusWindow':
|
case "focusWindow":
|
||||||
if (global.mainWindow.isMinimized()) {
|
if (global.mainWindow.isMinimized()) {
|
||||||
global.mainWindow.restore();
|
global.mainWindow.restore();
|
||||||
} else if (!global.mainWindow.isVisible()) {
|
} else if (!global.mainWindow.isVisible()) {
|
||||||
@@ -102,31 +102,31 @@ ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
|||||||
global.mainWindow.focus();
|
global.mainWindow.focus();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'getConfig':
|
case "getConfig":
|
||||||
ret = global.vectorConfig;
|
ret = global.vectorConfig;
|
||||||
break;
|
break;
|
||||||
case 'navigateBack':
|
case "navigateBack":
|
||||||
if (global.mainWindow.webContents.canGoBack()) {
|
if (global.mainWindow.webContents.canGoBack()) {
|
||||||
global.mainWindow.webContents.goBack();
|
global.mainWindow.webContents.goBack();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'navigateForward':
|
case "navigateForward":
|
||||||
if (global.mainWindow.webContents.canGoForward()) {
|
if (global.mainWindow.webContents.canGoForward()) {
|
||||||
global.mainWindow.webContents.goForward();
|
global.mainWindow.webContents.goForward();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'setSpellCheckEnabled':
|
case "setSpellCheckEnabled":
|
||||||
if (typeof args[0] !== 'boolean') return;
|
if (typeof args[0] !== "boolean") return;
|
||||||
|
|
||||||
global.mainWindow.webContents.session.setSpellCheckerEnabled(args[0]);
|
global.mainWindow.webContents.session.setSpellCheckerEnabled(args[0]);
|
||||||
global.store.set("spellCheckerEnabled", args[0]);
|
global.store.set("spellCheckerEnabled", args[0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'getSpellCheckEnabled':
|
case "getSpellCheckEnabled":
|
||||||
ret = global.store.get("spellCheckerEnabled", true);
|
ret = global.store.get("spellCheckerEnabled", true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'setSpellCheckLanguages':
|
case "setSpellCheckLanguages":
|
||||||
try {
|
try {
|
||||||
global.mainWindow.webContents.session.setSpellCheckerLanguages(args[0]);
|
global.mainWindow.webContents.session.setSpellCheckerLanguages(args[0]);
|
||||||
} catch (er) {
|
} catch (er) {
|
||||||
@@ -134,18 +134,18 @@ ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'getSpellCheckLanguages':
|
case "getSpellCheckLanguages":
|
||||||
ret = global.mainWindow.webContents.session.getSpellCheckerLanguages();
|
ret = global.mainWindow.webContents.session.getSpellCheckerLanguages();
|
||||||
break;
|
break;
|
||||||
case 'getAvailableSpellCheckLanguages':
|
case "getAvailableSpellCheckLanguages":
|
||||||
ret = global.mainWindow.webContents.session.availableSpellCheckerLanguages;
|
ret = global.mainWindow.webContents.session.availableSpellCheckerLanguages;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'startSSOFlow':
|
case "startSSOFlow":
|
||||||
recordSSOSession(args[0]);
|
recordSSOSession(args[0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'getPickleKey':
|
case "getPickleKey":
|
||||||
try {
|
try {
|
||||||
ret = await keytar?.getPassword("element.io", `${args[0]}|${args[1]}`);
|
ret = await keytar?.getPassword("element.io", `${args[0]}|${args[1]}`);
|
||||||
// migrate from riot.im (remove once we think there will no longer be
|
// migrate from riot.im (remove once we think there will no longer be
|
||||||
@@ -160,7 +160,7 @@ ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'createPickleKey':
|
case "createPickleKey":
|
||||||
try {
|
try {
|
||||||
const pickleKey = await randomArray(32);
|
const pickleKey = await randomArray(32);
|
||||||
await keytar?.setPassword("element.io", `${args[0]}|${args[1]}`, pickleKey);
|
await keytar?.setPassword("element.io", `${args[0]}|${args[1]}`, pickleKey);
|
||||||
@@ -170,7 +170,7 @@ ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'destroyPickleKey':
|
case "destroyPickleKey":
|
||||||
try {
|
try {
|
||||||
await keytar?.deletePassword("element.io", `${args[0]}|${args[1]}`);
|
await keytar?.deletePassword("element.io", `${args[0]}|${args[1]}`);
|
||||||
// migrate from riot.im (remove once we think there will no longer be
|
// migrate from riot.im (remove once we think there will no longer be
|
||||||
@@ -178,7 +178,7 @@ ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
|||||||
await keytar?.deletePassword("riot.im", `${args[0]}|${args[1]}`);
|
await keytar?.deletePassword("riot.im", `${args[0]}|${args[1]}`);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
break;
|
break;
|
||||||
case 'getDesktopCapturerSources':
|
case "getDesktopCapturerSources":
|
||||||
ret = (await desktopCapturer.getSources(args[0])).map((source) => ({
|
ret = (await desktopCapturer.getSources(args[0])).map((source) => ({
|
||||||
id: source.id,
|
id: source.id,
|
||||||
name: source.name,
|
name: source.name,
|
||||||
@@ -187,16 +187,15 @@ ipcMain.on('ipcCall', async function(_ev: IpcMainEvent, payload) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
global.mainWindow.webContents.send('ipcReply', {
|
global.mainWindow.webContents.send("ipcReply", {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
error: "Unknown IPC Call: " + payload.name,
|
error: "Unknown IPC Call: " + payload.name,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
global.mainWindow.webContents.send('ipcReply', {
|
global.mainWindow.webContents.send("ipcReply", {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
reply: ret,
|
reply: ret,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
+1
-1
@@ -19,7 +19,7 @@ import type * as Keytar from "keytar"; // Hak dependency type
|
|||||||
let keytar: typeof Keytar | undefined;
|
let keytar: typeof Keytar | undefined;
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
keytar = require('keytar');
|
keytar = require("keytar");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if ((<NodeJS.ErrnoException>e).code === "MODULE_NOT_FOUND") {
|
if ((<NodeJS.ErrnoException>e).code === "MODULE_NOT_FOUND") {
|
||||||
console.log("Keytar isn't installed; secure key storage is disabled.");
|
console.log("Keytar isn't installed; secure key storage is disabled.");
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ limitations under the License.
|
|||||||
|
|
||||||
import counterpart from "counterpart";
|
import counterpart from "counterpart";
|
||||||
|
|
||||||
import type Store from 'electron-store';
|
import type Store from "electron-store";
|
||||||
|
|
||||||
const FALLBACK_LOCALE = 'en';
|
const FALLBACK_LOCALE = "en";
|
||||||
|
|
||||||
export function _td(text: string): string {
|
export function _td(text: string): string {
|
||||||
return text;
|
return text;
|
||||||
@@ -44,11 +44,11 @@ export function _t(text: string, variables: IVariables = {}): string {
|
|||||||
Object.keys(variables).forEach((key) => {
|
Object.keys(variables).forEach((key) => {
|
||||||
if (variables[key] === undefined) {
|
if (variables[key] === undefined) {
|
||||||
console.warn("safeCounterpartTranslate called with undefined interpolation name: " + key);
|
console.warn("safeCounterpartTranslate called with undefined interpolation name: " + key);
|
||||||
variables[key] = 'undefined';
|
variables[key] = "undefined";
|
||||||
}
|
}
|
||||||
if (variables[key] === null) {
|
if (variables[key] === null) {
|
||||||
console.warn("safeCounterpartTranslate called with null interpolation name: " + key);
|
console.warn("safeCounterpartTranslate called with null interpolation name: " + key);
|
||||||
variables[key] = 'null';
|
variables[key] = "null";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let translated = counterpart.translate(text, variables);
|
let translated = counterpart.translate(text, variables);
|
||||||
@@ -71,10 +71,10 @@ export class AppLocalization {
|
|||||||
private readonly store: TypedStore;
|
private readonly store: TypedStore;
|
||||||
private readonly localizedComponents?: Set<Component>;
|
private readonly localizedComponents?: Set<Component>;
|
||||||
|
|
||||||
constructor({ store, components = [] }: { store: TypedStore, components: Component[] }) {
|
public constructor({ store, components = [] }: { store: TypedStore; components: Component[] }) {
|
||||||
counterpart.registerTranslations(FALLBACK_LOCALE, this.fetchTranslationJson("en_EN"));
|
counterpart.registerTranslations(FALLBACK_LOCALE, this.fetchTranslationJson("en_EN"));
|
||||||
counterpart.setFallbackLocale(FALLBACK_LOCALE);
|
counterpart.setFallbackLocale(FALLBACK_LOCALE);
|
||||||
counterpart.setSeparator('|');
|
counterpart.setSeparator("|");
|
||||||
|
|
||||||
if (Array.isArray(components)) {
|
if (Array.isArray(components)) {
|
||||||
this.localizedComponents = new Set(components);
|
this.localizedComponents = new Set(components);
|
||||||
@@ -119,7 +119,7 @@ export class AppLocalization {
|
|||||||
locales = [locales];
|
locales = [locales];
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadedLocales = locales.filter(locale => {
|
const loadedLocales = locales.filter((locale) => {
|
||||||
const translations = this.fetchTranslationJson(locale);
|
const translations = this.fetchTranslationJson(locale);
|
||||||
if (translations !== null) {
|
if (translations !== null) {
|
||||||
counterpart.registerTranslations(locale, translations);
|
counterpart.registerTranslations(locale, translations);
|
||||||
@@ -135,7 +135,7 @@ export class AppLocalization {
|
|||||||
|
|
||||||
public resetLocalizedUI(): void {
|
public resetLocalizedUI(): void {
|
||||||
console.log("Resetting the UI components after locale change");
|
console.log("Resetting the UI components after locale change");
|
||||||
this.localizedComponents?.forEach(componentSetup => {
|
this.localizedComponents?.forEach((componentSetup) => {
|
||||||
if (typeof componentSetup === "function") {
|
if (typeof componentSetup === "function") {
|
||||||
componentSetup();
|
componentSetup();
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-19
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ipcRenderer, contextBridge, IpcRendererEvent } from 'electron';
|
import { ipcRenderer, contextBridge, IpcRendererEvent } from "electron";
|
||||||
|
|
||||||
// Expose only expected IPC wrapper APIs to the renderer process to avoid
|
// Expose only expected IPC wrapper APIs to the renderer process to avoid
|
||||||
// handing out generalised messaging access.
|
// handing out generalised messaging access.
|
||||||
@@ -36,22 +36,19 @@ const CHANNELS = [
|
|||||||
"userDownloadAction",
|
"userDownloadAction",
|
||||||
];
|
];
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld(
|
contextBridge.exposeInMainWorld("electron", {
|
||||||
"electron",
|
on(channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void): void {
|
||||||
{
|
if (!CHANNELS.includes(channel)) {
|
||||||
on(channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void): void {
|
console.error(`Unknown IPC channel ${channel} ignored`);
|
||||||
if (!CHANNELS.includes(channel)) {
|
return;
|
||||||
console.error(`Unknown IPC channel ${channel} ignored`);
|
}
|
||||||
return;
|
ipcRenderer.on(channel, listener);
|
||||||
}
|
|
||||||
ipcRenderer.on(channel, listener);
|
|
||||||
},
|
|
||||||
send(channel: string, ...args: any[]): void {
|
|
||||||
if (!CHANNELS.includes(channel)) {
|
|
||||||
console.error(`Unknown IPC channel ${channel} ignored`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ipcRenderer.send(channel, ...args);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
send(channel: string, ...args: any[]): void {
|
||||||
|
if (!CHANNELS.includes(channel)) {
|
||||||
|
console.error(`Unknown IPC channel ${channel} ignored`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipcRenderer.send(channel, ...args);
|
||||||
|
},
|
||||||
|
});
|
||||||
+11
-10
@@ -67,7 +67,7 @@ function writeStore(data: Record<string, string>): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function recordSSOSession(sessionID: string): void {
|
export function recordSSOSession(sessionID: string): void {
|
||||||
const userDataPath = app.getPath('userData');
|
const userDataPath = app.getPath("userData");
|
||||||
const store = readStore();
|
const store = readStore();
|
||||||
for (const key in store) {
|
for (const key in store) {
|
||||||
// ensure each instance only has one (the latest) session ID to prevent the file growing unbounded
|
// ensure each instance only has one (the latest) session ID to prevent the file growing unbounded
|
||||||
@@ -82,7 +82,7 @@ export function recordSSOSession(sessionID: string): void {
|
|||||||
|
|
||||||
export function getProfileFromDeeplink(args: string[]): string | undefined {
|
export function getProfileFromDeeplink(args: string[]): string | undefined {
|
||||||
// check if we are passed a profile in the SSO callback url
|
// check if we are passed a profile in the SSO callback url
|
||||||
const deeplinkUrl = args.find(arg => arg.startsWith(PROTOCOL + '//'));
|
const deeplinkUrl = args.find((arg) => arg.startsWith(PROTOCOL + "//"));
|
||||||
if (deeplinkUrl?.includes(SEARCH_PARAM)) {
|
if (deeplinkUrl?.includes(SEARCH_PARAM)) {
|
||||||
const parsedUrl = new URL(deeplinkUrl);
|
const parsedUrl = new URL(deeplinkUrl);
|
||||||
if (parsedUrl.protocol === PROTOCOL) {
|
if (parsedUrl.protocol === PROTOCOL) {
|
||||||
@@ -98,25 +98,26 @@ export function protocolInit(): void {
|
|||||||
// get all args except `hidden` as it'd mean the app would not get focused
|
// get all args except `hidden` as it'd mean the app would not get focused
|
||||||
// XXX: passing args to protocol handlers only works on Windows, so unpackaged deep-linking
|
// XXX: passing args to protocol handlers only works on Windows, so unpackaged deep-linking
|
||||||
// --profile/--profile-dir are passed via the SEARCH_PARAM var in the callback url
|
// --profile/--profile-dir are passed via the SEARCH_PARAM var in the callback url
|
||||||
const args = process.argv.slice(1).filter(arg => arg !== "--hidden" && arg !== "-hidden");
|
const args = process.argv.slice(1).filter((arg) => arg !== "--hidden" && arg !== "-hidden");
|
||||||
if (app.isPackaged) {
|
if (app.isPackaged) {
|
||||||
app.setAsDefaultProtocolClient('element', process.execPath, args);
|
app.setAsDefaultProtocolClient("element", process.execPath, args);
|
||||||
} else if (process.platform === 'win32') { // on Mac/Linux this would just cause the electron binary to open
|
} else if (process.platform === "win32") {
|
||||||
|
// on Mac/Linux this would just cause the electron binary to open
|
||||||
// special handler for running without being packaged, e.g `electron .` by passing our app path to electron
|
// special handler for running without being packaged, e.g `electron .` by passing our app path to electron
|
||||||
app.setAsDefaultProtocolClient('element', process.execPath, [app.getAppPath(), ...args]);
|
app.setAsDefaultProtocolClient("element", process.execPath, [app.getAppPath(), ...args]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === "darwin") {
|
||||||
// Protocol handler for macos
|
// Protocol handler for macos
|
||||||
app.on('open-url', function(ev, url) {
|
app.on("open-url", function (ev, url) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
processUrl(url);
|
processUrl(url);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Protocol handler for win32/Linux
|
// Protocol handler for win32/Linux
|
||||||
app.on('second-instance', (ev, commandLine) => {
|
app.on("second-instance", (ev, commandLine) => {
|
||||||
const url = commandLine[commandLine.length - 1];
|
const url = commandLine[commandLine.length - 1];
|
||||||
if (!url.startsWith(PROTOCOL + '//')) return;
|
if (!url.startsWith(PROTOCOL + "//")) return;
|
||||||
processUrl(url);
|
processUrl(url);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-32
@@ -34,7 +34,7 @@ let ReindexError: typeof ReindexErrorType;
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const seshatModule = require('matrix-seshat');
|
const seshatModule = require("matrix-seshat");
|
||||||
Seshat = seshatModule.Seshat;
|
Seshat = seshatModule.Seshat;
|
||||||
SeshatRecovery = seshatModule.SeshatRecovery;
|
SeshatRecovery = seshatModule.SeshatRecovery;
|
||||||
ReindexError = seshatModule.ReindexError;
|
ReindexError = seshatModule.ReindexError;
|
||||||
@@ -75,29 +75,29 @@ const deleteContents = async (p: string): Promise<void> => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
ipcMain.on("seshat", async function (_ev: IpcMainEvent, payload): Promise<void> {
|
||||||
if (!global.mainWindow) return;
|
if (!global.mainWindow) return;
|
||||||
|
|
||||||
// We do this here to ensure we get the path after --profile has been resolved
|
// We do this here to ensure we get the path after --profile has been resolved
|
||||||
const eventStorePath = path.join(app.getPath('userData'), 'EventStore');
|
const eventStorePath = path.join(app.getPath("userData"), "EventStore");
|
||||||
|
|
||||||
const sendError = (id: string, e: Error) => {
|
const sendError = (id: string, e: Error): void => {
|
||||||
const error = {
|
const error = {
|
||||||
message: e.message,
|
message: e.message,
|
||||||
};
|
};
|
||||||
|
|
||||||
global.mainWindow?.webContents.send('seshatReply', { id, error });
|
global.mainWindow?.webContents.send("seshatReply", { id, error });
|
||||||
};
|
};
|
||||||
|
|
||||||
const args = payload.args || [];
|
const args = payload.args || [];
|
||||||
let ret: any;
|
let ret: any;
|
||||||
|
|
||||||
switch (payload.name) {
|
switch (payload.name) {
|
||||||
case 'supportsEventIndexing':
|
case "supportsEventIndexing":
|
||||||
ret = seshatSupported;
|
ret = seshatSupported;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'initEventIndex':
|
case "initEventIndex":
|
||||||
if (eventIndex === null) {
|
if (eventIndex === null) {
|
||||||
const userId = args[0];
|
const userId = args[0];
|
||||||
const deviceId = args[1];
|
const deviceId = args[1];
|
||||||
@@ -127,8 +127,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteContents(eventStorePath);
|
await deleteContents(eventStorePath);
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
await recoveryIndex.reindex();
|
await recoveryIndex.reindex();
|
||||||
}
|
}
|
||||||
@@ -142,7 +141,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'closeEventIndex':
|
case "closeEventIndex":
|
||||||
if (eventIndex !== null) {
|
if (eventIndex !== null) {
|
||||||
const index = eventIndex;
|
const index = eventIndex;
|
||||||
eventIndex = null;
|
eventIndex = null;
|
||||||
@@ -156,26 +155,24 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'deleteEventIndex': {
|
case "deleteEventIndex": {
|
||||||
try {
|
try {
|
||||||
await deleteContents(eventStorePath);
|
await deleteContents(eventStorePath);
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'isEventIndexEmpty':
|
case "isEventIndexEmpty":
|
||||||
if (eventIndex === null) ret = true;
|
if (eventIndex === null) ret = true;
|
||||||
else ret = await eventIndex.isEmpty();
|
else ret = await eventIndex.isEmpty();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'isRoomIndexed':
|
case "isRoomIndexed":
|
||||||
if (eventIndex === null) ret = false;
|
if (eventIndex === null) ret = false;
|
||||||
else ret = await eventIndex.isRoomIndexed(args[0]);
|
else ret = await eventIndex.isRoomIndexed(args[0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'addEventToIndex':
|
case "addEventToIndex":
|
||||||
try {
|
try {
|
||||||
eventIndex?.addEvent(args[0], args[1]);
|
eventIndex?.addEvent(args[0], args[1]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -184,7 +181,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'deleteEvent':
|
case "deleteEvent":
|
||||||
try {
|
try {
|
||||||
ret = await eventIndex?.deleteEvent(args[0]);
|
ret = await eventIndex?.deleteEvent(args[0]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -193,7 +190,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'commitLiveEvents':
|
case "commitLiveEvents":
|
||||||
try {
|
try {
|
||||||
ret = await eventIndex?.commit();
|
ret = await eventIndex?.commit();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -202,7 +199,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'searchEventIndex':
|
case "searchEventIndex":
|
||||||
try {
|
try {
|
||||||
ret = await eventIndex?.search(args[0]);
|
ret = await eventIndex?.search(args[0]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -211,12 +208,11 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'addHistoricEvents':
|
case "addHistoricEvents":
|
||||||
if (eventIndex === null) ret = false;
|
if (eventIndex === null) ret = false;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
ret = await eventIndex.addHistoricEvents(
|
ret = await eventIndex.addHistoricEvents(args[0], args[1], args[2]);
|
||||||
args[0], args[1], args[2]);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
sendError(payload.id, <Error>e);
|
sendError(payload.id, <Error>e);
|
||||||
return;
|
return;
|
||||||
@@ -224,7 +220,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'getStats':
|
case "getStats":
|
||||||
if (eventIndex === null) ret = 0;
|
if (eventIndex === null) ret = 0;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@@ -236,7 +232,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'removeCrawlerCheckpoint':
|
case "removeCrawlerCheckpoint":
|
||||||
if (eventIndex === null) ret = false;
|
if (eventIndex === null) ret = false;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@@ -248,7 +244,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'addCrawlerCheckpoint':
|
case "addCrawlerCheckpoint":
|
||||||
if (eventIndex === null) ret = false;
|
if (eventIndex === null) ret = false;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@@ -260,7 +256,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'loadFileEvents':
|
case "loadFileEvents":
|
||||||
if (eventIndex === null) ret = [];
|
if (eventIndex === null) ret = [];
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@@ -272,7 +268,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'loadCheckpoints':
|
case "loadCheckpoints":
|
||||||
if (eventIndex === null) ret = [];
|
if (eventIndex === null) ret = [];
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@@ -283,7 +279,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'setUserVersion':
|
case "setUserVersion":
|
||||||
if (eventIndex === null) break;
|
if (eventIndex === null) break;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@@ -295,7 +291,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'getUserVersion':
|
case "getUserVersion":
|
||||||
if (eventIndex === null) ret = 0;
|
if (eventIndex === null) ret = 0;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@@ -308,14 +304,14 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
global.mainWindow.webContents.send('seshatReply', {
|
global.mainWindow.webContents.send("seshatReply", {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
error: "Unknown IPC Call: " + payload.name,
|
error: "Unknown IPC Call: " + payload.name,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
global.mainWindow.webContents.send('seshatReply', {
|
global.mainWindow.webContents.send("seshatReply", {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
reply: ret,
|
reply: ret,
|
||||||
});
|
});
|
||||||
|
|||||||
+8
-6
@@ -42,17 +42,19 @@ export const Settings: Record<string, Setting> = {
|
|||||||
global.store.set("warnBeforeExit", value);
|
global.store.set("warnBeforeExit", value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Electron.alwaysShowMenuBar": { // not supported on macOS
|
"Electron.alwaysShowMenuBar": {
|
||||||
|
// not supported on macOS
|
||||||
async read(): Promise<any> {
|
async read(): Promise<any> {
|
||||||
return !global.mainWindow!.autoHideMenuBar;
|
return !global.mainWindow!.autoHideMenuBar;
|
||||||
},
|
},
|
||||||
async write(value: any): Promise<void> {
|
async write(value: any): Promise<void> {
|
||||||
global.store.set('autoHideMenuBar', !value);
|
global.store.set("autoHideMenuBar", !value);
|
||||||
global.mainWindow!.autoHideMenuBar = !value;
|
global.mainWindow!.autoHideMenuBar = !value;
|
||||||
global.mainWindow!.setMenuBarVisibility(value);
|
global.mainWindow!.setMenuBarVisibility(value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Electron.showTrayIcon": { // not supported on macOS
|
"Electron.showTrayIcon": {
|
||||||
|
// not supported on macOS
|
||||||
async read(): Promise<any> {
|
async read(): Promise<any> {
|
||||||
return tray.hasTray();
|
return tray.hasTray();
|
||||||
},
|
},
|
||||||
@@ -63,15 +65,15 @@ export const Settings: Record<string, Setting> = {
|
|||||||
} else {
|
} else {
|
||||||
tray.destroy();
|
tray.destroy();
|
||||||
}
|
}
|
||||||
global.store.set('minimizeToTray', value);
|
global.store.set("minimizeToTray", value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Electron.enableHardwareAcceleration": {
|
"Electron.enableHardwareAcceleration": {
|
||||||
async read(): Promise<any> {
|
async read(): Promise<any> {
|
||||||
return !global.store.get('disableHardwareAcceleration', false);
|
return !global.store.get("disableHardwareAcceleration", false);
|
||||||
},
|
},
|
||||||
async write(value: any): Promise<void> {
|
async write(value: any): Promise<void> {
|
||||||
global.store.set('disableHardwareAcceleration', !value);
|
global.store.set("disableHardwareAcceleration", !value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
+10
-10
@@ -23,29 +23,29 @@ function runUpdateExe(args: string[]): Promise<void> {
|
|||||||
// Note that there's an Update.exe in the app-x.x.x directory and one in the parent
|
// Note that there's an Update.exe in the app-x.x.x directory and one in the parent
|
||||||
// directory: we need to run the one in the parent directory, because it discovers
|
// directory: we need to run the one in the parent directory, because it discovers
|
||||||
// information about the app by inspecting the directory it's run from.
|
// information about the app by inspecting the directory it's run from.
|
||||||
const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
|
const updateExe = path.resolve(path.dirname(process.execPath), "..", "Update.exe");
|
||||||
console.log(`Spawning '${updateExe}' with args '${args}'`);
|
console.log(`Spawning '${updateExe}' with args '${args}'`);
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
spawn(updateExe, args, {
|
spawn(updateExe, args, {
|
||||||
detached: true,
|
detached: true,
|
||||||
}).on('close', resolve);
|
}).on("close", resolve);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkSquirrelHooks(): boolean {
|
function checkSquirrelHooks(): boolean {
|
||||||
if (process.platform !== 'win32') return false;
|
if (process.platform !== "win32") return false;
|
||||||
const cmd = process.argv[1];
|
const cmd = process.argv[1];
|
||||||
const target = path.basename(process.execPath);
|
const target = path.basename(process.execPath);
|
||||||
if (cmd === '--squirrel-install') {
|
if (cmd === "--squirrel-install") {
|
||||||
runUpdateExe(['--createShortcut=' + target]).then(() => app.quit());
|
runUpdateExe(["--createShortcut=" + target]).then(() => app.quit());
|
||||||
return true;
|
return true;
|
||||||
} else if (cmd === '--squirrel-updated') {
|
} else if (cmd === "--squirrel-updated") {
|
||||||
app.quit();
|
app.quit();
|
||||||
return true;
|
return true;
|
||||||
} else if (cmd === '--squirrel-uninstall') {
|
} else if (cmd === "--squirrel-uninstall") {
|
||||||
runUpdateExe(['--removeShortcut=' + target]).then(() => app.quit());
|
runUpdateExe(["--removeShortcut=" + target]).then(() => app.quit());
|
||||||
return true;
|
return true;
|
||||||
} else if (cmd === '--squirrel-obsolete') {
|
} else if (cmd === "--squirrel-obsolete") {
|
||||||
app.quit();
|
app.quit();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-12
@@ -25,7 +25,7 @@ import { _t } from "./language-helper";
|
|||||||
let trayIcon: Tray | null = null;
|
let trayIcon: Tray | null = null;
|
||||||
|
|
||||||
export function hasTray(): boolean {
|
export function hasTray(): boolean {
|
||||||
return (trayIcon !== null);
|
return trayIcon !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function destroy(): void {
|
export function destroy(): void {
|
||||||
@@ -52,17 +52,17 @@ interface IConfig {
|
|||||||
|
|
||||||
export function create(config: IConfig): void {
|
export function create(config: IConfig): void {
|
||||||
// no trays on darwin
|
// no trays on darwin
|
||||||
if (process.platform === 'darwin' || trayIcon) return;
|
if (process.platform === "darwin" || trayIcon) return;
|
||||||
const defaultIcon = nativeImage.createFromPath(config.icon_path);
|
const defaultIcon = nativeImage.createFromPath(config.icon_path);
|
||||||
|
|
||||||
trayIcon = new Tray(defaultIcon);
|
trayIcon = new Tray(defaultIcon);
|
||||||
trayIcon.setToolTip(config.brand);
|
trayIcon.setToolTip(config.brand);
|
||||||
initApplicationMenu();
|
initApplicationMenu();
|
||||||
trayIcon.on('click', toggleWin);
|
trayIcon.on("click", toggleWin);
|
||||||
|
|
||||||
let lastFavicon: string | null = null;
|
let lastFavicon: string | null = null;
|
||||||
global.mainWindow?.webContents.on('page-favicon-updated', async function(ev, favicons) {
|
global.mainWindow?.webContents.on("page-favicon-updated", async function (ev, favicons) {
|
||||||
if (!favicons || favicons.length <= 0 || !favicons[0].startsWith('data:')) {
|
if (!favicons || favicons.length <= 0 || !favicons[0].startsWith("data:")) {
|
||||||
if (lastFavicon !== null) {
|
if (lastFavicon !== null) {
|
||||||
global.mainWindow?.setIcon(defaultIcon);
|
global.mainWindow?.setIcon(defaultIcon);
|
||||||
trayIcon?.setImage(defaultIcon);
|
trayIcon?.setImage(defaultIcon);
|
||||||
@@ -78,9 +78,9 @@ export function create(config: IConfig): void {
|
|||||||
let newFavicon = nativeImage.createFromDataURL(favicons[0]);
|
let newFavicon = nativeImage.createFromDataURL(favicons[0]);
|
||||||
|
|
||||||
// Windows likes ico's too much.
|
// Windows likes ico's too much.
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === "win32") {
|
||||||
try {
|
try {
|
||||||
const icoPath = path.join(app.getPath('temp'), 'win32_element_icon.ico');
|
const icoPath = path.join(app.getPath("temp"), "win32_element_icon.ico");
|
||||||
fs.writeFileSync(icoPath, await pngToIco(newFavicon.toPNG()));
|
fs.writeFileSync(icoPath, await pngToIco(newFavicon.toPNG()));
|
||||||
newFavicon = nativeImage.createFromPath(icoPath);
|
newFavicon = nativeImage.createFromPath(icoPath);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -92,7 +92,7 @@ export function create(config: IConfig): void {
|
|||||||
global.mainWindow?.setIcon(newFavicon);
|
global.mainWindow?.setIcon(newFavicon);
|
||||||
});
|
});
|
||||||
|
|
||||||
global.mainWindow?.webContents.on('page-title-updated', function(ev, title) {
|
global.mainWindow?.webContents.on("page-title-updated", function (ev, title) {
|
||||||
trayIcon?.setToolTip(title);
|
trayIcon?.setToolTip(title);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -104,13 +104,13 @@ export function initApplicationMenu(): void {
|
|||||||
|
|
||||||
const contextMenu = Menu.buildFromTemplate([
|
const contextMenu = Menu.buildFromTemplate([
|
||||||
{
|
{
|
||||||
label: _t('Show/Hide'),
|
label: _t("Show/Hide"),
|
||||||
click: toggleWin,
|
click: toggleWin,
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: _t('Quit'),
|
label: _t("Quit"),
|
||||||
click: function() {
|
click: function (): void {
|
||||||
app.quit();
|
app.quit();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
+38
-41
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { app, autoUpdater, ipcMain } from "electron";
|
import { autoUpdater, ipcMain } from "electron";
|
||||||
|
|
||||||
const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000;
|
const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000;
|
||||||
const INITIAL_UPDATE_DELAY_MS = 30 * 1000;
|
const INITIAL_UPDATE_DELAY_MS = 30 * 1000;
|
||||||
@@ -37,45 +37,39 @@ function pollForUpdates(): void {
|
|||||||
autoUpdater.checkForUpdates();
|
autoUpdater.checkForUpdates();
|
||||||
} else {
|
} else {
|
||||||
console.log("Skipping update check as download already present");
|
console.log("Skipping update check as download already present");
|
||||||
global.mainWindow?.webContents.send('update-downloaded', latestUpdateDownloaded);
|
global.mainWindow?.webContents.send("update-downloaded", latestUpdateDownloaded);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Couldn\'t check for update', e);
|
console.log("Couldn't check for update", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function start(updateBaseUrl: string): void {
|
export function start(updateBaseUrl: string): void {
|
||||||
if (updateBaseUrl.slice(-1) !== '/') {
|
if (updateBaseUrl.slice(-1) !== "/") {
|
||||||
updateBaseUrl = updateBaseUrl + '/';
|
updateBaseUrl = updateBaseUrl + "/";
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let url: string;
|
let url: string;
|
||||||
// For reasons best known to Squirrel, the way it checks for updates
|
let serverType: "json" | undefined;
|
||||||
// is completely different between macOS and windows. On macOS, it
|
|
||||||
// hits a URL that either gives it a 200 with some json or
|
if (process.platform === "darwin") {
|
||||||
// 204 No Content. On windows it takes a base path and looks for
|
// On macOS it takes a JSON file with a map between versions and their URLs
|
||||||
// files under that path.
|
url = `${updateBaseUrl}macos/releases.json`;
|
||||||
if (process.platform === 'darwin') {
|
serverType = "json";
|
||||||
// include the current version in the URL we hit. Electron doesn't add
|
} else if (process.platform === "win32") {
|
||||||
// it anywhere (apart from the User-Agent) so it's up to us. We could
|
// On windows it takes a base path and looks for files under that path.
|
||||||
// (and previously did) just use the User-Agent, but this doesn't
|
|
||||||
// rely on NSURLConnection setting the User-Agent to what we expect,
|
|
||||||
// and also acts as a convenient cache-buster to ensure that when the
|
|
||||||
// app updates it always gets a fresh value to avoid update-looping.
|
|
||||||
url = `${updateBaseUrl}macos/?localVersion=${encodeURIComponent(app.getVersion())}`;
|
|
||||||
} else if (process.platform === 'win32') {
|
|
||||||
url = `${updateBaseUrl}win32/${process.arch}/`;
|
url = `${updateBaseUrl}win32/${process.arch}/`;
|
||||||
} else {
|
} else {
|
||||||
// Squirrel / electron only supports auto-update on these two platforms.
|
// Squirrel / electron only supports auto-update on these two platforms.
|
||||||
// I'm not even going to try to guess which feed style they'd use if they
|
// I'm not even going to try to guess which feed style they'd use if they
|
||||||
// implemented it on Linux, or if it would be different again.
|
// implemented it on Linux, or if it would be different again.
|
||||||
console.log('Auto update not supported on this platform');
|
console.log("Auto update not supported on this platform");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
console.log(`Update URL: ${url}`);
|
console.log(`Update URL: ${url}`);
|
||||||
autoUpdater.setFeedURL({ url });
|
autoUpdater.setFeedURL({ url, serverType });
|
||||||
// We check for updates ourselves rather than using 'updater' because we need to
|
// We check for updates ourselves rather than using 'updater' because we need to
|
||||||
// do it in the main process (and we don't really need to check every 10 minutes:
|
// do it in the main process (and we don't really need to check every 10 minutes:
|
||||||
// every hour should be just fine for a desktop app)
|
// every hour should be just fine for a desktop app)
|
||||||
@@ -88,15 +82,15 @@ export function start(updateBaseUrl: string): void {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// will fail if running in debug mode
|
// will fail if running in debug mode
|
||||||
console.log('Couldn\'t enable update checking', err);
|
console.log("Couldn't enable update checking", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.on('install_update', installUpdate);
|
ipcMain.on("install_update", installUpdate);
|
||||||
ipcMain.on('check_updates', pollForUpdates);
|
ipcMain.on("check_updates", pollForUpdates);
|
||||||
|
|
||||||
function ipcChannelSendUpdateStatus(status: boolean | string): void {
|
function ipcChannelSendUpdateStatus(status: boolean | string): void {
|
||||||
global.mainWindow?.webContents.send('check_updates', status);
|
global.mainWindow?.webContents.send("check_updates", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ICachedUpdate {
|
interface ICachedUpdate {
|
||||||
@@ -108,23 +102,26 @@ interface ICachedUpdate {
|
|||||||
|
|
||||||
// cache the latest update which has been downloaded as electron offers no api to read it
|
// cache the latest update which has been downloaded as electron offers no api to read it
|
||||||
let latestUpdateDownloaded: ICachedUpdate;
|
let latestUpdateDownloaded: ICachedUpdate;
|
||||||
autoUpdater.on('update-available', function() {
|
autoUpdater
|
||||||
ipcChannelSendUpdateStatus(true);
|
.on("update-available", function () {
|
||||||
}).on('update-not-available', function() {
|
ipcChannelSendUpdateStatus(true);
|
||||||
if (latestUpdateDownloaded) {
|
})
|
||||||
// the only time we will get `update-not-available` if `latestUpdateDownloaded` is already set
|
.on("update-not-available", function () {
|
||||||
// is if the user used the Manual Update check and there is no update newer than the one we
|
if (latestUpdateDownloaded) {
|
||||||
// have downloaded, so show it to them as the latest again.
|
// the only time we will get `update-not-available` if `latestUpdateDownloaded` is already set
|
||||||
global.mainWindow?.webContents.send('update-downloaded', latestUpdateDownloaded);
|
// is if the user used the Manual Update check and there is no update newer than the one we
|
||||||
} else {
|
// have downloaded, so show it to them as the latest again.
|
||||||
ipcChannelSendUpdateStatus(false);
|
global.mainWindow?.webContents.send("update-downloaded", latestUpdateDownloaded);
|
||||||
}
|
} else {
|
||||||
}).on('error', function(error) {
|
ipcChannelSendUpdateStatus(false);
|
||||||
ipcChannelSendUpdateStatus(error.message);
|
}
|
||||||
});
|
})
|
||||||
|
.on("error", function (error) {
|
||||||
|
ipcChannelSendUpdateStatus(error.message);
|
||||||
|
});
|
||||||
|
|
||||||
autoUpdater.on('update-downloaded', (ev, releaseNotes, releaseName, releaseDate, updateURL) => {
|
autoUpdater.on("update-downloaded", (ev, releaseNotes, releaseName, releaseDate, updateURL) => {
|
||||||
// forward to renderer
|
// forward to renderer
|
||||||
latestUpdateDownloaded = { releaseNotes, releaseName, releaseDate, updateURL };
|
latestUpdateDownloaded = { releaseNotes, releaseName, releaseDate, updateURL };
|
||||||
global.mainWindow?.webContents.send('update-downloaded', latestUpdateDownloaded);
|
global.mainWindow?.webContents.send("update-downloaded", latestUpdateDownloaded);
|
||||||
});
|
});
|
||||||
+1
-1
@@ -22,7 +22,7 @@ export async function randomArray(size: number): Promise<string> {
|
|||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
resolve(buf.toString("base64").replace(/=+$/g, ''));
|
resolve(buf.toString("base64").replace(/=+$/g, ""));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+112
-101
@@ -14,125 +14,133 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { app, shell, Menu, MenuItem, MenuItemConstructorOptions } from 'electron';
|
import { app, shell, Menu, MenuItem, MenuItemConstructorOptions } from "electron";
|
||||||
|
|
||||||
import { _t } from './language-helper';
|
import { _t } from "./language-helper";
|
||||||
|
|
||||||
const isMac = process.platform === 'darwin';
|
const isMac = process.platform === "darwin";
|
||||||
|
|
||||||
export function buildMenuTemplate(): Menu {
|
export function buildMenuTemplate(): Menu {
|
||||||
// Menu template from http://electron.atom.io/docs/api/menu/, edited
|
// Menu template from http://electron.atom.io/docs/api/menu/, edited
|
||||||
const template: Array<(MenuItemConstructorOptions) | (MenuItem)> = [
|
const template: Array<MenuItemConstructorOptions | MenuItem> = [
|
||||||
{
|
{
|
||||||
label: _t('Edit'),
|
label: _t("Edit"),
|
||||||
accelerator: 'e',
|
accelerator: "e",
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
role: 'undo',
|
role: "undo",
|
||||||
label: _t('Undo'),
|
label: _t("Undo"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'redo',
|
role: "redo",
|
||||||
label: _t('Redo'),
|
label: _t("Redo"),
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
role: 'cut',
|
role: "cut",
|
||||||
label: _t('Cut'),
|
label: _t("Cut"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'copy',
|
role: "copy",
|
||||||
label: _t('Copy'),
|
label: _t("Copy"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'paste',
|
role: "paste",
|
||||||
label: _t('Paste'),
|
label: _t("Paste"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'pasteAndMatchStyle',
|
role: "pasteAndMatchStyle",
|
||||||
label: _t('Paste and Match Style'),
|
label: _t("Paste and Match Style"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'delete',
|
role: "delete",
|
||||||
label: _t('Delete'),
|
label: _t("Delete"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'selectAll',
|
role: "selectAll",
|
||||||
label: _t('Select All'),
|
label: _t("Select All"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _t('View'),
|
label: _t("View"),
|
||||||
accelerator: 'V',
|
accelerator: "V",
|
||||||
submenu: [
|
submenu: [
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
role: 'resetZoom',
|
role: "resetZoom",
|
||||||
accelerator: 'CmdOrCtrl+Num0',
|
accelerator: "CmdOrCtrl+Num0",
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'zoomIn',
|
role: "zoomIn",
|
||||||
accelerator: 'CmdOrCtrl+NumAdd',
|
accelerator: "CmdOrCtrl+NumAdd",
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'zoomOut',
|
role: "zoomOut",
|
||||||
accelerator: 'CmdOrCtrl+NumSub',
|
accelerator: "CmdOrCtrl+NumSub",
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'resetZoom',
|
role: "resetZoom",
|
||||||
label: _t('Actual Size'),
|
label: _t("Actual Size"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'zoomIn',
|
role: "zoomIn",
|
||||||
label: _t('Zoom In'),
|
label: _t("Zoom In"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'zoomOut',
|
role: "zoomOut",
|
||||||
label: _t('Zoom Out'),
|
label: _t("Zoom Out"),
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
// in macOS the Preferences menu item goes in the first menu
|
// in macOS the Preferences menu item goes in the first menu
|
||||||
...(!isMac ? [{
|
...(!isMac
|
||||||
label: _t('Preferences'),
|
? [
|
||||||
click() { global.mainWindow?.webContents.send('preferences'); },
|
{
|
||||||
}] : []),
|
label: _t("Preferences"),
|
||||||
|
click(): void {
|
||||||
|
global.mainWindow?.webContents.send("preferences");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
role: 'togglefullscreen',
|
role: "togglefullscreen",
|
||||||
label: _t('Toggle Full Screen'),
|
label: _t("Toggle Full Screen"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'toggleDevTools',
|
role: "toggleDevTools",
|
||||||
label: _t('Toggle Developer Tools'),
|
label: _t("Toggle Developer Tools"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _t('Window'),
|
label: _t("Window"),
|
||||||
accelerator: 'w',
|
accelerator: "w",
|
||||||
role: 'window',
|
role: "window",
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
role: 'minimize',
|
role: "minimize",
|
||||||
label: _t('Minimize'),
|
label: _t("Minimize"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'close',
|
role: "close",
|
||||||
label: _t('Close'),
|
label: _t("Close"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _t('Help'),
|
label: _t("Help"),
|
||||||
accelerator: 'h',
|
accelerator: "h",
|
||||||
role: 'help',
|
role: "help",
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: _t('Element Help'),
|
label: _t("Element Help"),
|
||||||
click() { shell.openExternal('https://element.io/help'); },
|
click(): void {
|
||||||
|
shell.openExternal("https://element.io/help");
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -142,92 +150,95 @@ export function buildMenuTemplate(): Menu {
|
|||||||
if (isMac) {
|
if (isMac) {
|
||||||
template.unshift({
|
template.unshift({
|
||||||
// first macOS menu is the name of the app
|
// first macOS menu is the name of the app
|
||||||
role: 'appMenu',
|
role: "appMenu",
|
||||||
label: app.name,
|
label: app.name,
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
role: 'about',
|
role: "about",
|
||||||
label: _t('About') + ' ' + app.name,
|
label: _t("About") + " " + app.name,
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: _t('Preferences') + '…',
|
label: _t("Preferences") + "…",
|
||||||
accelerator: 'Command+,', // Mac-only accelerator
|
accelerator: "Command+,", // Mac-only accelerator
|
||||||
click() { global.mainWindow?.webContents.send('preferences'); },
|
click(): void {
|
||||||
|
global.mainWindow?.webContents.send("preferences");
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
role: 'services',
|
role: "services",
|
||||||
label: _t('Services'),
|
label: _t("Services"),
|
||||||
submenu: [],
|
submenu: [],
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
role: 'hide',
|
role: "hide",
|
||||||
label: _t('Hide'),
|
label: _t("Hide"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'hideOthers',
|
role: "hideOthers",
|
||||||
label: _t('Hide Others'),
|
label: _t("Hide Others"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'unhide',
|
role: "unhide",
|
||||||
label: _t('Unhide'),
|
label: _t("Unhide"),
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
role: 'quit',
|
role: "quit",
|
||||||
label: _t('Quit'),
|
label: _t("Quit"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
// Edit menu.
|
// Edit menu.
|
||||||
// This has a 'speech' section on macOS
|
// This has a 'speech' section on macOS
|
||||||
(template[1].submenu as MenuItemConstructorOptions[]).push(
|
(template[1].submenu as MenuItemConstructorOptions[]).push(
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: _t('Speech'),
|
label: _t("Speech"),
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
role: 'startSpeaking',
|
role: "startSpeaking",
|
||||||
label: _t('Start Speaking'),
|
label: _t("Start Speaking"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'stopSpeaking',
|
role: "stopSpeaking",
|
||||||
label: _t('Stop Speaking'),
|
label: _t("Stop Speaking"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Window menu.
|
// Window menu.
|
||||||
// This also has specific functionality on macOS
|
// This also has specific functionality on macOS
|
||||||
template[3].submenu = [
|
template[3].submenu = [
|
||||||
{
|
{
|
||||||
label: _t('Close'),
|
label: _t("Close"),
|
||||||
accelerator: 'CmdOrCtrl+W',
|
accelerator: "CmdOrCtrl+W",
|
||||||
role: 'close',
|
role: "close",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _t('Minimize'),
|
label: _t("Minimize"),
|
||||||
accelerator: 'CmdOrCtrl+M',
|
accelerator: "CmdOrCtrl+M",
|
||||||
role: 'minimize',
|
role: "minimize",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _t('Zoom'),
|
label: _t("Zoom"),
|
||||||
role: 'zoom',
|
role: "zoom",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator',
|
type: "separator",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: _t('Bring All to Front'),
|
label: _t("Bring All to Front"),
|
||||||
role: 'front',
|
role: "front",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
template.unshift({
|
template.unshift({
|
||||||
label: _t('File'),
|
label: _t("File"),
|
||||||
accelerator: 'f',
|
accelerator: "f",
|
||||||
submenu: [
|
submenu: [
|
||||||
// For some reason, 'about' does not seem to work on windows.
|
// For some reason, 'about' does not seem to work on windows.
|
||||||
/*{
|
/*{
|
||||||
@@ -235,8 +246,8 @@ export function buildMenuTemplate(): Menu {
|
|||||||
label: _t('About'),
|
label: _t('About'),
|
||||||
},*/
|
},*/
|
||||||
{
|
{
|
||||||
role: 'quit',
|
role: "quit",
|
||||||
label: _t('Quit'),
|
label: _t("Quit"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
+140
-122
@@ -28,22 +28,18 @@ import {
|
|||||||
DownloadItem,
|
DownloadItem,
|
||||||
MenuItemConstructorOptions,
|
MenuItemConstructorOptions,
|
||||||
IpcMainEvent,
|
IpcMainEvent,
|
||||||
} from 'electron';
|
} from "electron";
|
||||||
import url from 'url';
|
import url from "url";
|
||||||
import fs from 'fs';
|
import fs from "fs";
|
||||||
import fetch from 'node-fetch';
|
import fetch from "node-fetch";
|
||||||
import { pipeline } from 'stream';
|
import { pipeline } from "stream";
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
|
|
||||||
import { _t } from './language-helper';
|
import { _t } from "./language-helper";
|
||||||
|
|
||||||
const MAILTO_PREFIX = "mailto:";
|
const MAILTO_PREFIX = "mailto:";
|
||||||
|
|
||||||
const PERMITTED_URL_SCHEMES: string[] = [
|
const PERMITTED_URL_SCHEMES: string[] = ["http:", "https:", MAILTO_PREFIX];
|
||||||
'http:',
|
|
||||||
'https:',
|
|
||||||
MAILTO_PREFIX,
|
|
||||||
];
|
|
||||||
|
|
||||||
function safeOpenURL(target: string): void {
|
function safeOpenURL(target: string): void {
|
||||||
// openExternal passes the target to open/start/xdg-open,
|
// openExternal passes the target to open/start/xdg-open,
|
||||||
@@ -70,7 +66,7 @@ function onWindowOrNavigate(ev: Event, target: string): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function writeNativeImage(filePath: string, img: NativeImage): Promise<void> {
|
function writeNativeImage(filePath: string, img: NativeImage): Promise<void> {
|
||||||
switch (filePath.split('.').pop()?.toLowerCase()) {
|
switch (filePath.split(".").pop()?.toLowerCase()) {
|
||||||
case "jpg":
|
case "jpg":
|
||||||
case "jpeg":
|
case "jpeg":
|
||||||
return fs.promises.writeFile(filePath, img.toJPEG(100));
|
return fs.promises.writeFile(filePath, img.toJPEG(100));
|
||||||
@@ -85,7 +81,7 @@ function writeNativeImage(filePath: string, img: NativeImage): Promise<void> {
|
|||||||
function onLinkContextMenu(ev: Event, params: ContextMenuParams, webContents: WebContents): void {
|
function onLinkContextMenu(ev: Event, params: ContextMenuParams, webContents: WebContents): void {
|
||||||
let url = params.linkURL || params.srcURL;
|
let url = params.linkURL || params.srcURL;
|
||||||
|
|
||||||
if (url.startsWith('vector://vector/webapp')) {
|
if (url.startsWith("vector://vector/webapp")) {
|
||||||
// Avoid showing a context menu for app icons
|
// Avoid showing a context menu for app icons
|
||||||
if (params.hasImageContents) return;
|
if (params.hasImageContents) return;
|
||||||
// Rewrite URL so that it can be used outside of the app
|
// Rewrite URL so that it can be used outside of the app
|
||||||
@@ -94,82 +90,90 @@ function onLinkContextMenu(ev: Event, params: ContextMenuParams, webContents: We
|
|||||||
|
|
||||||
const popupMenu = new Menu();
|
const popupMenu = new Menu();
|
||||||
// No point trying to open blob: URLs in an external browser: it ain't gonna work.
|
// No point trying to open blob: URLs in an external browser: it ain't gonna work.
|
||||||
if (!url.startsWith('blob:')) {
|
if (!url.startsWith("blob:")) {
|
||||||
popupMenu.append(new MenuItem({
|
popupMenu.append(
|
||||||
label: url,
|
new MenuItem({
|
||||||
click() {
|
label: url,
|
||||||
safeOpenURL(url);
|
click(): void {
|
||||||
},
|
safeOpenURL(url);
|
||||||
}));
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.hasImageContents) {
|
if (params.hasImageContents) {
|
||||||
popupMenu.append(new MenuItem({
|
popupMenu.append(
|
||||||
label: _t('Copy image'),
|
new MenuItem({
|
||||||
accelerator: 'c',
|
label: _t("Copy image"),
|
||||||
click() {
|
accelerator: "c",
|
||||||
webContents.copyImageAt(params.x, params.y);
|
click(): void {
|
||||||
},
|
webContents.copyImageAt(params.x, params.y);
|
||||||
}));
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No point offering to copy a blob: URL either
|
// No point offering to copy a blob: URL either
|
||||||
if (!url.startsWith('blob:')) {
|
if (!url.startsWith("blob:")) {
|
||||||
// Special-case e-mail URLs to strip the `mailto:` like modern browsers do
|
// Special-case e-mail URLs to strip the `mailto:` like modern browsers do
|
||||||
if (url.startsWith(MAILTO_PREFIX)) {
|
if (url.startsWith(MAILTO_PREFIX)) {
|
||||||
popupMenu.append(new MenuItem({
|
popupMenu.append(
|
||||||
label: _t('Copy email address'),
|
new MenuItem({
|
||||||
accelerator: 'a',
|
label: _t("Copy email address"),
|
||||||
click() {
|
accelerator: "a",
|
||||||
clipboard.writeText(url.substr(MAILTO_PREFIX.length));
|
click(): void {
|
||||||
},
|
clipboard.writeText(url.substr(MAILTO_PREFIX.length));
|
||||||
}));
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
popupMenu.append(new MenuItem({
|
popupMenu.append(
|
||||||
label: params.hasImageContents
|
new MenuItem({
|
||||||
? _t('Copy image address')
|
label: params.hasImageContents ? _t("Copy image address") : _t("Copy link address"),
|
||||||
: _t('Copy link address'),
|
accelerator: "a",
|
||||||
accelerator: 'a',
|
click(): void {
|
||||||
click() {
|
clipboard.writeText(url);
|
||||||
clipboard.writeText(url);
|
},
|
||||||
},
|
}),
|
||||||
}));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: We cannot easily save a blob from the main process as
|
// XXX: We cannot easily save a blob from the main process as
|
||||||
// only the renderer can resolve them so don't give the user an option to.
|
// only the renderer can resolve them so don't give the user an option to.
|
||||||
if (params.hasImageContents && !url.startsWith('blob:')) {
|
if (params.hasImageContents && !url.startsWith("blob:")) {
|
||||||
popupMenu.append(new MenuItem({
|
popupMenu.append(
|
||||||
label: _t('Save image as...'),
|
new MenuItem({
|
||||||
accelerator: 's',
|
label: _t("Save image as..."),
|
||||||
async click() {
|
accelerator: "s",
|
||||||
const targetFileName = params.suggestedFilename || params.altText || "image.png";
|
async click(): Promise<void> {
|
||||||
const { filePath } = await dialog.showSaveDialog({
|
const targetFileName = params.suggestedFilename || params.altText || "image.png";
|
||||||
defaultPath: targetFileName,
|
const { filePath } = await dialog.showSaveDialog({
|
||||||
});
|
defaultPath: targetFileName,
|
||||||
|
|
||||||
if (!filePath) return; // user cancelled dialog
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (url.startsWith("data:")) {
|
|
||||||
await writeNativeImage(filePath, nativeImage.createFromDataURL(url));
|
|
||||||
} else {
|
|
||||||
const resp = await fetch(url);
|
|
||||||
if (!resp.ok) throw new Error(`unexpected response ${resp.statusText}`);
|
|
||||||
if (!resp.body) throw new Error(`unexpected response has no body ${resp.statusText}`);
|
|
||||||
pipeline(resp.body, fs.createWriteStream(filePath));
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
dialog.showMessageBox({
|
|
||||||
type: "error",
|
|
||||||
title: _t("Failed to save image"),
|
|
||||||
message: _t("The image failed to save"),
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
},
|
if (!filePath) return; // user cancelled dialog
|
||||||
}));
|
|
||||||
|
try {
|
||||||
|
if (url.startsWith("data:")) {
|
||||||
|
await writeNativeImage(filePath, nativeImage.createFromDataURL(url));
|
||||||
|
} else {
|
||||||
|
const resp = await fetch(url);
|
||||||
|
if (!resp.ok) throw new Error(`unexpected response ${resp.statusText}`);
|
||||||
|
if (!resp.body) throw new Error(`unexpected response has no body ${resp.statusText}`);
|
||||||
|
pipeline(resp.body, fs.createWriteStream(filePath));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
dialog.showMessageBox({
|
||||||
|
type: "error",
|
||||||
|
title: _t("Failed to save image"),
|
||||||
|
message: _t("The image failed to save"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// popup() requires an options object even for no options
|
// popup() requires an options object even for no options
|
||||||
@@ -181,7 +185,7 @@ function cutCopyPasteSelectContextMenus(params: ContextMenuParams): MenuItemCons
|
|||||||
const options: MenuItemConstructorOptions[] = [];
|
const options: MenuItemConstructorOptions[] = [];
|
||||||
|
|
||||||
if (params.misspelledWord) {
|
if (params.misspelledWord) {
|
||||||
params.dictionarySuggestions.forEach(word => {
|
params.dictionarySuggestions.forEach((word) => {
|
||||||
options.push({
|
options.push({
|
||||||
label: word,
|
label: word,
|
||||||
click: (menuItem, browserWindow) => {
|
click: (menuItem, browserWindow) => {
|
||||||
@@ -189,46 +193,56 @@ function cutCopyPasteSelectContextMenus(params: ContextMenuParams): MenuItemCons
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
options.push({
|
options.push(
|
||||||
type: 'separator',
|
{
|
||||||
}, {
|
type: "separator",
|
||||||
label: _t('Add to dictionary'),
|
|
||||||
click: (menuItem, browserWindow) => {
|
|
||||||
browserWindow?.webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord);
|
|
||||||
},
|
},
|
||||||
}, {
|
{
|
||||||
type: 'separator',
|
label: _t("Add to dictionary"),
|
||||||
});
|
click: (menuItem, browserWindow) => {
|
||||||
|
browserWindow?.webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "separator",
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
options.push({
|
options.push(
|
||||||
role: 'cut',
|
{
|
||||||
label: _t('Cut'),
|
role: "cut",
|
||||||
accelerator: 't',
|
label: _t("Cut"),
|
||||||
enabled: params.editFlags.canCut,
|
accelerator: "t",
|
||||||
}, {
|
enabled: params.editFlags.canCut,
|
||||||
role: 'copy',
|
},
|
||||||
label: _t('Copy'),
|
{
|
||||||
accelerator: 'c',
|
role: "copy",
|
||||||
enabled: params.editFlags.canCopy,
|
label: _t("Copy"),
|
||||||
}, {
|
accelerator: "c",
|
||||||
role: 'paste',
|
enabled: params.editFlags.canCopy,
|
||||||
label: _t('Paste'),
|
},
|
||||||
accelerator: 'p',
|
{
|
||||||
enabled: params.editFlags.canPaste,
|
role: "paste",
|
||||||
}, {
|
label: _t("Paste"),
|
||||||
role: 'pasteAndMatchStyle',
|
accelerator: "p",
|
||||||
enabled: params.editFlags.canPaste,
|
enabled: params.editFlags.canPaste,
|
||||||
}, {
|
},
|
||||||
role: 'selectAll',
|
{
|
||||||
label: _t("Select All"),
|
role: "pasteAndMatchStyle",
|
||||||
accelerator: 'a',
|
enabled: params.editFlags.canPaste,
|
||||||
enabled: params.editFlags.canSelectAll,
|
},
|
||||||
});
|
{
|
||||||
|
role: "selectAll",
|
||||||
|
label: _t("Select All"),
|
||||||
|
accelerator: "a",
|
||||||
|
enabled: params.editFlags.canSelectAll,
|
||||||
|
},
|
||||||
|
);
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSelectedContextMenu(ev: Event, params: ContextMenuParams) {
|
function onSelectedContextMenu(ev: Event, params: ContextMenuParams): void {
|
||||||
const items = cutCopyPasteSelectContextMenus(params);
|
const items = cutCopyPasteSelectContextMenus(params);
|
||||||
const popupMenu = Menu.buildFromTemplate(items);
|
const popupMenu = Menu.buildFromTemplate(items);
|
||||||
|
|
||||||
@@ -237,11 +251,11 @@ function onSelectedContextMenu(ev: Event, params: ContextMenuParams) {
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEditableContextMenu(ev: Event, params: ContextMenuParams) {
|
function onEditableContextMenu(ev: Event, params: ContextMenuParams): void {
|
||||||
const items: MenuItemConstructorOptions[] = [
|
const items: MenuItemConstructorOptions[] = [
|
||||||
{ role: 'undo' },
|
{ role: "undo" },
|
||||||
{ role: 'redo', enabled: params.editFlags.canRedo },
|
{ role: "redo", enabled: params.editFlags.canRedo },
|
||||||
{ type: 'separator' },
|
{ type: "separator" },
|
||||||
...cutCopyPasteSelectContextMenus(params),
|
...cutCopyPasteSelectContextMenus(params),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -254,7 +268,7 @@ function onEditableContextMenu(ev: Event, params: ContextMenuParams) {
|
|||||||
|
|
||||||
let userDownloadIndex = 0;
|
let userDownloadIndex = 0;
|
||||||
const userDownloadMap = new Map<number, string>(); // Map from id to path
|
const userDownloadMap = new Map<number, string>(); // Map from id to path
|
||||||
ipcMain.on('userDownloadAction', function(ev: IpcMainEvent, { id, open = false }) {
|
ipcMain.on("userDownloadAction", function (ev: IpcMainEvent, { id, open = false }) {
|
||||||
const path = userDownloadMap.get(id);
|
const path = userDownloadMap.get(id);
|
||||||
if (open && path) {
|
if (open && path) {
|
||||||
shell.openPath(path);
|
shell.openPath(path);
|
||||||
@@ -263,13 +277,17 @@ ipcMain.on('userDownloadAction', function(ev: IpcMainEvent, { id, open = false }
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default (webContents: WebContents): void => {
|
export default (webContents: WebContents): void => {
|
||||||
webContents.on('new-window', onWindowOrNavigate);
|
webContents.setWindowOpenHandler((details) => {
|
||||||
webContents.on('will-navigate', (ev: Event, target: string): void => {
|
safeOpenURL(details.url);
|
||||||
|
return { action: "deny" };
|
||||||
|
});
|
||||||
|
|
||||||
|
webContents.on("will-navigate", (ev: Event, target: string): void => {
|
||||||
if (target.startsWith("vector://")) return;
|
if (target.startsWith("vector://")) return;
|
||||||
return onWindowOrNavigate(ev, target);
|
return onWindowOrNavigate(ev, target);
|
||||||
});
|
});
|
||||||
|
|
||||||
webContents.on('context-menu', function(ev: Event, params: ContextMenuParams): void {
|
webContents.on("context-menu", function (ev: Event, params: ContextMenuParams): void {
|
||||||
if (params.linkURL || params.srcURL) {
|
if (params.linkURL || params.srcURL) {
|
||||||
onLinkContextMenu(ev, params, webContents);
|
onLinkContextMenu(ev, params, webContents);
|
||||||
} else if (params.selectionText) {
|
} else if (params.selectionText) {
|
||||||
@@ -279,13 +297,13 @@ export default (webContents: WebContents): void => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
webContents.session.on('will-download', (event: Event, item: DownloadItem): void => {
|
webContents.session.on("will-download", (event: Event, item: DownloadItem): void => {
|
||||||
item.once('done', (event, state) => {
|
item.once("done", (event, state) => {
|
||||||
if (state === 'completed') {
|
if (state === "completed") {
|
||||||
const savePath = item.getSavePath();
|
const savePath = item.getSavePath();
|
||||||
const id = userDownloadIndex++;
|
const id = userDownloadIndex++;
|
||||||
userDownloadMap.set(id, savePath);
|
userDownloadMap.set(id, savePath);
|
||||||
webContents.send('userDownloadCompleted', {
|
webContents.send("userDownloadCompleted", {
|
||||||
id,
|
id,
|
||||||
name: path.basename(savePath),
|
name: path.basename(savePath),
|
||||||
});
|
});
|
||||||
|
|||||||
+2
-2
@@ -45,13 +45,13 @@ describe("App launch", () => {
|
|||||||
args,
|
args,
|
||||||
recordVideo: {
|
recordVideo: {
|
||||||
dir: artifactsPath,
|
dir: artifactsPath,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
window = await app.firstWindow();
|
window = await app.firstWindow();
|
||||||
}, 30000);
|
}, 30000);
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
await app?.close().catch(e => {
|
await app?.close().catch((e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
});
|
});
|
||||||
fs.rmSync(tmpDir, { recursive: true });
|
fs.rmSync(tmpDir, { recursive: true });
|
||||||
|
|||||||
+16
-22
@@ -1,24 +1,18 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"target": "es2016",
|
"target": "es2016",
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"outDir": "./lib",
|
"outDir": "./lib",
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"typeRoots": ["src/@types"],
|
"typeRoots": ["src/@types"],
|
||||||
"lib": [
|
"lib": ["es2019", "dom"],
|
||||||
"es2019",
|
"types": ["jest", "node"],
|
||||||
"dom"
|
"strict": true
|
||||||
],
|
},
|
||||||
"types": ["jest", "node"],
|
"include": ["./src/**/*.ts", "./tests/**/*.ts"]
|
||||||
"strict": true
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"./src/**/*.ts",
|
|
||||||
"./tests/**/*.ts"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user