Compare commits

..

12 Commits

Author SHA1 Message Date
RiotRobot
ba38b73a15 v1.10.15 2022-06-14 14:33:17 +01:00
RiotRobot
4013651ae2 Prepare changelog for v1.10.15 2022-06-14 14:33:16 +01:00
RiotRobot
e0adf93e21 Upgrade matrix-react-sdk to 3.47.0 2022-06-14 14:29:51 +01:00
Michael Telatynski
3a4ac1373c Abstract electron settings properly to avoid boilerplate-hell (#22491)
* Remove unused method `BasePlatform::screenCaptureErrorString`

* Extract SeshatIndexManager into its own file

* Improve platform typescripting

* Consolidate IPC call promisification into IPCManager

* Abstract electron settings properly to avoid boilerplate-hell

* i18n

* Iterate PR

(cherry picked from commit 2c0965c240)
2022-06-14 14:28:14 +01:00
RiotRobot
b7b5408fb1 v1.10.14 2022-06-07 12:18:15 +01:00
RiotRobot
657af4f844 Prepare changelog for v1.10.14 2022-06-07 12:18:14 +01:00
RiotRobot
59d90b3a69 Upgrade matrix-react-sdk to 3.46.0 2022-06-07 12:16:33 +01:00
RiotRobot
e89892c4d5 Upgrade matrix-js-sdk to 18.1.0 2022-06-07 12:15:43 +01:00
RiotRobot
e2d5aebea2 v1.10.14-rc.1 2022-05-31 11:57:51 +01:00
RiotRobot
e24d66410b Prepare changelog for v1.10.14-rc.1 2022-05-31 11:57:50 +01:00
RiotRobot
82a564a1be Upgrade matrix-react-sdk to 3.46.0-rc.1 2022-05-31 11:51:35 +01:00
RiotRobot
fa585521d0 Upgrade matrix-js-sdk to 18.1.0-rc.1 2022-05-31 11:50:00 +01:00
25 changed files with 680 additions and 896 deletions

View File

@@ -1,33 +0,0 @@
name: Build
on:
pull_request: { }
push:
branches: [ master ]
# develop pushes and repository_dispatch handled in build_develop.yaml
env:
# These must be set for fetchdep.sh to get the right branch
REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
jobs:
build:
name: "Build"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
cache: 'yarn'
- name: Install Dependencies
run: "./scripts/layered.sh"
- name: Build & Package
run: "./scripts/ci_package.sh"
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: previewbuild
path: dist/*.tar.gz
retention-days: 28

View File

@@ -1,38 +0,0 @@
# Separate to the main build workflow for access to develop
# environment secrets, largely similar to build.yaml.
name: Build develop
on:
push:
branches: [ develop ]
repository_dispatch:
types: [ element-web-notify ]
jobs:
build:
name: "Build & Upload source maps to Sentry"
runs-on: ubuntu-latest
environment: develop
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
cache: 'yarn'
- name: Install Dependencies
run: "./scripts/layered.sh"
- name: Build, Package & Upload sourcemaps
run: "./scripts/ci_package.sh"
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_URL: ${{ secrets.SENTRY_URL }}
SENTRY_ORG: sentry
SENTRY_PROJECT: riot-web
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: previewbuild
path: dist/*.tar.gz
retention-days: 1

View File

@@ -1,64 +0,0 @@
# Triggers after the Build has finished,
# because artifacts are not externally available
# until the end of their workflow.
name: Deploy develop.element.io
concurrency: deploy_develop
on:
workflow_run:
workflows: [ "Build develop" ]
types:
- completed
jobs:
deploy:
runs-on: ubuntu-latest
environment: develop
if: github.event.workflow_run.conclusion == 'success'
steps:
- name: Find Artifact ID
uses: actions/github-script@v3.1.0
id: find_artifact
with:
result-encoding: string
script: |
const artifacts = await github.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.event.workflow_run.id }},
});
const matchArtifact = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "previewbuild"
})[0];
const download = await github.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
return download.url;
- name: Create Deployment
uses: bobheadxi/deployments@v1
id: deployment
with:
step: start
token: ${{ secrets.GITHUB_TOKEN }}
env: Develop
ref: ${{ github.head_ref }}
- name: Notify the redeploy script
uses: distributhor/workflow-webhook@v2
env:
webhook_url: ${{ secrets.DEVELOP_DEPLOY_WEBHOOK_URL }}
webhook_secret: ${{ secrets.DEVELOP_DEPLOY_WEBHOOK_SECRET }}
data: '{"url": "${{ steps.find_artifact.outputs.result }}"}'
- name: Update deployment status
uses: bobheadxi/deployments@v1
if: always()
with:
step: finish
token: ${{ secrets.GITHUB_TOKEN }}
status: ${{ job.status }}
env: ${{ steps.deployment.outputs.env }}
deployment_id: ${{ steps.deployment.outputs.deployment_id }}
env_url: https://develop.element.io

View File

@@ -1,148 +0,0 @@
# For duplicate issues, ensure the close type is right (not planned), update it if not
# For all closed (completed) issues, cascade the closure onto any referenced rageshakes
# For all closed (not planned) issues, comment on rageshakes to move them into the canonical issue if one exists
on:
issues:
types: [ closed ]
jobs:
tidy:
name: Tidy closed issues
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v5
with:
# PAT needed as the GITHUB_TOKEN won't be able to see cross-references from other orgs (matrix-org)
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
script: |
const variables = {
owner: context.repo.owner,
name: context.repo.repo,
number: context.issue.number,
};
const query = `query($owner:String!, $name:String!, $number:Int!) {
repository(owner: $owner, name: $name) {
issue(number: $number) {
stateReason,
timelineItems(first: 100, itemTypes: [MARKED_AS_DUPLICATE_EVENT, UNMARKED_AS_DUPLICATE_EVENT, CROSS_REFERENCED_EVENT]) {
edges {
node {
__typename
... on MarkedAsDuplicateEvent {
canonical {
... on Issue {
repository {
nameWithOwner
}
number
}
... on PullRequest {
repository {
nameWithOwner
}
number
}
}
}
... on UnmarkedAsDuplicateEvent {
canonical {
... on Issue {
repository {
nameWithOwner
}
number
}
... on PullRequest {
repository {
nameWithOwner
}
number
}
}
}
... on CrossReferencedEvent {
source {
... on Issue {
repository {
nameWithOwner
}
number
}
... on PullRequest {
repository {
nameWithOwner
}
number
}
}
}
}
}
}
}
}
}`;
const result = await github.graphql(query, variables);
const { stateReason, timelineItems: { edges } } = result.repository.issue;
const RAGESHAKE_OWNER = "matrix-org";
const RAGESHAKE_REPO = "element-web-rageshakes";
const rageshakes = new Set();
const duplicateOf = new Set();
console.log("Edges: ", JSON.stringify(edges));
for (const { node } of edges) {
switch(node.__typename) {
case "MarkedAsDuplicateEvent":
duplicateOf.add(node.canonical.repository.nameWithOwner + "#" + node.canonical.number);
break;
case "UnmarkedAsDuplicateEvent":
duplicateOf.remove(node.canonical.repository.nameWithOwner + "#" + node.canonical.number);
break;
case "CrossReferencedEvent":
if (node.source.repository.nameWithOwner === (RAGESHAKE_OWNER + "/" + RAGESHAKE_REPO)) {
rageshakes.add(node.source.number);
}
break;
}
}
console.log("Duplicate of: ", duplicateOf);
console.log("Found rageshakes: ", rageshakes);
if (duplicateOf.size) {
const body = Array.from(duplicateOf).join("\n");
// Comment on all rageshakes to create relationship to the issue this was closed as duplicate of
for (const rageshake of rageshakes) {
github.rest.issues.createComment({
owner: RAGESHAKE_OWNER,
repo: RAGESHAKE_REPO,
issue_number: rageshake,
body,
});
}
// Duplicate was closed with wrong reason, fix it
if (stateReason === "COMPLETED") {
await github.graphql(`mutation($id:ID!) {
closeIssue(input: { issueId:$id, stateReason:NOT_PLANNED }) {
clientMutationId
}
}`, {
id: context.payload.issue.node_id,
});
}
} else {
// This issue was closed, close all related rageshakes
for (const rageshake of rageshakes) {
github.rest.issues.update({
owner: RAGESHAKE_OWNER,
repo: RAGESHAKE_REPO,
issue_number: rageshake,
state: "closed",
});
}
}

View File

@@ -8,8 +8,30 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: true
jobs:
prdetails:
name: PR Details
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request'
uses: matrix-org/matrix-js-sdk/.github/workflows/pr_details.yml@develop
with:
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
branch: ${{ github.event.workflow_run.head_branch }}
sonarqube:
name: 🩻 SonarQube
needs: prdetails
# Only wait for prdetails if it isn't skipped
if: |
always() &&
(needs.prdetails.result == 'success' || needs.prdetails.result == 'skipped') &&
github.event.workflow_run.conclusion == 'success'
uses: matrix-org/matrix-js-sdk/.github/workflows/sonarcloud.yml@develop
with:
repo: ${{ github.event.workflow_run.head_repository.full_name }}
pr_id: ${{ needs.prdetails.outputs.pr_id }}
head_branch: ${{ needs.prdetails.outputs.head_branch || github.event.workflow_run.head_branch }}
base_branch: ${{ needs.prdetails.outputs.base_branch }}
revision: ${{ github.event.workflow_run.head_sha }}
coverage_workflow_name: tests.yml
coverage_run_id: ${{ github.event.workflow_run.id }}
secrets:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

View File

@@ -1,27 +0,0 @@
name: Test
on:
pull_request: { }
push:
branches: [ master, develop ]
repository_dispatch:
types: [ element-web-notify ]
env:
# These must be set for fetchdep.sh to get the right branch
REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
jobs:
test:
name: "Test"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
cache: 'yarn'
- name: Install Dependencies
run: "./scripts/layered.sh"
- name: Run Tests
run: "yarn test"

View File

@@ -1,18 +0,0 @@
name: Move issued assigned to specific team members to their boards
on:
issues:
types: [ assigned ]
jobs:
web-app-team:
runs-on: ubuntu-latest
if: |
contains(github.event.issue.assignees.*.login, 't3chguy') ||
contains(github.event.issue.assignees.*.login, 'turt2live')
steps:
- uses: alex-page/github-project-automation-plus@bb266ff4dde9242060e2d5418e120a133586d488
with:
project: Web App Team
column: Ready
repo-token: ${{ secrets.ELEMENT_BOT_TOKEN }}

View File

@@ -101,6 +101,7 @@ jobs:
if: >
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience') ||
contains(github.event.issue.labels.*.name, 'Team: Delight') ||
contains(github.event.issue.labels.*.name, 'Z-IA') ||
contains(github.event.issue.labels.*.name, 'Z-NewUserJourney')
steps:
- uses: octokit/graphql-action@v2.x

2
.gitignore vendored
View File

@@ -24,5 +24,3 @@ electron/pub
.vscode/
.env
/coverage
/scripts/extracted/
/scripts/latest

View File

@@ -1,3 +1,66 @@
Changes in [1.10.15](https://github.com/vector-im/element-web/releases/tag/v1.10.15) (2022-06-14)
=================================================================================================
## 🐛 Bug Fixes
* Fix missing element desktop preferences ([\#8798](https://github.com/matrix-org/matrix-react-sdk/pull/8798)). Contributed by @t3chguy.
Changes in [1.10.14](https://github.com/vector-im/element-web/releases/tag/v1.10.14) (2022-06-07)
=================================================================================================
## ✨ Features
* Make Lao translation available ([\#22358](https://github.com/vector-im/element-web/pull/22358)). Fixes #22327.
* Option to disable hardware acceleration on Element Desktop ([\#22295](https://github.com/vector-im/element-web/pull/22295)). Contributed by @novocaine.
* Configure custom home.html via `.well-known/matrix/client["io.element.embedded_pages"]["home_url"]` for all your element-web/desktop users ([\#7790](https://github.com/matrix-org/matrix-react-sdk/pull/7790)). Contributed by @johannes-krude.
* Live location sharing - open location in OpenStreetMap ([\#8695](https://github.com/matrix-org/matrix-react-sdk/pull/8695)). Contributed by @kerryarchibald.
* Show a dialog when Jitsi encounters an error ([\#8701](https://github.com/matrix-org/matrix-react-sdk/pull/8701)). Fixes #22284.
* Add support for setting the `avatar_url` of widgets by integration managers. ([\#8550](https://github.com/matrix-org/matrix-react-sdk/pull/8550)). Contributed by @Fox32.
* Add an option to ignore (block) a user when reporting their events ([\#8471](https://github.com/matrix-org/matrix-react-sdk/pull/8471)).
* Add the option to disable hardware acceleration ([\#8655](https://github.com/matrix-org/matrix-react-sdk/pull/8655)). Contributed by @novocaine.
* Slightly better presentation of read receipts to screen reader users ([\#8662](https://github.com/matrix-org/matrix-react-sdk/pull/8662)). Fixes #22293. Contributed by @pvagner.
* Add jump to related event context menu item ([\#6775](https://github.com/matrix-org/matrix-react-sdk/pull/6775)). Fixes #19883.
* Add public room directory hook ([\#8626](https://github.com/matrix-org/matrix-react-sdk/pull/8626)).
## 🐛 Bug Fixes
* Stop Jitsi if we time out while connecting to a video room ([\#22301](https://github.com/vector-im/element-web/pull/22301)). Fixes #22283.
* Remove inline margin from UTD error message inside a reply tile on ThreadView ([\#8708](https://github.com/matrix-org/matrix-react-sdk/pull/8708)). Fixes #22376. Contributed by @luixxiul.
* Move unread notification dots of the threads list to the expected position ([\#8700](https://github.com/matrix-org/matrix-react-sdk/pull/8700)). Fixes #22350. Contributed by @luixxiul.
* Prevent overflow of grid items on a bubble with UTD generally ([\#8697](https://github.com/matrix-org/matrix-react-sdk/pull/8697)). Contributed by @luixxiul.
* Create 'Unable To Decrypt' grid layout for hidden events on a bubble layout ([\#8704](https://github.com/matrix-org/matrix-react-sdk/pull/8704)). Fixes #22365. Contributed by @luixxiul.
* Fix - AccessibleButton does not set disabled attribute ([\#8682](https://github.com/matrix-org/matrix-react-sdk/pull/8682)). Contributed by @kerryarchibald.
* Fix font not resetting when logging out ([\#8670](https://github.com/matrix-org/matrix-react-sdk/pull/8670)). Fixes #17228.
* Fix local aliases section of room settings not working for some homeservers (ie ([\#8698](https://github.com/matrix-org/matrix-react-sdk/pull/8698)). Fixes #22337.
* Align EventTile_line with display name on message bubble ([\#8692](https://github.com/matrix-org/matrix-react-sdk/pull/8692)). Fixes #22343. Contributed by @luixxiul.
* Convert references to direct chat -> direct message ([\#8694](https://github.com/matrix-org/matrix-react-sdk/pull/8694)). Contributed by @novocaine.
* Improve combining diacritics for U+20D0 to U+20F0 in Chrome ([\#8687](https://github.com/matrix-org/matrix-react-sdk/pull/8687)).
* Make the empty thread panel fill BaseCard ([\#8690](https://github.com/matrix-org/matrix-react-sdk/pull/8690)). Fixes #22338. Contributed by @luixxiul.
* Fix edge case around composer handling gendered facepalm emoji ([\#8686](https://github.com/matrix-org/matrix-react-sdk/pull/8686)).
* Fix a grid blowout due to nowrap displayName on a bubble with UTD ([\#8688](https://github.com/matrix-org/matrix-react-sdk/pull/8688)). Fixes #21914. Contributed by @luixxiul.
* Apply the same max-width to image tile on the thread timeline as message bubble ([\#8669](https://github.com/matrix-org/matrix-react-sdk/pull/8669)). Fixes #22313. Contributed by @luixxiul.
* Fix dropdown button size for picture-in-picture CallView ([\#8680](https://github.com/matrix-org/matrix-react-sdk/pull/8680)). Fixes #22316. Contributed by @luixxiul.
* Live location sharing - fix square border for image-less avatar (PSF-1052) ([\#8679](https://github.com/matrix-org/matrix-react-sdk/pull/8679)). Contributed by @kerryarchibald.
* Stop connecting to a video room if the widget messaging disappears ([\#8660](https://github.com/matrix-org/matrix-react-sdk/pull/8660)).
* Fix file button and audio player overflowing from message bubble ([\#8666](https://github.com/matrix-org/matrix-react-sdk/pull/8666)). Fixes #22308. Contributed by @luixxiul.
* Don't show broken composer format bar when selection is whitespace ([\#8673](https://github.com/matrix-org/matrix-react-sdk/pull/8673)). Fixes #10788.
* Fix media upload http 413 handling ([\#8674](https://github.com/matrix-org/matrix-react-sdk/pull/8674)).
* Fix emoji picker for editing thread responses ([\#8671](https://github.com/matrix-org/matrix-react-sdk/pull/8671)). Fixes matrix-org/element-web-rageshakes#13129.
* Map attribution while sharing live location is now visible ([\#8621](https://github.com/matrix-org/matrix-react-sdk/pull/8621)). Fixes #22236. Contributed by @weeman1337.
* Fix info tile overlapping the time stamp on TimelineCard ([\#8639](https://github.com/matrix-org/matrix-react-sdk/pull/8639)). Fixes #22256. Contributed by @luixxiul.
* Fix position of wide images on IRC / modern layout ([\#8667](https://github.com/matrix-org/matrix-react-sdk/pull/8667)). Fixes #22309. Contributed by @luixxiul.
* Fix other user's displayName being wrapped on the bubble message layout ([\#8456](https://github.com/matrix-org/matrix-react-sdk/pull/8456)). Fixes #22004. Contributed by @luixxiul.
* Set spacing declarations to elements in mx_EventTile_mediaLine ([\#8665](https://github.com/matrix-org/matrix-react-sdk/pull/8665)). Fixes #22307. Contributed by @luixxiul.
* Fix wide image overflowing from the thumbnail container ([\#8663](https://github.com/matrix-org/matrix-react-sdk/pull/8663)). Fixes #22303. Contributed by @luixxiul.
* Fix styles of "Show all" link button on ReactionsRow ([\#8658](https://github.com/matrix-org/matrix-react-sdk/pull/8658)). Fixes #22300. Contributed by @luixxiul.
* Automatically log in after registration ([\#8654](https://github.com/matrix-org/matrix-react-sdk/pull/8654)). Fixes #19305. Contributed by @justjanne.
* Fix offline status in window title not working reliably ([\#8656](https://github.com/matrix-org/matrix-react-sdk/pull/8656)).
* Align input area with event body's first letter in a thread on IRC/modern layout ([\#8636](https://github.com/matrix-org/matrix-react-sdk/pull/8636)). Fixes #22252. Contributed by @luixxiul.
* Fix crash on null idp for SSO buttons ([\#8650](https://github.com/matrix-org/matrix-react-sdk/pull/8650)). Contributed by @hughns.
* Don't open the regular browser or our context menu on right-clicking the `Options` button in the message action bar ([\#8648](https://github.com/matrix-org/matrix-react-sdk/pull/8648)). Fixes #22279.
* Show notifications even when Element is focused ([\#8590](https://github.com/matrix-org/matrix-react-sdk/pull/8590)). Contributed by @sumnerevans.
* Remove padding from the buttons on edit message composer of a event tile on a thread ([\#8632](https://github.com/matrix-org/matrix-react-sdk/pull/8632)). Contributed by @luixxiul.
* ensure metaspace changes correctly notify listeners ([\#8611](https://github.com/matrix-org/matrix-react-sdk/pull/8611)). Fixes #21006. Contributed by @justjanne.
* Hide image banner on stickers, they have a tooltip already ([\#8641](https://github.com/matrix-org/matrix-react-sdk/pull/8641)). Fixes #22244.
* Adjust EditMessageComposer style declarations ([\#8631](https://github.com/matrix-org/matrix-react-sdk/pull/8631)). Fixes #22231. Contributed by @luixxiul.
Changes in [1.10.13](https://github.com/vector-im/element-web/releases/tag/v1.10.13) (2022-05-24)
=================================================================================================

View File

@@ -193,7 +193,7 @@ Starting with `branding`, the following subproperties are available:
`welcome.html` that ships with Element will be used instead.
2. `home_url`: A URL to an HTML page to show within the app as the "home" page. When the app doesn't have a room/screen to
show the user, it will use the home page instead. The home page is additionally accessible from the user menu. By default,
no home page is set and therefore a hardcoded landing screen is used. More documentation and examples are [here](./custom-home.md).
no home page is set and therefore a hardcoded landing screen is used.
3. `login_for_welcome`: When `true` (default `false`), the app will use the login form as a welcome page instead of the welcome
page itself. This disables use of `welcome_url` and all welcome page functionality.

View File

@@ -1,65 +0,0 @@
# Custom Home Page
The home page is shown whenever the user is logged in, but no room is selected.
A custom `home.html` replacing the default home page can be configured either in `.well-known/matrix/client` or `config.json`.
Such a custom home page can be used to communicate helpful information and important rules to the users.
## Configuration
To provide a custom home page for all element-web/desktop users of a homeserver, include the following in `.well-known/matrix/client`:
```
{
"io.element.embedded_pages": {
"home_url": "https://example.org/home.html"
}
}
```
The home page can be overridden in `config.json` to provide all users of an element-web installation with the same experience:
```
{
"embeddedPages": {
"homeUrl": "https://example.org/home.html"
}
}
```
## `home.html` Example
The following is a simple example for a custom `home.html`:
```
<style type="text/css">
.tos {
width: auto;
color: black;
background : #ffcccb;
font-weight: bold;
}
</style>
<h1>The example.org Matrix Server</h1>
<div class="tos">
<p>Behave appropriately.</p>
</div>
<h2>Start Chatting</h2>
<ul>
<li><a href="#/dm">Send a Direct Message</a></li>
<li><a href="#/directory">Explore Public Rooms</a></li>
<li><a href="#/new">Create a Group Chat</a></li>
</ul>
```
When choosing colors, be aware that the home page may be displayed in either light or dark mode.
It may be needed to set CORS headers for the `home.html` to enable element-desktop to fetch it, with e.g., the following nginx config:
```
add_header Access-Control-Allow-Origin *;
```

View File

@@ -1,6 +1,6 @@
{
"name": "element-web",
"version": "1.10.13",
"version": "1.10.15",
"description": "A feature-rich client for Matrix.org",
"author": "New Vector Ltd.",
"repository": {
@@ -58,8 +58,8 @@
"gfm.css": "^1.1.2",
"jsrsasign": "^10.2.0",
"katex": "^0.12.0",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#develop",
"matrix-js-sdk": "18.1.0",
"matrix-react-sdk": "3.47.0",
"matrix-widget-api": "^0.1.0-beta.18",
"prop-types": "^15.7.2",
"react": "17.0.2",

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# download and unpack a element-web tarball.
#
# Allows `bundles` to be extracted to a common directory, and a link to
# config.json to be added.
@@ -23,11 +23,9 @@ except ImportError:
# python2
from urllib import urlretrieve
class DeployException(Exception):
pass
def create_relative_symlink(linkname, target):
relpath = os.path.relpath(target, os.path.dirname(linkname))
print ("Symlink %s -> %s" % (linkname, relpath))
@@ -59,11 +57,10 @@ def move_bundles(source, dest):
else:
renames[os.path.join(source, f)] = dst
for (src, dst) in renames.items():
for (src, dst) in renames.iteritems():
print ("Move %s -> %s" % (src, dst))
os.rename(src, dst)
class Deployer:
def __init__(self):
self.packages_path = "."
@@ -103,7 +100,7 @@ class Deployer:
print ("Extracted into: %s" % extracted_dir)
if self.symlink_paths:
for link_path, file_path in self.symlink_paths.items():
for link_path, file_path in self.symlink_paths.iteritems():
create_relative_symlink(
target=file_path,
linkname=os.path.join(extracted_dir, link_path)
@@ -142,7 +139,6 @@ class Deployer:
print ("Done")
return local_filename
if __name__ == "__main__":
parser = argparse.ArgumentParser("Deploy a Riot build on a web server.")
parser.add_argument(

99
scripts/fetch-develop.deps.sh Executable file
View File

@@ -0,0 +1,99 @@
#!/bin/bash
# Fetches the js-sdk and matrix-react-sdk dependencies for development
# or testing purposes
# If there exists a branch of that dependency with the same name as
# the branch the current checkout is on, use that branch. Otherwise,
# use develop.
set -ex
GIT_CLONE_ARGS=("$@")
[ -z "$defbranch" ] && defbranch="develop"
# clone a specific branch of a github repo
function clone() {
org=$1
repo=$2
branch=$3
# Chop 'origin' off the start as jenkins ends up using
# branches on the origin, but this doesn't work if we
# specify the branch when cloning.
branch=${branch#origin/}
if [ -n "$branch" ]
then
echo "Trying to use $org/$repo#$branch"
# Disable auth prompts: https://serverfault.com/a/665959
GIT_TERMINAL_PROMPT=0 git clone https://github.com/$org/$repo.git $repo --branch $branch \
"${GIT_CLONE_ARGS[@]}"
return $?
fi
return 1
}
function dodep() {
deforg=$1
defrepo=$2
rm -rf $defrepo
# Try the PR author's branch in case it exists on the deps as well.
# Try the target branch of the push or PR.
# Use the default branch as the last resort.
if [[ "$BUILDKITE" == true ]]; then
# If BUILDKITE_BRANCH is set, it will contain either:
# * "branch" when the author's branch and target branch are in the same repo
# * "author:branch" when the author's branch is in their fork
# We can split on `:` into an array to check.
BUILDKITE_BRANCH_ARRAY=(${BUILDKITE_BRANCH//:/ })
if [[ "${#BUILDKITE_BRANCH_ARRAY[@]}" == "2" ]]; then
prAuthor=${BUILDKITE_BRANCH_ARRAY[0]}
prBranch=${BUILDKITE_BRANCH_ARRAY[1]}
else
prAuthor=$deforg
prBranch=$BUILDKITE_BRANCH
fi
clone $prAuthor $defrepo $prBranch ||
clone $deforg $defrepo $BUILDKITE_PULL_REQUEST_BASE_BRANCH ||
clone $deforg $defrepo $defbranch ||
return $?
else
clone $deforg $defrepo $ghprbSourceBranch ||
clone $deforg $defrepo $GIT_BRANCH ||
clone $deforg $defrepo `git rev-parse --abbrev-ref HEAD` ||
clone $deforg $defrepo $defbranch ||
return $?
fi
echo "$defrepo set to branch "`git -C "$defrepo" rev-parse --abbrev-ref HEAD`
}
##############################
echo 'Setting up matrix-js-sdk'
dodep matrix-org matrix-js-sdk
pushd matrix-js-sdk
yarn link
yarn install --pure-lockfile
popd
yarn link matrix-js-sdk
##############################
echo 'Setting up matrix-react-sdk'
dodep matrix-org matrix-react-sdk
pushd matrix-react-sdk
yarn link
yarn link matrix-js-sdk
yarn install --pure-lockfile
popd
yarn link matrix-react-sdk
##############################

View File

@@ -1,78 +0,0 @@
#!/bin/bash
set -x
deforg="$1"
defrepo="$2"
defbranch="$3"
[ -z "$defbranch" ] && defbranch="develop"
rm -r "$defrepo" || true
# A function that clones a branch of a repo based on the org, repo and branch
clone() {
org=$1
repo=$2
branch=$3
if [ -n "$branch" ]
then
echo "Trying to use $org/$repo#$branch"
# Disable auth prompts: https://serverfault.com/a/665959
GIT_TERMINAL_PROMPT=0 git clone https://github.com/$org/$repo.git $repo --branch "$branch" --depth 1 && exit 0
fi
}
# A function that gets info about a PR from the GitHub API based on its number
getPRInfo() {
number=$1
if [ -n "$number" ]; then
echo "Getting info about a PR with number $number"
apiEndpoint="https://api.github.com/repos/${REPOSITORY:-"vector-im/element-web"}/pulls/"
apiEndpoint+=$number
head=$(curl $apiEndpoint | jq -r '.head.label')
fi
}
# Some CIs don't give us enough info, so we just get the PR number and ask the
# GH API for more info - "fork:branch". Some give us this directly.
if [ -n "$BUILDKITE_BRANCH" ]; then
# BuildKite
head=$BUILDKITE_BRANCH
elif [ -n "$PR_NUMBER" ]; then
# GitHub
getPRInfo $PR_NUMBER
elif [ -n "$REVIEW_ID" ]; then
# Netlify
getPRInfo $REVIEW_ID
fi
# for forks, $head will be in the format "fork:branch", so we split it by ":"
# into an array. On non-forks, this has the effect of splitting into a single
# element array given ":" shouldn't appear in the head - it'll just be the
# branch name. Based on the results, we clone.
BRANCH_ARRAY=(${head//:/ })
TRY_ORG=$deforg
TRY_BRANCH=${BRANCH_ARRAY[0]}
if [[ "$head" == *":"* ]]; then
# ... but only match that fork if it's a real fork
if [ "${BRANCH_ARRAY[0]}" != "matrix-org" ]; then
TRY_ORG=${BRANCH_ARRAY[0]}
fi
TRY_BRANCH=${BRANCH_ARRAY[1]}
fi
clone ${TRY_ORG} $defrepo ${TRY_BRANCH}
# Try the target branch of the push or PR.
if [ -n "$GITHUB_BASE_REF" ]; then
clone $deforg $defrepo $GITHUB_BASE_REF
elif [ -n "$BUILDKITE_PULL_REQUEST_BASE_BRANCH" ]; then
clone $deforg $defrepo $BUILDKITE_PULL_REQUEST_BASE_BRANCH
fi
# Try HEAD which is the branch name in Netlify (not BRANCH which is pull/xxxx/head for PR builds)
clone $deforg $defrepo $HEAD
# Use the default branch as the last resort.
clone $deforg $defrepo $defbranch

View File

@@ -1,40 +1,45 @@
#!/usr/bin/env python
#
# auto-deploy script for https://develop.element.io
# Listens for Github Action webhook pokes (https://github.com/marketplace/actions/workflow-webhook-action)
# When it gets one: downloads the artifact from github actions and deploys it as the new version.
#
# Listens for buildkite webhook pokes (https://buildkite.com/docs/apis/webhooks)
# When it gets one, downloads the artifact from buildkite
# and deploys it as the new version.
#
# Requires the following python packages:
#
# - requests
# - flask
# - python-github-webhook
#
from __future__ import print_function
import argparse
import os
import errno
import json, requests, tarfile, argparse, os, errno
import time
import traceback
from urlparse import urljoin
import glob
from io import BytesIO
from urllib.request import urlopen
from zipfile import ZipFile
import re
import shutil
import threading
from Queue import Queue
from github_webhook import Webhook
from flask import Flask, abort
from flask import Flask, jsonify, request, abort
from deploy import Deployer, DeployException
app = Flask(__name__)
webhook = Webhook(app, endpoint="/")
deployer = None
arg_extract_path = None
arg_symlink = None
arg_webhook_token = None
arg_api_token = None
def create_symlink(source: str, linkname: str):
workQueue = Queue()
def create_symlink(source, linkname):
try:
os.symlink(source, linkname)
except OSError as e:
except OSError, e:
if e.errno == errno.EEXIST:
# atomic modification
os.symlink(source, linkname + ".tmp")
@@ -42,43 +47,118 @@ def create_symlink(source: str, linkname: str):
else:
raise e
def req_headers():
return {
"Authorization": "Bearer %s" % (arg_api_token,),
}
@webhook.hook(event_type="workflow_run")
def on_deployment(payload: dict):
repository = payload.get("repository")
if repository is None:
abort(400, "No 'repository' specified")
# Buildkite considers a poke to have failed if it has to wait more than 10s for
# data (any data, not just the initial response) and it normally takes longer than
# that to download an artifact from buildkite. Apparently there is no way in flask
# to finish the response and then keep doing stuff, so instead this has to involve
# threading. Sigh.
def worker_thread():
while True:
toDeploy = workQueue.get()
deploy_buildkite_artifact(*toDeploy)
@app.route("/", methods=["POST"])
def on_receive_buildkite_poke():
got_webhook_token = request.headers.get('X-Buildkite-Token')
if got_webhook_token != arg_webbook_token:
print("Denying request with incorrect webhook token: %s" % (got_webhook_token,))
abort(400, "Incorrect webhook token")
return
workflow = payload.get("workflow")
if repository is None:
abort(400, "No 'workflow' specified")
required_api_prefix = None
if arg_buildkite_org is not None:
required_api_prefix = 'https://api.buildkite.com/v2/organizations/%s' % (arg_buildkite_org,)
incoming_json = request.get_json()
if not incoming_json:
abort(400, "No JSON provided!")
return
print("Incoming JSON: %s" % (incoming_json,))
event = incoming_json.get("event")
if event is None:
abort(400, "No 'event' specified")
return
request_id = payload.get("requestID")
if request_id is None:
abort(400, "No 'request_id' specified")
if event == 'ping':
print("Got ping request - responding")
return jsonify({'response': 'pong!'})
if event != 'build.finished':
print("Rejecting '%s' event")
abort(400, "Unrecognised event")
return
if arg_github_org is not None and not repository.startswith(arg_github_org):
print("Denying poke for repository with incorrect prefix: %s" % (repository,))
abort(400, "Invalid repository")
build_obj = incoming_json.get("build")
if build_obj is None:
abort(400, "No 'build' object")
return
if arg_github_workflow is not None and workflow != arg_github_workflow:
print("Denying poke for incorrect workflow: %s" % (workflow,))
abort(400, "Incorrect workflow")
build_url = build_obj.get('url')
if build_url is None:
abort(400, "build has no url")
return
artifact_url = payload.get("data", {}).get("url")
if artifact_url is None:
abort(400, "No 'data.url' specified")
if required_api_prefix is not None and not build_url.startswith(required_api_prefix):
print("Denying poke for build url with incorrect prefix: %s" % (build_url,))
abort(400, "Invalid build url")
return
deploy_artifact(artifact_url, request_id)
build_num = build_obj.get('number')
if build_num is None:
abort(400, "build has no number")
return
pipeline_obj = incoming_json.get("pipeline")
if pipeline_obj is None:
abort(400, "No 'pipeline' object")
return
pipeline_name = pipeline_obj.get('name')
if pipeline_name is None:
abort(400, "pipeline has no name")
return
artifacts_url = build_url + "/artifacts"
artifacts_resp = requests.get(artifacts_url, headers=req_headers())
artifacts_resp.raise_for_status()
artifacts_array = artifacts_resp.json()
artifact_to_deploy = None
for artifact in artifacts_array:
if re.match(r"dist/.*.tar.gz", artifact['path']):
artifact_to_deploy = artifact
if artifact_to_deploy is None:
print("No suitable artifacts found")
return jsonify({})
# double paranoia check: make sure the artifact is on the right org too
if required_api_prefix is not None and not artifact_to_deploy['url'].startswith(required_api_prefix):
print("Denying poke for build url with incorrect prefix: %s" % (artifact_to_deploy['url'],))
abort(400, "Refusing to deploy artifact from URL %s", artifact_to_deploy['url'])
return
# there's no point building up a queue of things to deploy, so if there are any pending jobs,
# remove them
while not workQueue.empty():
try:
workQueue.get(False)
except:
pass
workQueue.put([artifact_to_deploy, pipeline_name, build_num])
return jsonify({})
def deploy_buildkite_artifact(artifact, pipeline_name, build_num):
artifact_response = requests.get(artifact['url'], headers=req_headers())
artifact_response.raise_for_status()
artifact_obj = artifact_response.json()
def deploy_artifact(artifact_url: str, request_id: str):
# we extract into a directory based on the build number. This avoids the
# problem of multiple builds building the same git version and thus having
# the same tarball name. That would lead to two potential problems:
@@ -86,42 +166,58 @@ def deploy_artifact(artifact_url: str, request_id: str):
# a good deploy with a bad one
# (b) we'll be overwriting the live deployment, which means people might
# see half-written files.
build_dir = os.path.join(arg_extract_path, "gha-%s" % (request_id,))
if os.path.exists(build_dir):
# We have already deployed this, nop
return
os.mkdir(build_dir)
build_dir = os.path.join(arg_extract_path, "%s-#%s" % (pipeline_name, build_num))
try:
with urlopen(artifact_url) as f:
with ZipFile(BytesIO(f.read()), "r") as z:
name = next((x for x in z.namelist() if x.endswith(".tar.gz")))
z.extract(name, build_dir)
extracted_dir = deployer.deploy(os.path.join(build_dir, name), build_dir)
create_symlink(source=extracted_dir, linkname=arg_symlink)
extracted_dir = deploy_tarball(artifact_obj, build_dir)
except DeployException as e:
traceback.print_exc()
abort(400, str(e))
finally:
if deployer.should_clean:
os.remove(os.path.join(build_dir, name))
abort(400, e.message)
create_symlink(source=extracted_dir, linkname=arg_symlink)
def deploy_tarball(artifact, build_dir):
"""Download a tarball from jenkins and unpack it
Returns:
(str) the path to the unpacked deployment
"""
if os.path.exists(build_dir):
raise DeployException(
"Not deploying. We have previously deployed this build."
)
os.mkdir(build_dir)
print("Fetching artifact %s -> %s..." % (artifact['download_url'], artifact['filename']))
# Download the tarball here as buildkite needs auth to do this
# we don't pgp-sign buildkite artifacts, relying on HTTPS and buildkite
# not being evil. If that's not good enough for you, don't use develop.element.io.
resp = requests.get(artifact['download_url'], stream=True, headers=req_headers())
resp.raise_for_status()
with open(artifact['filename'], 'wb') as ofp:
shutil.copyfileobj(resp.raw, ofp)
print("...download complete. Deploying...")
# we rely on the fact that flask only serves one request at a time to
# ensure that we do not overwrite a tarball from a concurrent request.
return deployer.deploy(artifact['filename'], build_dir)
if __name__ == "__main__":
parser = argparse.ArgumentParser("Runs an Element redeployment server.")
parser = argparse.ArgumentParser("Runs a Vector redeployment server.")
parser.add_argument(
"-p", "--port", dest="port", default=4000, type=int, help=(
"The port to listen on for redeployment requests."
"The port to listen on for requests from Jenkins."
)
)
parser.add_argument(
"-e", "--extract", dest="extract", default="./extracted", type=str, help=(
"-e", "--extract", dest="extract", default="./extracted", help=(
"The location to extract .tar.gz files to."
)
)
parser.add_argument(
"-b", "--bundles-dir", dest="bundles_dir", type=str, help=(
"-b", "--bundles-dir", dest="bundles_dir", help=(
"A directory to move the contents of the 'bundles' directory to. A \
symlink to the bundles directory will also be written inside the \
extracted tarball. Example: './bundles'."
@@ -133,7 +229,7 @@ if __name__ == "__main__":
)
)
parser.add_argument(
"-s", "--symlink", dest="symlink", default="./latest", type=str, help=(
"-s", "--symlink", dest="symlink", default="./latest", help=(
"Write a symlink to this location pointing to the extracted tarball. \
New builds will keep overwriting this symlink. The symlink will point \
to the /vector directory INSIDE the tarball."
@@ -142,65 +238,71 @@ if __name__ == "__main__":
# --include ../../config.json ./localhost.json homepages/*
parser.add_argument(
"--include", nargs='*', default='./config*.json', type=str, help=(
"--include", nargs='*', default='./config*.json', help=(
"Symlink these files into the root of the deployed tarball. \
Useful for config files and home pages. Supports glob syntax. \
(Default: '%(default)s')"
)
)
parser.add_argument(
"--test", dest="tarball_uri", type=str, help=(
"Don't start an HTTP listener. Instead download a build from this URL immediately."
"--test", dest="tarball_uri", help=(
"Don't start an HTTP listener. Instead download a build from Jenkins \
immediately."
),
)
parser.add_argument(
"--webhook-token", dest="webhook_token", type=str, help=(
"Only accept pokes signed with this github token."
"--webhook-token", dest="webhook_token", help=(
"Only accept pokes with this buildkite token."
), required=True,
)
parser.add_argument(
"--api-token", dest="api_token", help=(
"API access token for buildkite. Require read_artifacts scope."
), required=True,
)
# We require a matching webhook token, but because we take everything else
# about what to deploy from the poke body, we can be a little more paranoid
# and only accept builds / artifacts from a specific github org
# and only accept builds / artifacts from a specific buildkite org
parser.add_argument(
"--org", dest="github_org", type=str, help=(
"Lock down to this github org"
)
)
# Optional matching workflow name
parser.add_argument(
"--workflow", dest="github_workflow", type=str, help=(
"Lock down to this github workflow"
"--org", dest="buildkite_org", help=(
"Lock down to this buildkite org"
)
)
args = parser.parse_args()
arg_extract_path = args.extract
arg_symlink = args.symlink
arg_github_org = args.github_org
arg_github_workflow = args.github_workflow
arg_webbook_token = args.webhook_token
arg_api_token = args.api_token
arg_buildkite_org = args.buildkite_org
if not os.path.isdir(arg_extract_path):
os.mkdir(arg_extract_path)
webhook.secret = args.webhook_token
deployer = Deployer()
deployer.bundles_path = args.bundles_dir
deployer.should_clean = args.clean
for include in args.include.split(" "):
for include in args.include:
deployer.symlink_paths.update({ os.path.basename(pth): pth for pth in glob.iglob(include) })
print(
"Listening on port %s. Extracting to %s%s. Symlinking to %s. Include files: %s" %
(args.port,
arg_extract_path,
" (clean after)" if deployer.should_clean else "",
arg_symlink,
deployer.symlink_paths,
if args.tarball_uri is not None:
build_dir = os.path.join(arg_extract_path, "test-%i" % (time.time()))
deploy_tarball(args.tarball_uri, build_dir)
else:
print(
"Listening on port %s. Extracting to %s%s. Symlinking to %s. Include files: %s" %
(args.port,
arg_extract_path,
" (clean after)" if deployer.should_clean else "",
arg_symlink,
deployer.symlink_paths,
)
)
)
app.run(port=args.port, debug=False)
fred = threading.Thread(target=worker_thread)
fred.daemon = True
fred.start()
app.run(port=args.port, debug=False)

View File

@@ -14,7 +14,6 @@
"Go to your browser to complete Sign In": "Go to your browser to complete Sign In",
"Unknown device": "Unknown device",
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
"You need to be using HTTPS to place a screen-sharing call.": "You need to be using HTTPS to place a screen-sharing call.",
"Powered by Matrix": "Powered by Matrix",
"Use %(brand)s on mobile": "Use %(brand)s on mobile",
"Unsupported browser": "Unsupported browser",

View File

@@ -18,13 +18,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { UpdateCheckStatus } from "matrix-react-sdk/src/BasePlatform";
import BaseEventIndexManager, {
ICrawlerCheckpoint,
IEventAndProfile,
IIndexStats,
ISearchArgs,
} from 'matrix-react-sdk/src/indexing/BaseEventIndexManager';
import { UpdateCheckStatus, UpdateStatus } from "matrix-react-sdk/src/BasePlatform";
import BaseEventIndexManager from 'matrix-react-sdk/src/indexing/BaseEventIndexManager';
import dis from 'matrix-react-sdk/src/dispatcher/dispatcher';
import { _t } from 'matrix-react-sdk/src/languageHandler';
import SdkConfig from 'matrix-react-sdk/src/SdkConfig';
@@ -43,11 +38,12 @@ import { showToast as showUpdateToast } from "matrix-react-sdk/src/toasts/Update
import { CheckUpdatesPayload } from "matrix-react-sdk/src/dispatcher/payloads/CheckUpdatesPayload";
import ToastStore from "matrix-react-sdk/src/stores/ToastStore";
import GenericExpiringToast from "matrix-react-sdk/src/components/views/toasts/GenericExpiringToast";
import { IMatrixProfile, IEventWithRoomId as IMatrixEvent, IResultRoomEvents } from "matrix-js-sdk/src/@types/search";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import VectorBasePlatform from './VectorBasePlatform';
import { SeshatIndexManager } from "./SeshatIndexManager";
import { IPCManager } from "./IPCManager";
const electron = window.electron;
const isMac = navigator.platform.toUpperCase().includes('MAC');
@@ -71,14 +67,14 @@ function platformFriendlyName(): string {
}
}
function _onAction(payload: ActionPayload) {
function onAction(payload: ActionPayload): void {
// Whitelist payload actions, no point sending most across
if (['call_state'].includes(payload.action)) {
electron.send('app_onAction', payload);
}
}
function getUpdateCheckStatus(status: boolean | string) {
function getUpdateCheckStatus(status: boolean | string): UpdateStatus {
if (status === true) {
return { status: UpdateCheckStatus.Downloading };
} else if (status === false) {
@@ -91,139 +87,16 @@ function getUpdateCheckStatus(status: boolean | string) {
}
}
interface IPCPayload {
id?: number;
error?: string;
reply?: any;
}
class SeshatIndexManager extends BaseEventIndexManager {
private pendingIpcCalls: Record<number, { resolve, reject }> = {};
private nextIpcCallId = 0;
constructor() {
super();
electron.on('seshatReply', this.onIpcReply);
}
private async ipcCall(name: string, ...args: any[]): Promise<any> {
// TODO this should be moved into the preload.js file.
const ipcCallId = ++this.nextIpcCallId;
return new Promise((resolve, reject) => {
this.pendingIpcCalls[ipcCallId] = { resolve, reject };
window.electron.send('seshat', { id: ipcCallId, name, args });
});
}
private onIpcReply = (ev: {}, payload: IPCPayload) => {
if (payload.id === undefined) {
logger.warn("Ignoring IPC reply with no ID");
return;
}
if (this.pendingIpcCalls[payload.id] === undefined) {
logger.warn("Unknown IPC payload ID: " + payload.id);
return;
}
const callbacks = this.pendingIpcCalls[payload.id];
delete this.pendingIpcCalls[payload.id];
if (payload.error) {
callbacks.reject(payload.error);
} else {
callbacks.resolve(payload.reply);
}
};
async supportsEventIndexing(): Promise<boolean> {
return this.ipcCall('supportsEventIndexing');
}
async initEventIndex(userId: string, deviceId: string): Promise<void> {
return this.ipcCall('initEventIndex', userId, deviceId);
}
async addEventToIndex(ev: IMatrixEvent, profile: IMatrixProfile): Promise<void> {
return this.ipcCall('addEventToIndex', ev, profile);
}
async deleteEvent(eventId: string): Promise<boolean> {
return this.ipcCall('deleteEvent', eventId);
}
async isEventIndexEmpty(): Promise<boolean> {
return this.ipcCall('isEventIndexEmpty');
}
async isRoomIndexed(roomId: string): Promise<boolean> {
return this.ipcCall('isRoomIndexed', roomId);
}
async commitLiveEvents(): Promise<void> {
return this.ipcCall('commitLiveEvents');
}
async searchEventIndex(searchConfig: ISearchArgs): Promise<IResultRoomEvents> {
return this.ipcCall('searchEventIndex', searchConfig);
}
async addHistoricEvents(
events: IEventAndProfile[],
checkpoint: ICrawlerCheckpoint | null,
oldCheckpoint: ICrawlerCheckpoint | null,
): Promise<boolean> {
return this.ipcCall('addHistoricEvents', events, checkpoint, oldCheckpoint);
}
async addCrawlerCheckpoint(checkpoint: ICrawlerCheckpoint): Promise<void> {
return this.ipcCall('addCrawlerCheckpoint', checkpoint);
}
async removeCrawlerCheckpoint(checkpoint: ICrawlerCheckpoint): Promise<void> {
return this.ipcCall('removeCrawlerCheckpoint', checkpoint);
}
async loadFileEvents(args): Promise<IEventAndProfile[]> {
return this.ipcCall('loadFileEvents', args);
}
async loadCheckpoints(): Promise<ICrawlerCheckpoint[]> {
return this.ipcCall('loadCheckpoints');
}
async closeEventIndex(): Promise<void> {
return this.ipcCall('closeEventIndex');
}
async getStats(): Promise<IIndexStats> {
return this.ipcCall('getStats');
}
async getUserVersion(): Promise<number> {
return this.ipcCall('getUserVersion');
}
async setUserVersion(version: number): Promise<void> {
return this.ipcCall('setUserVersion', version);
}
async deleteEventIndex(): Promise<void> {
return this.ipcCall('deleteEventIndex');
}
}
export default class ElectronPlatform extends VectorBasePlatform {
private eventIndexManager: BaseEventIndexManager = new SeshatIndexManager();
private pendingIpcCalls: Record<number, { resolve, reject }> = {};
private nextIpcCallId = 0;
private readonly ipc = new IPCManager("ipcCall", "ipcReply");
private readonly eventIndexManager: BaseEventIndexManager = new SeshatIndexManager();
// this is the opaque token we pass to the HS which when we get it in our callback we can resolve to a profile
private ssoID: string = randomString(32);
private readonly ssoID: string = randomString(32);
constructor() {
super();
dis.register(_onAction);
dis.register(onAction);
/*
IPC Call `check_updates` returns:
true if there is an update available
@@ -243,7 +116,6 @@ export default class ElectronPlatform extends VectorBasePlatform {
rageshake.flush();
});
electron.on('ipcReply', this.onIpcReply);
electron.on('update-downloaded', this.onUpdateDownloaded);
electron.on('preferences', () => {
@@ -278,14 +150,14 @@ export default class ElectronPlatform extends VectorBasePlatform {
});
});
this.ipcCall("startSSOFlow", this.ssoID);
this.ipc.call("startSSOFlow", this.ssoID);
}
async getConfig(): Promise<IConfigOptions> {
return this.ipcCall('getConfig');
public async getConfig(): Promise<IConfigOptions> {
return this.ipc.call('getConfig');
}
onUpdateDownloaded = async (ev, { releaseNotes, releaseName }) => {
private onUpdateDownloaded = async (ev, { releaseNotes, releaseName }) => {
dis.dispatch<CheckUpdatesPayload>({
action: Action.CheckUpdates,
status: UpdateCheckStatus.Ready,
@@ -295,7 +167,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
}
};
getHumanReadableName(): string {
public getHumanReadableName(): string {
return 'Electron Platform'; // no translation required: only used for analytics
}
@@ -303,7 +175,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
* Return true if platform supports multi-language
* spell-checking, otherwise false.
*/
supportsMultiLanguageSpellCheck(): boolean {
public supportsMultiLanguageSpellCheck(): boolean {
// Electron uses OS spell checking on macOS, so no need for in-app options
if (isMac) return false;
return true;
@@ -320,15 +192,21 @@ export default class ElectronPlatform extends VectorBasePlatform {
electron.send('setBadgeCount', count);
}
supportsNotifications(): boolean {
public supportsNotifications(): boolean {
return true;
}
maySendNotifications(): boolean {
public maySendNotifications(): boolean {
return true;
}
displayNotification(title: string, msg: string, avatarUrl: string, room: Room, ev?: MatrixEvent): Notification {
public displayNotification(
title: string,
msg: string,
avatarUrl: string,
room: Room,
ev?: MatrixEvent,
): Notification {
// GNOME notification spec parses HTML tags for styling...
// Electron Docs state all supported linux notification systems follow this markup spec
// https://github.com/electron/electron/blob/master/docs/tutorial/desktop-environment-integration.md#linux
@@ -350,100 +228,56 @@ export default class ElectronPlatform extends VectorBasePlatform {
const handler = notification.onclick as Function;
notification.onclick = () => {
handler?.();
this.ipcCall('focusWindow');
this.ipc.call('focusWindow');
};
return notification;
}
loudNotification(ev: MatrixEvent, room: Room) {
public loudNotification(ev: MatrixEvent, room: Room) {
electron.send('loudNotification');
}
async getAppVersion(): Promise<string> {
return this.ipcCall('getAppVersion');
public async getAppVersion(): Promise<string> {
return this.ipc.call('getAppVersion');
}
supportsAutoLaunch(): boolean {
return true;
public supportsSetting(settingName?: string): boolean {
switch (settingName) {
case "Electron.showTrayIcon": // Things other than Mac support tray icons
case "Electron.alwaysShowMenuBar": // This isn't relevant on Mac as Menu bars don't live in the app window
return !isMac;
default:
return true;
}
}
async getAutoLaunchEnabled(): Promise<boolean> {
return this.ipcCall('getAutoLaunchEnabled');
public getSettingValue(settingName: string): Promise<any> {
return this.ipc.call("getSettingValue", settingName);
}
async setAutoLaunchEnabled(enabled: boolean): Promise<void> {
return this.ipcCall('setAutoLaunchEnabled', enabled);
}
supportsWarnBeforeExit(): boolean {
return true;
}
async shouldWarnBeforeExit(): Promise<boolean> {
return this.ipcCall('shouldWarnBeforeExit');
}
async setWarnBeforeExit(enabled: boolean): Promise<void> {
return this.ipcCall('setWarnBeforeExit', enabled);
}
supportsAutoHideMenuBar(): boolean {
// This is irelevant on Mac as Menu bars don't live in the app window
return !isMac;
}
async getAutoHideMenuBarEnabled(): Promise<boolean> {
return this.ipcCall('getAutoHideMenuBarEnabled');
}
async setAutoHideMenuBarEnabled(enabled: boolean): Promise<void> {
return this.ipcCall('setAutoHideMenuBarEnabled', enabled);
}
supportsMinimizeToTray(): boolean {
// Things other than Mac support tray icons
return !isMac;
}
async getMinimizeToTrayEnabled(): Promise<boolean> {
return this.ipcCall('getMinimizeToTrayEnabled');
}
async setMinimizeToTrayEnabled(enabled: boolean): Promise<void> {
return this.ipcCall('setMinimizeToTrayEnabled', enabled);
}
public supportsTogglingHardwareAcceleration(): boolean {
return true;
}
public async getHardwareAccelerationEnabled(): Promise<boolean> {
return this.ipcCall('getHardwareAccelerationEnabled');
}
public async setHardwareAccelerationEnabled(enabled: boolean): Promise<void> {
return this.ipcCall('setHardwareAccelerationEnabled', enabled);
public setSettingValue(settingName: string, value: any): Promise<void> {
return this.ipc.call("setSettingValue", settingName, value);
}
async canSelfUpdate(): Promise<boolean> {
const feedUrl = await this.ipcCall('getUpdateFeedUrl');
const feedUrl = await this.ipc.call('getUpdateFeedUrl');
return Boolean(feedUrl);
}
startUpdateCheck() {
public startUpdateCheck() {
super.startUpdateCheck();
electron.send('check_updates');
}
installUpdate() {
public installUpdate() {
// IPC to the main process to install the update, since quitAndInstall
// doesn't fire the before-quit event so the main process needs to know
// it should exit.
electron.send('install_update');
}
getDefaultDeviceDisplayName(): string {
public getDefaultDeviceDisplayName(): string {
const brand = SdkConfig.get().brand;
return _t('%(brand)s Desktop (%(platformName)s)', {
brand,
@@ -451,86 +285,58 @@ export default class ElectronPlatform extends VectorBasePlatform {
});
}
screenCaptureErrorString(): string | null {
return null;
}
requestNotificationPermission(): Promise<string> {
public requestNotificationPermission(): Promise<string> {
return Promise.resolve('granted');
}
reload() {
public reload() {
window.location.reload();
}
private async ipcCall(name: string, ...args: any[]): Promise<any> {
const ipcCallId = ++this.nextIpcCallId;
return new Promise((resolve, reject) => {
this.pendingIpcCalls[ipcCallId] = { resolve, reject };
window.electron.send('ipcCall', { id: ipcCallId, name, args });
// Maybe add a timeout to these? Probably not necessary.
});
}
private onIpcReply = (ev, payload) => {
if (payload.id === undefined) {
logger.warn("Ignoring IPC reply with no ID");
return;
}
if (this.pendingIpcCalls[payload.id] === undefined) {
logger.warn("Unknown IPC payload ID: " + payload.id);
return;
}
const callbacks = this.pendingIpcCalls[payload.id];
delete this.pendingIpcCalls[payload.id];
if (payload.error) {
callbacks.reject(payload.error);
} else {
callbacks.resolve(payload.reply);
}
};
getEventIndexingManager(): BaseEventIndexManager | null {
public getEventIndexingManager(): BaseEventIndexManager | null {
return this.eventIndexManager;
}
async setLanguage(preferredLangs: string[]) {
return this.ipcCall('setLanguage', preferredLangs);
public async setLanguage(preferredLangs: string[]) {
return this.ipc.call('setLanguage', preferredLangs);
}
setSpellCheckLanguages(preferredLangs: string[]) {
this.ipcCall('setSpellCheckLanguages', preferredLangs).catch(error => {
public setSpellCheckLanguages(preferredLangs: string[]) {
this.ipc.call('setSpellCheckLanguages', preferredLangs).catch(error => {
logger.log("Failed to send setSpellCheckLanguages IPC to Electron");
logger.error(error);
});
}
async getSpellCheckLanguages(): Promise<string[]> {
return this.ipcCall('getSpellCheckLanguages');
public async getSpellCheckLanguages(): Promise<string[]> {
return this.ipc.call('getSpellCheckLanguages');
}
async getDesktopCapturerSources(options: GetSourcesOptions): Promise<Array<DesktopCapturerSource>> {
return this.ipcCall('getDesktopCapturerSources', options);
public async getDesktopCapturerSources(options: GetSourcesOptions): Promise<Array<DesktopCapturerSource>> {
return this.ipc.call('getDesktopCapturerSources', options);
}
supportsDesktopCapturer(): boolean {
public supportsDesktopCapturer(): boolean {
return true;
}
async getAvailableSpellCheckLanguages(): Promise<string[]> {
return this.ipcCall('getAvailableSpellCheckLanguages');
public async getAvailableSpellCheckLanguages(): Promise<string[]> {
return this.ipc.call('getAvailableSpellCheckLanguages');
}
getSSOCallbackUrl(fragmentAfterLogin: string): URL {
public getSSOCallbackUrl(fragmentAfterLogin: string): URL {
const url = super.getSSOCallbackUrl(fragmentAfterLogin);
url.protocol = "element";
url.searchParams.set("element-desktop-ssoid", this.ssoID);
return url;
}
startSingleSignOn(mxClient: MatrixClient, loginType: "sso" | "cas", fragmentAfterLogin: string, idpId?: string) {
public startSingleSignOn(
mxClient: MatrixClient,
loginType: "sso" | "cas",
fragmentAfterLogin: string,
idpId?: string,
) {
// this will get intercepted by electron-main will-navigate
super.startSingleSignOn(mxClient, loginType, fragmentAfterLogin, idpId);
Modal.createTrackedDialog('Electron', 'SSO', InfoDialog, {
@@ -540,16 +346,16 @@ export default class ElectronPlatform extends VectorBasePlatform {
}
public navigateForwardBack(back: boolean): void {
this.ipcCall(back ? "navigateBack" : "navigateForward");
this.ipc.call(back ? "navigateBack" : "navigateForward");
}
public overrideBrowserShortcuts(): boolean {
return true;
}
async getPickleKey(userId: string, deviceId: string): Promise<string | null> {
public async getPickleKey(userId: string, deviceId: string): Promise<string | null> {
try {
return await this.ipcCall('getPickleKey', userId, deviceId);
return await this.ipc.call('getPickleKey', userId, deviceId);
} catch (e) {
// if we can't connect to the password storage, assume there's no
// pickle key
@@ -557,9 +363,9 @@ export default class ElectronPlatform extends VectorBasePlatform {
}
}
async createPickleKey(userId: string, deviceId: string): Promise<string | null> {
public async createPickleKey(userId: string, deviceId: string): Promise<string | null> {
try {
return await this.ipcCall('createPickleKey', userId, deviceId);
return await this.ipc.call('createPickleKey', userId, deviceId);
} catch (e) {
// if we can't connect to the password storage, assume there's no
// pickle key
@@ -567,9 +373,9 @@ export default class ElectronPlatform extends VectorBasePlatform {
}
}
async destroyPickleKey(userId: string, deviceId: string): Promise<void> {
public async destroyPickleKey(userId: string, deviceId: string): Promise<void> {
try {
await this.ipcCall('destroyPickleKey', userId, deviceId);
await this.ipc.call('destroyPickleKey', userId, deviceId);
} catch (e) {}
}
}

View File

@@ -0,0 +1,70 @@
/*
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.
*/
import { defer, IDeferred } from 'matrix-js-sdk/src/utils';
import { logger } from "matrix-js-sdk/src/logger";
import { ElectronChannel } from "../../@types/global";
const electron = window.electron;
interface IPCPayload {
id?: number;
error?: string;
reply?: any;
}
export class IPCManager {
private pendingIpcCalls: { [ipcCallId: number]: IDeferred<any> } = {};
private nextIpcCallId = 0;
public constructor(
private readonly sendChannel: ElectronChannel = "ipcCall",
private readonly recvChannel: ElectronChannel = "ipcReply",
) {
electron.on(this.recvChannel, this.onIpcReply);
}
public async call(name: string, ...args: any[]): Promise<any> {
// TODO this should be moved into the preload.js file.
const ipcCallId = ++this.nextIpcCallId;
const deferred = defer<any>();
this.pendingIpcCalls[ipcCallId] = deferred;
// Maybe add a timeout to these? Probably not necessary.
window.electron.send(this.sendChannel, { id: ipcCallId, name, args });
return deferred.promise;
}
private onIpcReply = (ev: {}, payload: IPCPayload): void => {
if (payload.id === undefined) {
logger.warn("Ignoring IPC reply with no ID");
return;
}
if (this.pendingIpcCalls[payload.id] === undefined) {
logger.warn("Unknown IPC payload ID: " + payload.id);
return;
}
const callbacks = this.pendingIpcCalls[payload.id];
delete this.pendingIpcCalls[payload.id];
if (payload.error) {
callbacks.reject(payload.error);
} else {
callbacks.resolve(payload.reply);
}
};
}

View File

@@ -19,7 +19,7 @@ import { logger } from "matrix-js-sdk/src/logger";
import WebPlatform from "./WebPlatform";
export default class PWAPlatform extends WebPlatform {
setNotificationCount(count: number) {
public setNotificationCount(count: number): void {
if (!navigator.setAppBadge) return super.setNotificationCount(count);
if (this.notificationCount === count) return;
this.notificationCount = count;

View File

@@ -0,0 +1,105 @@
/*
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.
*/
import BaseEventIndexManager, {
ICrawlerCheckpoint,
IEventAndProfile,
IIndexStats,
ISearchArgs,
} from 'matrix-react-sdk/src/indexing/BaseEventIndexManager';
import { IMatrixProfile, IEventWithRoomId as IMatrixEvent, IResultRoomEvents } from "matrix-js-sdk/src/@types/search";
import { IPCManager } from "./IPCManager";
export class SeshatIndexManager extends BaseEventIndexManager {
private readonly ipc = new IPCManager("seshat", "seshatReply");
public async supportsEventIndexing(): Promise<boolean> {
return this.ipc.call('supportsEventIndexing');
}
public async initEventIndex(userId: string, deviceId: string): Promise<void> {
return this.ipc.call('initEventIndex', userId, deviceId);
}
public async addEventToIndex(ev: IMatrixEvent, profile: IMatrixProfile): Promise<void> {
return this.ipc.call('addEventToIndex', ev, profile);
}
public async deleteEvent(eventId: string): Promise<boolean> {
return this.ipc.call('deleteEvent', eventId);
}
public async isEventIndexEmpty(): Promise<boolean> {
return this.ipc.call('isEventIndexEmpty');
}
public async isRoomIndexed(roomId: string): Promise<boolean> {
return this.ipc.call('isRoomIndexed', roomId);
}
public async commitLiveEvents(): Promise<void> {
return this.ipc.call('commitLiveEvents');
}
public async searchEventIndex(searchConfig: ISearchArgs): Promise<IResultRoomEvents> {
return this.ipc.call('searchEventIndex', searchConfig);
}
public async addHistoricEvents(
events: IEventAndProfile[],
checkpoint: ICrawlerCheckpoint | null,
oldCheckpoint: ICrawlerCheckpoint | null,
): Promise<boolean> {
return this.ipc.call('addHistoricEvents', events, checkpoint, oldCheckpoint);
}
public async addCrawlerCheckpoint(checkpoint: ICrawlerCheckpoint): Promise<void> {
return this.ipc.call('addCrawlerCheckpoint', checkpoint);
}
public async removeCrawlerCheckpoint(checkpoint: ICrawlerCheckpoint): Promise<void> {
return this.ipc.call('removeCrawlerCheckpoint', checkpoint);
}
public async loadFileEvents(args): Promise<IEventAndProfile[]> {
return this.ipc.call('loadFileEvents', args);
}
public async loadCheckpoints(): Promise<ICrawlerCheckpoint[]> {
return this.ipc.call('loadCheckpoints');
}
public async closeEventIndex(): Promise<void> {
return this.ipc.call('closeEventIndex');
}
public async getStats(): Promise<IIndexStats> {
return this.ipc.call('getStats');
}
public async getUserVersion(): Promise<number> {
return this.ipc.call('getUserVersion');
}
public async setUserVersion(version: number): Promise<void> {
return this.ipc.call('setUserVersion', version);
}
public async deleteEventIndex(): Promise<void> {
return this.ipc.call('deleteEventIndex');
}
}

View File

@@ -30,11 +30,11 @@ import Favicon from "../../favicon";
export default abstract class VectorBasePlatform extends BasePlatform {
protected _favicon: Favicon;
async getConfig(): Promise<IConfigOptions> {
public async getConfig(): Promise<IConfigOptions> {
return getVectorConfig();
}
getHumanReadableName(): string {
public getHumanReadableName(): string {
return 'Vector Base Platform'; // no translation required: only used for analytics
}
@@ -43,7 +43,7 @@ export default abstract class VectorBasePlatform extends BasePlatform {
* it uses canvas, which can trigger a permission prompt in Firefox's resist fingerprinting mode.
* See https://github.com/vector-im/element-web/issues/9605.
*/
get favicon() {
public get favicon() {
if (this._favicon) {
return this._favicon;
}
@@ -62,13 +62,13 @@ export default abstract class VectorBasePlatform extends BasePlatform {
this.favicon.badge(notif, { bgColor });
}
setNotificationCount(count: number) {
public setNotificationCount(count: number) {
if (this.notificationCount === count) return;
super.setNotificationCount(count);
this.updateFavicon();
}
setErrorStatus(errorDidOccur: boolean) {
public setErrorStatus(errorDidOccur: boolean) {
if (this.errorDidOccur === errorDidOccur) return;
super.setErrorStatus(errorDidOccur);
this.updateFavicon();
@@ -77,14 +77,14 @@ export default abstract class VectorBasePlatform extends BasePlatform {
/**
* Begin update polling, if applicable
*/
startUpdater() {
public startUpdater() {
}
/**
* Get a sensible default display name for the
* device Vector is running on
*/
getDefaultDeviceDisplayName(): string {
public getDefaultDeviceDisplayName(): string {
return _t("Unknown device");
}
}

View File

@@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { UpdateCheckStatus } from "matrix-react-sdk/src/BasePlatform";
import { UpdateCheckStatus, UpdateStatus } from "matrix-react-sdk/src/BasePlatform";
import request from 'browser-request';
import dis from 'matrix-react-sdk/src/dispatcher/dispatcher';
import { _t } from 'matrix-react-sdk/src/languageHandler';
@@ -31,6 +31,15 @@ import { parseQs } from "../url_utils";
const POKE_RATE_MS = 10 * 60 * 1000; // 10 min
function getNormalizedAppVersion(version: string): string {
// if version looks like semver with leading v, strip it (matches scripts/normalize-version.sh)
const semVerRegex = /^v\d+.\d+.\d+(-.+)?$/;
if (semVerRegex.test(version)) {
return version.substring(1);
}
return version;
}
export default class WebPlatform extends VectorBasePlatform {
constructor() {
super();
@@ -40,7 +49,7 @@ export default class WebPlatform extends VectorBasePlatform {
}
}
getHumanReadableName(): string {
public getHumanReadableName(): string {
return 'Web Platform'; // no translation required: only used for analytics
}
@@ -48,7 +57,7 @@ export default class WebPlatform extends VectorBasePlatform {
* Returns true if the platform supports displaying
* notifications, otherwise false.
*/
supportsNotifications(): boolean {
public supportsNotifications(): boolean {
return Boolean(window.Notification);
}
@@ -56,7 +65,7 @@ export default class WebPlatform extends VectorBasePlatform {
* Returns true if the application currently has permission
* to display notifications. Otherwise false.
*/
maySendNotifications(): boolean {
public maySendNotifications(): boolean {
return window.Notification.permission === 'granted';
}
@@ -67,7 +76,7 @@ export default class WebPlatform extends VectorBasePlatform {
* that is 'granted' if the user allowed the request or
* 'denied' otherwise.
*/
requestNotificationPermission(): Promise<string> {
public requestNotificationPermission(): Promise<string> {
// annoyingly, the latest spec says this returns a
// promise, but this is only supported in Chrome 46
// and Firefox 47, so adapt the callback API.
@@ -99,26 +108,17 @@ export default class WebPlatform extends VectorBasePlatform {
return;
}
resolve(this.getNormalizedAppVersion(body.trim()));
resolve(getNormalizedAppVersion(body.trim()));
},
);
});
}
getNormalizedAppVersion(version: string): string {
// if version looks like semver with leading v, strip it (matches scripts/normalize-version.sh)
const semVerRegex = /^v\d+.\d+.\d+(-.+)?$/;
if (semVerRegex.test(version)) {
return version.substring(1);
}
return version;
public getAppVersion(): Promise<string> {
return Promise.resolve(getNormalizedAppVersion(process.env.VERSION));
}
getAppVersion(): Promise<string> {
return Promise.resolve(this.getNormalizedAppVersion(process.env.VERSION));
}
startUpdater() {
public startUpdater(): void {
// Poll for an update immediately, and reload the page now if we're out of date
// already as we've just initialised an old version of the app somehow.
//
@@ -127,7 +127,7 @@ export default class WebPlatform extends VectorBasePlatform {
//
// Ideally, loading an old copy would be impossible with the
// cache-control: nocache HTTP header set, but Firefox doesn't always obey it :/
console.log("startUpdater, current version is " + this.getNormalizedAppVersion(process.env.VERSION));
console.log("startUpdater, current version is " + getNormalizedAppVersion(process.env.VERSION));
this.pollForUpdate((version: string, newVersion: string) => {
const query = parseQs(location);
if (query.updated) {
@@ -147,16 +147,16 @@ export default class WebPlatform extends VectorBasePlatform {
setInterval(() => this.pollForUpdate(showUpdateToast, hideUpdateToast), POKE_RATE_MS);
}
async canSelfUpdate(): Promise<boolean> {
public async canSelfUpdate(): Promise<boolean> {
return true;
}
pollForUpdate = (
private pollForUpdate = (
showUpdate: (currentVersion: string, mostRecentVersion: string) => void,
showNoUpdate?: () => void,
) => {
): Promise<UpdateStatus> => {
return this.getMostRecentVersion().then((mostRecentVersion) => {
const currentVersion = this.getNormalizedAppVersion(process.env.VERSION);
const currentVersion = getNormalizedAppVersion(process.env.VERSION);
if (currentVersion !== mostRecentVersion) {
if (this.shouldShowUpdate(mostRecentVersion)) {
@@ -181,7 +181,7 @@ export default class WebPlatform extends VectorBasePlatform {
});
};
startUpdateCheck() {
public startUpdateCheck(): void {
super.startUpdateCheck();
this.pollForUpdate(showUpdateToast, hideUpdateToast).then((updateState) => {
dis.dispatch<CheckUpdatesPayload>({
@@ -191,11 +191,11 @@ export default class WebPlatform extends VectorBasePlatform {
});
}
installUpdate() {
public installUpdate(): void {
window.location.reload();
}
getDefaultDeviceDisplayName(): string {
public getDefaultDeviceDisplayName(): string {
// strip query-string and fragment from uri
const url = new URL(window.location.href);
@@ -217,15 +217,7 @@ export default class WebPlatform extends VectorBasePlatform {
});
}
screenCaptureErrorString(): string | null {
// it won't work at all if you're not on HTTPS so whine whine whine
if (window.location.protocol !== "https:") {
return _t("You need to be using HTTPS to place a screen-sharing call.");
}
return null;
}
reload() {
public reload(): void {
window.location.reload();
}
}

View File

@@ -8215,7 +8215,7 @@ mathml-tag-names@^2.1.3:
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
"matrix-analytics-events@github:matrix-org/matrix-analytics-events.git#a0687ca6fbdb7258543d49b99fb88b9201e900b0":
"matrix-analytics-events@github:matrix-org/matrix-analytics-events#a0687ca6fbdb7258543d49b99fb88b9201e900b0":
version "0.0.1"
resolved "https://codeload.github.com/matrix-org/matrix-analytics-events/tar.gz/a0687ca6fbdb7258543d49b99fb88b9201e900b0"
@@ -8229,9 +8229,10 @@ matrix-events-sdk@^0.0.1-beta.7:
resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1-beta.7.tgz#5ffe45eba1f67cc8d7c2377736c728b322524934"
integrity sha512-9jl4wtWanUFSy2sr2lCjErN/oC8KTAtaeaozJtrgot1JiQcEI4Rda9OLgQ7nLKaqb4Z/QUx/fR3XpDzm5Jy1JA==
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
version "18.0.0"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/2f540e31a5dd266a463f65c07eec19024bfc5762"
matrix-js-sdk@18.1.0:
version "18.1.0"
resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-18.1.0.tgz#9fa312e71cf153c4a8aac88d908c5ddd52181c6c"
integrity sha512-O5D36paIsY7a2M2mOo7KKU7v1Mb3PVkmYKupXYcXd9gB/Ki1K4mds+vSDEhgkKyKwk6MK1AV/vgvp0xJCsttvg==
dependencies:
"@babel/runtime" "^7.12.5"
another-json "^0.2.0"
@@ -8252,9 +8253,10 @@ matrix-mock-request@^2.0.0:
dependencies:
expect "^1.20.2"
"matrix-react-sdk@github:matrix-org/matrix-react-sdk#develop":
version "3.45.0"
resolved "https://codeload.github.com/matrix-org/matrix-react-sdk/tar.gz/d214387c88d991935ca9c7775e47a2cf9dd1d972"
matrix-react-sdk@3.47.0:
version "3.47.0"
resolved "https://registry.yarnpkg.com/matrix-react-sdk/-/matrix-react-sdk-3.47.0.tgz#d662ac4eafc18b22366a7c46e749aa014c073e05"
integrity sha512-s6v0Z8rYHJJNKmPcu/8SyvzqcODskirYozi9nCsHKOGqa2ziuIZzkJUZhtOMwOO7k8pEUaCL58Hc1tERtNzkOA==
dependencies:
"@babel/runtime" "^7.12.5"
"@sentry/browser" "^6.11.0"
@@ -8292,7 +8294,7 @@ matrix-mock-request@^2.0.0:
matrix-analytics-events "github:matrix-org/matrix-analytics-events.git#a0687ca6fbdb7258543d49b99fb88b9201e900b0"
matrix-encrypt-attachment "^1.0.3"
matrix-events-sdk "^0.0.1-beta.7"
matrix-js-sdk "github:matrix-org/matrix-js-sdk#develop"
matrix-js-sdk "18.1.0"
matrix-widget-api "^0.1.0-beta.18"
minimist "^1.2.5"
opus-recorder "^8.0.3"