Merge branch 'bwindels/redesign' into experimental

pull/7541/head
Bruno Windels 2018-10-15 14:13:11 +02:00
commit 0d7093fe76
721 changed files with 24132 additions and 20293 deletions

View File

@ -1,4 +1,4 @@
{
"presets": ["react", "es2015", "es2016"],
"plugins": ["transform-class-properties", "transform-object-rest-spread", "transform-async-to-generator", "transform-runtime", "add-module-exports"]
"plugins": ["transform-class-properties", "transform-object-rest-spread", "transform-async-to-bluebird", "transform-runtime", "add-module-exports"]
}

View File

@ -1,3 +1,5 @@
<!-- Please report security issues by email to security@matrix.org -->
<!-- This is a bug report template. By following the instructions below and
filling out the sections with your information, you will help the us to get all
the necessary data to fix your issue.

5
.gitignore vendored
View File

@ -14,6 +14,9 @@
npm-debug.log
electron/dist
electron/pub
/.idea
**/.idea
/config.json
/config.json.*
/config.local*.json
/src/component-index.js
/.tmp

View File

@ -1,3 +1,13 @@
# we need trusty for the chrome addon
dist: trusty
# we don't need sudo, so can run in a container, which makes startup much
# quicker.
#
# unfortunately we do temporarily require sudo as a workaround for
# https://github.com/travis-ci/travis-ci/issues/8836
sudo: required
language: node_js
node_js:
# make sure we work with a range of node versions.
@ -5,8 +15,9 @@ node_js:
# - 4.x is still in LTS (until April 2018), but some of our deps (notably
# extract-zip) don't work with it
# - 5.x has been EOLed for nearly a year.
# - 6.x is the current 'LTS' version
# - 7.x is the current 'current' version (until October 2017)
# - 6.x is the active 'LTS' version
# - 7.x is no longer supported
# - 8.x is the current 'current' version (until October 2017)
#
# see: https://github.com/nodejs/LTS/
#
@ -16,6 +27,8 @@ node_js:
- 6.3
- 6
- 7
addons:
chrome: stable
install:
# clone the deps with depth 1: we know we will only ever need that one
# commit.

View File

@ -13,3 +13,6 @@ include:
* Michael Telatynski (https://github.com/t3chguy)
Improved consistency of inverted elements in dark theme across browsers
* Alexandr Korsak (https://github.com/oivoodoo)
Improved multiple file uploading

File diff suppressed because it is too large Load Diff

181
README.md
View File

@ -16,13 +16,13 @@ released version of Riot:
1. Download the latest version from https://github.com/vector-im/riot-web/releases
1. Untar the tarball on your web server
1. Move (or symlink) the vector-x.x.x directory to an appropriate name
1. Move (or symlink) the riot-x.x.x directory to an appropriate name
1. If desired, copy `config.sample.json` to `config.json` and edit it
as desired. See below for details.
1. Enter the URL into your browser and log into Riot!
Releases are signed by PGP, and can be checked against the public key
at https://riot.im/packages/keys/riot-master.asc
at https://riot.im/packages/keys/riot.asc
Note that Chrome does not allow microphone or webcam access for sites served
over http (except localhost), so for working VoIP you will need to serve Riot
@ -49,20 +49,30 @@ We have put some coarse mitigations into place to try to protect against this
situation, but it's still not good practice to do it in the first place. See
https://github.com/vector-im/riot-web/issues/1977 for more details.
The same applies for end-to-end encrypted content, but since this is decrypted
on the client, Riot needs a way to supply the decrypted content from a separate
origin to the one Riot is hosted on. This currently done with a 'cross origin
renderer' which is a small piece of javascript hosted on a different domain.
To avoid all Riot installs needing one of these to be set up, riot.im hosts
one on usercontent.riot.im which is used by default. See 'config.json' if you'd
like to host your own. https://github.com/vector-im/riot-web/issues/6173 tracks
progress on replacing this with something better.
Building From Source
====================
Riot is a modular webapp built with modern ES6 and requires a npm build system
to build.
1. Install or update `node.js` so that your `node` is at least v6.3.0 (and `npm`
is at least v3.10.x).
1. Install or update `node.js` so that your `node` is at least v8.12.0 (and `npm`
is at least v5.x).
1. Clone the repo: `git clone https://github.com/vector-im/riot-web.git`.
1. Switch to the riot-web directory: `cd riot-web`.
1. If you're using the `develop` branch, install the develop versions of the
dependencies, as the released ones will be too old:
1. If you're using the `develop` branch then it is recommended to set up a proper
development environment ("Setting up a dev environment" below) however one can
install the develop versions of the dependencies instead:
```
scripts/fetch-develop-deps.sh
scripts/fetch-develop.deps.sh
```
Whenever you git pull on riot-web you will also probably need to force an update
to these dependencies - the simplest way is to re-run the script, but you can also
@ -80,13 +90,9 @@ to build.
npm install
npm run build
```
However, we recommend setting up a proper development environment (see "Setting
up a development environment" below) if you want to run your own copy of the
`develop` branch, as it makes it much easier to keep these dependencies
up-to-date. Or just use https://riot.im/develop - the continuous integration
release of the develop branch.
(Note that we don't reference the develop versions in git directly due to
https://github.com/npm/npm/issues/3055.)
Or just use https://riot.im/develop - the continuous integration release of the
develop branch. (Note that we don't reference the develop versions in git directly
due to https://github.com/npm/npm/issues/3055.)
1. Install the prerequisites: `npm install`.
1. Configure the app by copying `config.sample.json` to `config.json` and
modifying it (see below for details).
@ -106,7 +112,9 @@ config.json
You can configure the app by copying `config.sample.json` to
`config.json` and customising it:
1. `default_hs_url` is the default home server url.
For a good example, see https://riot.im/develop/config.json
1. `default_hs_url` is the default homeserver url.
1. `default_is_url` is the default identity server url (this is the server used
for verifying third party identifiers like email addresses). If this is blank,
registering with an email address, adding an email address to your account,
@ -115,19 +123,53 @@ You can configure the app by copying `config.sample.json` to
addresses) to matrix IDs: see http://matrix.org/docs/spec/identity_service/unstable.html
for more details. Currently the only public matrix identity servers are https://matrix.org
and https://vector.im. In future identity servers will be decentralised.
1. `integrations_ui_url`: URL to the web interface for the integrations server.
1. `features`: Lookup of optional features that may be `enable`d, `disable`d, or exposed to the user
in the `labs` section of settings. The available optional experimental features vary from
release to release.
1. `brand`: String to pass to your homeserver when configuring email notifications, to let the
homeserver know what email template to use when talking to you.
1. `integrations_ui_url`: URL to the web interface for the integrations server. The integrations
server is not Riot and normally not your Home Server either. The integration server settings
may be left blank to disable integrations.
1. `integrations_rest_url`: URL to the REST interface for the integrations server.
1. `integrations_widgets_urls`: list of URLs to the REST interface for the widget integrations server.
1. `bug_report_endpoint_url`: endpoint to send bug reports to (must be running a
https://github.com/matrix-org/rageshake server). Bug reports are sent when a user clicks
"Send Logs" within the application. Bug reports can be disabled by leaving the
`bug_report_endpoint_url` out of your config file.
1. `roomDirectory`: config for the public room directory. This section is optional.
1. `roomDirectory.servers`: List of other Home Servers' directories to include in the drop
1. `roomDirectory.servers`: List of other homeservers' directories to include in the drop
down list. Optional.
1. `default_theme`: name of theme to use by default (e.g. 'light')
1. `update_base_url` (electron app only): HTTPS URL to a web server to download
updates from. This should be the path to the directory containing `macos`
and `win32` (for update packages, not installer packages).
1. `cross_origin_renderer_url`: URL to a static HTML page hosting code to help display
encrypted file attachments. This MUST be hosted on a completely separate domain to
anything else since it is used to isolate the privileges of file attachments to this
domain. Default: `usercontent.riot.im`. This needs to contain v1.html from
domain. Default: `https://usercontent.riot.im/v1.html`. This needs to contain v1.html from
https://github.com/matrix-org/usercontent/blob/master/v1.html
1. `piwik`: Analytics can be disabled by setting `piwik: false` or by leaving the piwik config
option out of your config file. If you want to enable analytics, set `piwik` to be an object
containing the following properties:
1. `url`: The URL of the Piwik instance to use for collecting analytics
1. `whitelistedHSUrls`: a list of HS URLs to not redact from the analytics
1. `whitelistedISUrls`: a list of IS URLs to not redact from the analytics
1. `siteId`: The Piwik Site ID to use when sending analytics to the Piwik server configured above
1. `teamServerConfig`, `teamTokenMap`, `referralBaseUrl`: an obsolete precursor to communities
with referral tracking; please ignore it.
1. `welcomeUserId`: the user ID of a bot to invite whenever users register that can give them a tour
Note that `index.html` also has an og:image meta tag that is set to an image
hosted on riot.im. This is the image used if links to your copy of Riot
appear in some websites like Facebook, and indeed Riot itself. This has to be
static in the HTML and an absolute URL (and HTTP rather than HTTPS), so it's
not possible for this to be an option in config.json. If you'd like to change
it, you can build Riot as above, but run
`RIOT_OG_IMAGE_URL="http://example.com/logo.png" npm run build`.
Alternatively, you can edit the `og:image` meta tag in `index.html` directly
each time you download a new version of Riot.
Running as a Desktop app
========================
@ -198,19 +240,7 @@ higher and lower level React components useful for building Matrix communication
apps using React.
After creating a new component you must run `npm run reskindex` to regenerate
the `component-index.js` for the app (used in future for skinning)
**However, as of July 2016 this layering abstraction is broken due to rapid
development on Riot forcing `matrix-react-sdk` to move fast at the expense of
maintaining a clear abstraction between the two.** Hacking on Riot inevitably
means hacking equally on `matrix-react-sdk`, and there are bits of
`matrix-react-sdk` behaviour incorrectly residing in the `riot-web` project
(e.g. matrix-react-sdk specific CSS), and a bunch of Riot specific behaviour
in the `matrix-react-sdk` (grep for `vector` / `riot`). This separation problem will be
solved asap once development on Riot (and thus matrix-react-sdk) has
stabilised. Until then, the two projects should basically be considered as a
single unit. In particular, `matrix-react-sdk` issues are currently filed
against `riot-web` in github.
the `component-index.js` for the app (used in future for skinning).
Please note that Riot is intended to run correctly without access to the public
internet. So please don't depend on resources (JS libs, CSS, images, fonts)
@ -227,33 +257,31 @@ having to manually rebuild each time.
First clone and build `matrix-js-sdk`:
1. `git clone git@github.com:matrix-org/matrix-js-sdk.git`
1. `git clone https://github.com/matrix-org/matrix-js-sdk.git`
1. `pushd matrix-js-sdk`
1. `git checkout develop`
1. `npm install`
1. `npm install source-map-loader` # because webpack is made of fail (https://github.com/webpack/webpack/issues/1472)
1. `npm install source-map-loader` # because webpack is made of fail (https://github.com/webpack/webpack/issues/1472)
1. `popd`
Then similarly with `matrix-react-sdk`:
1. `git clone git@github.com:matrix-org/matrix-react-sdk.git`
1. `git clone https://github.com/matrix-org/matrix-react-sdk.git`
1. `pushd matrix-react-sdk`
1. `git checkout develop`
1. `npm install`
1. `rm -r node_modules/matrix-js-sdk; ln -s ../../matrix-js-sdk node_modules/`
1. `npm link ../matrix-js-sdk`
1. `popd`
Finally, build and start Riot itself:
1. `git clone git@github.com:vector-im/riot-web.git`
1. `git clone https://github.com/vector-im/riot-web.git`
1. `cd riot-web`
1. `git checkout develop`
1. `npm install`
1. `rm -r node_modules/matrix-js-sdk; ln -s ../../matrix-js-sdk node_modules/`
1. `rm -r node_modules/matrix-react-sdk; ln -s ../../matrix-react-sdk node_modules/`
1. `npm link ../matrix-js-sdk`
1. `npm link ../matrix-react-sdk`
1. `npm start`
1. Wait a few seconds for the initial build to finish; you should see something like:
```
Hash: b0af76309dd56d7275c8
Version: webpack 1.12.14
@ -270,10 +298,8 @@ Finally, build and start Riot itself:
disables caching, so do NOT use it in production.
1. Open http://127.0.0.1:8080/ in your browser to see your newly built Riot.
When you make changes to `matrix-react-sdk` or `matrix-js-sdk`, you will need
to run `npm run build` in the relevant directory. You can do this automatically
by instead running `npm start` in the directory, to start a development builder
which will watch for changes to the files and rebuild automatically.
When you make changes to `matrix-react-sdk` or `matrix-js-sdk` they should be
automatically picked up by webpack and built.
If you add or remove any components from the Riot skin, you will need to rebuild
the skin's index by running, `npm run reskindex`.
@ -282,47 +308,82 @@ If any of these steps error with, `file table overflow`, you are probably on a m
which has a very low limit on max open files. Run `ulimit -Sn 1024` and try again.
You'll need to do this in each new terminal you open before building Riot.
How to add a new translation?
=============================
Running the tests
-----------------
[<img src="https://translate.nordgedanken.de/widgets/riot-web/-/multi-auto.svg" alt="translationsstatus" width="340">](https://translate.nordgedanken.de/engage/riot-web/?utm_source=widget)
There are a number of application-level tests in the `tests` directory; these
are designed to run in a browser instance under the control of
[karma](https://karma-runner.github.io). To run them:
* Make sure you have Chrome installed (a recent version, like 59)
* Make sure you have `matrix-js-sdk` and `matrix-react-sdk` installed and
built, as above
* `npm run test`
Head to the [translating doc](docs/translating.md)
The above will run the tests under Chrome in a `headless` mode.
Adding Strings to the translations (Developer Guide)
====================================================
You can also tell karma to run the tests in a loop (every time the source
changes), in an instance of Chrome on your desktop, with `npm run
test-multi`. This also gives you the option of running the tests in 'debug'
mode, which is useful for stepping through the tests in the developer tools.
Head to the [translating dev doc](docs/translating-dev.md)
Translations
============
To add a new translation, head to the [translating doc](docs/translating.md).
For a developer guide, see the [translating dev doc](docs/translating-dev.md).
[<img src="https://translate.riot.im/widgets/riot-web/-/multi-auto.svg" alt="translationsstatus" width="340">](https://translate.riot.im/engage/riot-web/?utm_source=widget)
Triaging issues
===============
Issues will be triaged by the core team using the following primary set of tags:
Issues will be triaged by the core team using the below set of tags.
priority:
Tags are meant to be used in combination - e.g.:
* P1 critical bug == really urgent stuff that should be next in the bugfixing todo list
* "release blocker" == stuff which is blocking us from cutting the next release.
* P1 feature type:voip == what VoIP features should we be working on next?
* P1: top priority; typically blocks releases
priority: **compulsory**
* P1: top priority - i.e. pool of stuff which we should be working on next
* P2: still need to fix, but lower than P1
* P3: non-urgent
* P4: intereseting idea - bluesky some day
* P4: interesting idea - bluesky some day
* P5: recorded for posterity/to avoid duplicates. No intention to resolves right now.
bug or feature:
bug or feature: **compulsory**
* bug
* feature
bug severity:
bug severity: **compulsory, if bug**
* cosmetic - feature works functionally but UI/UX is broken
* critical - whole app doesn't work
* major - entire feature doesn't work
* minor - partially broken feature (but still usable)
* cosmetic - feature works functionally but UI/UX is broken
additional categories:
types
* type:* - refers to a particular part of the app; used to filter bugs
on a given topic - e.g. VOIP, signup, timeline, etc.
additional categories (self-explanatory):
* release blocker
* ui/ux (think of this as cosmetic)
* network (specific to network conditions)
* platform (platform specific)
* platform specific
* accessibility
* maintenance
* performance
* i18n
* blocked - whether this issue currently can't be progressed due to outside factors
community engagement
* easy
* hacktoberfest
* bounty? - proposal to be included in a bounty programme
* bounty - included in Status Open Bounty

View File

@ -1,11 +1,22 @@
{
"default_hs_url": "https://matrix.org",
"default_is_url": "https://vector.im",
"disable_custom_urls": false,
"disable_guests": false,
"disable_login_language_selector": false,
"disable_3pid_login": false,
"brand": "Riot",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_jitsi_widget_url": "https://scalar.vector.im/api/widgets/jitsi.html",
"bug_report_endpoint_url": "https://riot.im/bugreports/submit",
"enableLabs": true,
"features": {
"feature_groups": "labs",
"feature_pinning": "labs"
},
"default_federate": true,
"welcomePageUrl": "home.html",
"default_theme": "light",
"roomDirectory": {
"servers": [
"matrix.org"
@ -14,6 +25,11 @@
"welcomeUserId": "@riot-bot:matrix.org",
"piwik": {
"url": "https://piwik.riot.im/",
"whitelistedHSUrls": ["https://matrix.org"],
"whitelistedISUrls": ["https://vector.im", "https://matrix.org"],
"siteId": 1
},
"enable_presence_by_hs_url": {
"https://matrix.org": false
}
}

68
docs/skinning thoughts.md Normal file
View File

@ -0,0 +1,68 @@
== Skinning refactor ==
matrix-react-sdk
- base images
- base CSS
- all the components needed to build a workable app (including the top layer)
riot-web: the riot skin
- riot-specific classes (e.g. login header/footer)
- riot-specific themes
- light
- dark
i.e. the only things which should go into riot-web are bits which apply vector-specific skinning
specifically "Stuff that any other brand would not want to use. (e.g. riot logos, links, T&Cs)"
- Questions:
- Electron app? (should probably be a separate repo in its own right? but might as well go here for now)
- index.html & index.js? (should be in matrix-react-sdk, given the SDK is useless without them?)
ideally matrix-react-sdk itself should ship with a default skin which actually works built in.
status skin (can go in the same app for now)
- has status theme
- which inherits from riot light theme
- how do we share graphics between skins?
- shove them into react-sdk, or...
- guess we do ../../vector/img
- this means keeping the skin name in the images (unless /img is a shortcut to the right skin's images)
out of scope:
- making the components more independent, so they can be used in isolation.
- that said, the bits which should probably be used by being embeded into a different app:
- login/reg
- RoomView + RoomSettings
- MessageComposer
- RoomList
- MemberList
- MemberInfo
- Voip UI
- UserSettings
- sharing different js-sdks between the different isolated modules
other changes:
- how do we handle i18n?
- each skin should really be its own i18n project. As long as all the commonality stuff is in matrix-react-sdk this shouldn't be too bad.
- ability to associate components with a given skin
- skins/vector/src <-- components
- skins/vector/css
- skins/vector/img
- skins/vector/fonts
- gather together themes (per skin) into a single place too
- skins/vector/themes/foo/css
- skins/vector/themes/foo/img
- skins/vector/themes/foo/fonts
- ideally riot-web would contain almost nothing but skins/vector directory.
- ability to entirely replace CSS rather than override it for a given theme
- e.g. if we replace `Login.js` with `StatusLogin.js`, then we should similarly be able to replace `_Login.scss` with `_StatusLogin.scss`.
random thoughts;
- should we be able to change the entire skin at runtime (more like wordpress) - to the extent of replacing entire components?
- might pose security issues if a theme can be swapped out to replace MatrixChat or other fundamental functionality at runtime
- if so, perhaps skins & themes should converge...
-----------------
Immediate plan for Status:
* Implement it as a theme for the riot skin
* Ideally move skins to a sensible level (possibly even including src?)

View File

@ -3,24 +3,61 @@
## Requirements
- A working [Development Setup](../../#setting-up-a-dev-environment)
- Including up-to-date versions of matrix-react-sdk and matrix-js-sdk
- Node 8 or later
- Be able to understand English
- Be able to understand the language you want to translate riot-web into
## Translating strings vs. marking strings for translation
Translating strings are done with the `_t()` function found in matrix-react-sdk/lib/languageHandler.js. It is recommended to call this function wherever you introduce a string constant which should be translated. However, translating can not be performed until after the translation system has been initialized. Thus, sometimes translation must be performed at a different location in the source code than where the string is introduced. This breaks some tooling and makes it difficult to find translatable strings. Therefore, there is the alternative `_td()` function which is used to mark strings for translation, without actually performing the translation (which must still be performed separately, and after the translation system has been initialized).
Basically, whenever a translatable string is introduced, you should call either `_t()` immediately OR `_td()` and later `_t()`.
Example:
```
// Module-level constant
const COLORS = {
'#f8481c': _td('reddish orange'), // Can't call _t() here yet
'#fc2647': _td('pinky red') // Use _td() instead so the text is picked up for translation anyway
}
// Function that is called some time after i18n has been loaded
function getColorName(hex) {
return _t(COLORS[hex]); // Perform actual translation here
}
```
## Adding new strings
1. Check if the import ``import _t from 'counterpart-riot'`` is present. If not add it to the other import statements.
2. Add ``_t()`` to your string. (Don't forget curly braces when you assign an expression to JSX attributes in the render method)
3. Add the String to the ``en_EN.json`` file in ``src/i18n`` or if you are working in matrix-react-sdk you can find the json file in ``src/i18n/strings``
1. Check if the import ``import { _t } from 'matrix-react-sdk/lib/languageHandler';`` is present. If not add it to the other import statements. Also import `_td` if needed.
1. Add ``_t()`` to your string. (Don't forget curly braces when you assign an expression to JSX attributes in the render method). If the string is introduced at a point before the translation system has not yet been initialized, use `_td()` instead, and call `_t()` at the appropriate time.
1. Run `npm run i18n` to update ``src/i18n/strings/en_EN.json``
1. If you added a string with a plural, you can add other English plural variants to ``src/i18n/strings/en_EN.json`` (remeber to edit the one in the same project as the source file containing your new translation).
## Editing existing strings
1. Edit every occurrence of the string inside `_t()` and `_td()` in the JSX files.
1. Run `npm run i18n` to update `src/i18n/strings/en_EN.json`. (Be sure to run this in the same project as the JSX files you just edited.)
1. Run `npm run prunei18n` to remove the old string from `src/i18n/strings/*.json`.
## Adding variables inside a string.
1. Extend your ``_t()`` call. Instead of ``_t(STRING)`` use ``_t(STRING, {})``
2. Decide how to name it. Please think about if the person who has to translate it can understand what it does.
3. Add it to the array in ``_t`` for example ``_t(STRING, {variable: this.variable})``
4. Add the variable inside the string. The syntax for variables is ``%(variable)s``. Please note the s at the end. The name of the variable has to match the previous used name.
1. Decide how to name it. Please think about if the person who has to translate it can understand what it does. E.g. using the name 'recipient' is bad, because a translator does not know if it is the name of a person, an email address, a user ID, etc. Rather use e.g. recipientEmailAddress.
1. Add it to the array in ``_t`` for example ``_t(STRING, {variable: this.variable})``
1. Add the variable inside the string. The syntax for variables is ``%(variable)s``. Please note the _s_ at the end. The name of the variable has to match the previous used name.
- You can use the special ``count`` variable to choose between multiple versions of the same string, in order to get the correct pluralization. E.g. ``_t('You have %(count)s new messages', { count: 2 })`` would show 'You have 2 new messages', while ``_t('You have %(count)s new messages', { count: 1 })`` would show 'You have one new message' (assuming a singular version of the string has been added to the translation file. See above). Passing in ``count`` is much prefered over having an if-statement choose the correct string to use, because some languages have much more complicated plural rules than english (e.g. they might need a completely different form if there are three things rather than two).
- If you want to translate text that includes e.g. hyperlinks or other HTML you have to also use tag substitution, e.g. ``_t('<a>Click here!</a>', {}, { 'a': (sub) => <a>{sub}</a> })``. If you don't do the tag substitution you will end up showing literally '<a>' rather than making a hyperlink.
- You can also use React components with normal variable substitution if you want to insert HTML markup, e.g. ``_t('Your email address is %(emailAddress)s', { emailAddress: <i>{userEmailAddress}</i> })``.
## Things to know/Style Guides
- Do not use it inside ``getDefaultProps`` at the point where ``getDefaultProps`` is initialized the translations aren't loaded yet and it causes missing translations.
- If using translated strings as constants, translated strings can't be in constants loaded at class-load time since the translations won't be loaded.
- Do not use `_t()` inside ``getDefaultProps``: the translations aren't loaded when `getDefaultProps` is called, leading to missing translations. Use `_td()` to indicate that `_t()` will be called on the string later.
- If using translated strings as constants, translated strings can't be in constants loaded at class-load time since the translations won't be loaded. Mark the strings using `_td()` instead and perform the actual translation later.
- If a string is presented in the UI with punctuation like a full stop, include this in the translation strings, since punctuation varies between languages too.
- Avoid "translation in parts", i.e. concatenating translated strings or using translated strings in variable substitutions. Context is important for translations, and translating partial strings this way is simply not always possible.
- Concatenating strings often also introduces an implicit assumption about word order (e.g. that the subject of the sentence comes first), which is incorrect for many languages.
- Translation 'smell test': If you have a string that does not begin with a capital letter (is not the start of a sentence) or it ends with e.g. ':' or a preposition (e.g. 'to') you should recheck that you are not trying to translate a partial sentence.
- If you have multiple strings, that are almost identical, except some part (e.g. a word or two) it is still better to translate the full sentence multiple times. It may seem like inefficient repetion, but unlike programming where you try to minimize repetition, translation is much faster if you have many, full, clear, sentences to work with, rather than fewer, but incomplete sentence fragments.

View File

@ -13,20 +13,20 @@
## Step 1: Preparing your Weblate Profile
1. Head to https://translate.nordgedanken.de and register either via Github or email
1. Head to https://translate.riot.im and register either via Github or email
2. After registering check if you got an email to verify your account and click the link (if there is none head to step 1.4)
3. Log into weblate
4. Head to https://translate.nordgedanken.de/accounts/profile/ and select the languages you know and maybe another language you know too.
6. Head to https://translate.nordgedanken.de/accounts/profile/#subscriptions and select Riot Web as Project
4. Head to https://translate.riot.im/accounts/profile/ and select the languages you know and maybe another language you know too.
6. Head to https://translate.riot.im/accounts/profile/#subscriptions and select Riot Web as Project
## How to check if your language already is being translated
Go to https://translate.nordgedanken.de/projects/riot-web/ and visit the 2 sub-projects.
Go to https://translate.riot.im/projects/riot-web/ and visit the 2 sub-projects.
If your language is listed go to Step 2a and if not go to Step 2b
## Step 2a: Helping on existing languages.
1. Head to one of the projects listed https://translate.nordgedanken.de/projects/riot-web/
1. Head to one of the projects listed https://translate.riot.im/projects/riot-web/
2. Click on the ``translate`` button on the right side of your language
3. Fill in the translations in the writeable field. You will see the original English string and the string of your second language above.
@ -34,8 +34,8 @@ Head to the explanations under Steb 2b
## Step 2b: Adding a new language
1. Go to one of the projects listed https://translate.nordgedanken.de/projects/riot-web/
2. Click the ``Start new language`` button at the bottom
1. Go to one of the projects listed https://translate.riot.im/projects/riot-web/
2. Click the ``Start new translation`` button at the bottom
3. Select a language
4. Start translating like in 2a.3
5. Repeat these steps for the other projects which are listed at the link of step 2b.1

View File

@ -2,7 +2,7 @@
"name": "riot-web",
"productName": "Riot",
"main": "src/electron-main.js",
"version": "0.10.2",
"version": "0.16.5",
"description": "A feature-rich client for Matrix.org",
"author": "Vector Creations Ltd.",
"dependencies": {

View File

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
MIIF0jCCBLqgAwIBAgIRAISYBqZi3VvCUeSfHXF+cbwwDQYJKoZIhvcNAQELBQAw
gZExCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTcwNQYD
VQQDEy5DT01PRE8gUlNBIEV4dGVuZGVkIFZhbGlkYXRpb24gQ29kZSBTaWduaW5n
IENBMB4XDTE3MDgyMzAwMDAwMFoXDTIwMDgyMjIzNTk1OVowgdgxETAPBgNVBAUT
CDEwODczNjYxMRMwEQYLKwYBBAGCNzwCAQMTAkdCMR0wGwYDVQQPExRQcml2YXRl
IE9yZ2FuaXphdGlvbjELMAkGA1UEBhMCR0IxETAPBgNVBBEMCFdDMVIgNEFHMQ8w
DQYDVQQIDAZMb25kb24xDzANBgNVBAcMBkxvbmRvbjEbMBkGA1UECQwSMjYgUmVk
IExpb24gU3F1YXJlMRcwFQYDVQQKDA5OZXcgVmVjdG9yIEx0ZDEXMBUGA1UEAwwO
TmV3IFZlY3RvciBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7
X0HP3oM/SVr6PboD03ndtYTONZDcJ/GJ3EyYi6UNrcbKjuDHwPktx9hjAhNjcVkG
lmuTEPluPj9DbvjaTrers0cQsAS1vJ0RHjLfA93Flg1ys9Q6OThUMw77FtFPtiJU
z5cSYzfFAhn/4dv7BcgGptn+Mv/8CaTu+RUZJUgoSlRWcT1TREmxkzWotbblqsHO
zjDmUg20tL5/qpt6BSWsNespf5udKQFXMtqkczBcLvBLmql0vurVcQy8BibB+Q89
QKwRzwLgaIa7O8WEssFcW8uJe9s0SNtUy8ehbuoSxpA/DbHFwsiDbNA78vp7HrqM
qY6t6OIgLtDYBFCfe/btAgMBAAGjggHaMIIB1jAfBgNVHSMEGDAWgBTfj/MgDOnK
pgTYW1g3Kj2rRtyDSTAdBgNVHQ4EFgQUH+mDOdRkF3bYDxCWEaGB4lxiCxcwDgYD
VR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMw
EQYJYIZIAYb4QgEBBAQDAgQQMEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQYBMCsw
KQYIKwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5jb20vQ1BTMFUGA1Ud
HwROMEwwSqBIoEaGRGh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET1JTQUV4
dGVuZGVkVmFsaWRhdGlvbkNvZGVTaWduaW5nQ0EuY3JsMIGGBggrBgEFBQcBAQR6
MHgwUAYIKwYBBQUHMAKGRGh0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9ET1JT
QUV4dGVuZGVkVmFsaWRhdGlvbkNvZGVTaWduaW5nQ0EuY3J0MCQGCCsGAQUFBzAB
hhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wJgYDVR0RBB8wHaAbBggrBgEFBQcI
A6APMA0MC0dCLTEwODczNjYxMA0GCSqGSIb3DQEBCwUAA4IBAQBJ2aH4aixh0aiz
4WKlK+LMVLHpQ2POE3FZYNpAW7o1q2YDGEADXdGrygPE9NCGNBXKo0CAemCYNWfX
Ov/jdoiMfeqW3vrZ66oEy8OqbvJSwK1xmomWuYw3wYPWcPVG+YbWYD2CGdQu8jTz
fzAJCpvAuY3Wji3fQjiecAC7JCSB4fBHa0ALJOmiSqKQUUpkXs5kW7O0lPBnHzNF
2tQGltXMSIrq1QfFtcreMyKlwDOxPIh360dv5aHhaeSRDRKxq7uq5ikQF2gjKx4k
ieg2HRbAW6fVPpFr4zRS5umpeZV3i06i11VQQPS/mA/OBEXyaqzx4mr6B7U6ptrp
jMqiUv2w
-----END CERTIFICATE-----

View File

@ -5,9 +5,12 @@
"brand": "Riot",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_widgets_urls": [
"https://scalar-staging.riot.im/scalar/api",
"https://scalar.vector.im/api"
],
"bug_report_endpoint_url": "https://riot.im/bugreports/submit",
"welcomeUserId": "@riot-bot:matrix.org",
"enableLabs": true,
"roomDirectory": {
"servers": [
"matrix.org"
@ -15,6 +18,10 @@
},
"piwik": {
"url": "https://piwik.riot.im/",
"siteId": 1
"siteId": 1,
"policyUrl": "https://matrix.org/docs/guides/riot_im_cookie_policy"
},
"enable_presence_by_hs_url": {
"https://matrix.org": false
}
}

View File

@ -23,17 +23,18 @@ const checkSquirrelHooks = require('./squirrelhooks');
if (checkSquirrelHooks()) return;
const argv = require('minimist')(process.argv);
const electron = require('electron');
const {app, ipcMain, powerSaveBlocker, BrowserWindow, Menu} = require('electron');
const AutoLaunch = require('auto-launch');
const tray = require('./tray');
const vectorMenu = require('./vectormenu');
const webContentsHandler = require('./webcontents-handler');
const updater = require('./updater');
const windowStateKeeper = require('electron-window-state');
if (argv.profile) {
electron.app.setPath('userData', `${electron.app.getPath('userData')}-${argv.profile}`);
if (argv['profile']) {
app.setPath('userData', `${app.getPath('userData')}-${argv['profile']}`);
}
let vectorConfig = {};
@ -46,69 +47,9 @@ try {
// Continue with the defaults (ie. an empty config)
}
const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000;
const INITIAL_UPDATE_DELAY_MS = 30 * 1000;
let mainWindow = null;
let appQuitting = false;
global.appQuitting = false;
function installUpdate() {
// for some reason, quitAndInstall does not fire the
// before-quit event, so we need to set the flag here.
appQuitting = true;
electron.autoUpdater.quitAndInstall();
}
function pollForUpdates() {
try {
electron.autoUpdater.checkForUpdates();
} catch (e) {
console.log('Couldn\'t check for update', e);
}
}
function startAutoUpdate(updateBaseUrl) {
if (updateBaseUrl.slice(-1) !== '/') {
updateBaseUrl = updateBaseUrl + '/';
}
try {
// For reasons best known to Squirrel, the way it checks for updates
// is completely different between macOS and windows. On macOS, it
// hits a URL that either gives it a 200 with some json or
// 204 No Content. On windows it takes a base path and looks for
// files under that path.
if (process.platform === 'darwin') {
// include the current version in the URL we hit. Electron doesn't add
// it anywhere (apart from the User-Agent) so it's up to us. We could
// (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.
electron.autoUpdater.setFeedURL(
`${updateBaseUrl}macos/?localVersion=${encodeURIComponent(electron.app.getVersion())}`);
} else if (process.platform === 'win32') {
electron.autoUpdater.setFeedURL(`${updateBaseUrl}win32/${process.arch}/`);
} else {
// 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
// implemented it on Linux, or if it would be different again.
console.log('Auto update not supported on this platform');
}
// 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:
// every hour should be just fine for a desktop app)
// However, we still let the main window listen for the update events.
// We also wait a short time before checking for updates the first time because
// of squirrel on windows and it taking a small amount of time to release a
// lock file.
setTimeout(pollForUpdates, INITIAL_UPDATE_DELAY_MS);
setInterval(pollForUpdates, UPDATE_POLL_INTERVAL_MS);
} catch (err) {
// will fail if running in debug mode
console.log('Couldn\'t enable update checking', err);
}
}
// handle uncaught errors otherwise it displays
// stack traces in popup dialogs, which is terrible (which
@ -120,17 +61,15 @@ process.on('uncaughtException', function(error) {
console.log('Unhandled exception', error);
});
electron.ipcMain.on('install_update', installUpdate);
let focusHandlerAttached = false;
electron.ipcMain.on('setBadgeCount', function(ev, count) {
electron.app.setBadgeCount(count);
if (count === 0) {
ipcMain.on('setBadgeCount', function(ev, count) {
app.setBadgeCount(count);
if (count === 0 && mainWindow) {
mainWindow.flashFrame(false);
}
});
electron.ipcMain.on('loudNotification', function() {
ipcMain.on('loudNotification', function() {
if (process.platform === 'win32' && mainWindow && !mainWindow.isFocused() && !focusHandlerAttached) {
mainWindow.flashFrame(true);
mainWindow.once('focus', () => {
@ -142,16 +81,16 @@ electron.ipcMain.on('loudNotification', function() {
});
let powerSaveBlockerId;
electron.ipcMain.on('app_onAction', function(ev, payload) {
ipcMain.on('app_onAction', function(ev, payload) {
switch (payload.action) {
case 'call_state':
if (powerSaveBlockerId && powerSaveBlockerId.isStarted(powerSaveBlockerId)) {
if (powerSaveBlockerId && powerSaveBlocker.isStarted(powerSaveBlockerId)) {
if (payload.state === 'ended') {
electron.powerSaveBlocker.stop(powerSaveBlockerId);
powerSaveBlocker.stop(powerSaveBlockerId);
}
} else {
if (payload.state === 'connected') {
powerSaveBlockerId = electron.powerSaveBlocker.start('prevent-display-sleep');
powerSaveBlockerId = powerSaveBlocker.start('prevent-display-sleep');
}
}
break;
@ -159,9 +98,12 @@ electron.ipcMain.on('app_onAction', function(ev, payload) {
});
electron.app.commandLine.appendSwitch('--enable-usermedia-screen-capturing');
app.commandLine.appendSwitch('--enable-usermedia-screen-capturing');
const shouldQuit = app.makeSingleInstance((commandLine, workingDirectory) => {
// If other instance launched with --hidden then skip showing window
if (commandLine.includes('--hidden')) return;
const shouldQuit = electron.app.makeSingleInstance((commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (mainWindow) {
if (!mainWindow.isVisible()) mainWindow.show();
@ -172,7 +114,7 @@ const shouldQuit = electron.app.makeSingleInstance((commandLine, workingDirector
if (shouldQuit) {
console.log('Other instance detected: exiting');
electron.app.exit();
app.exit();
}
@ -197,7 +139,7 @@ const settings = {
},
};
electron.ipcMain.on('settings_get', async function(ev) {
ipcMain.on('settings_get', async function(ev) {
const data = {};
try {
@ -206,34 +148,37 @@ electron.ipcMain.on('settings_get', async function(ev) {
}));
ev.sender.send('settings', data);
} catch(e) { console.error(e); }
} catch (e) {
console.error(e);
}
});
electron.ipcMain.on('settings_set', function(ev, key, value) {
ipcMain.on('settings_set', function(ev, key, value) {
console.log(key, value);
if (settings[key] && settings[key].set) {
settings[key].set(value);
}
});
electron.app.on('ready', () => {
if (argv.devtools) {
app.on('ready', () => {
if (argv['devtools']) {
try {
const { default: installExtension, REACT_DEVELOPER_TOOLS, REACT_PERF } = require('electron-devtools-installer');
installExtension(REACT_DEVELOPER_TOOLS)
const { default: installExt, REACT_DEVELOPER_TOOLS, REACT_PERF } = require('electron-devtools-installer');
installExt(REACT_DEVELOPER_TOOLS)
.then((name) => console.log(`Added Extension: ${name}`))
.catch((err) => console.log('An error occurred: ', err));
installExtension(REACT_PERF)
installExt(REACT_PERF)
.then((name) => console.log(`Added Extension: ${name}`))
.catch((err) => console.log('An error occurred: ', err));
} catch(e) {console.log(e);}
} catch (e) {
console.log(e);
}
}
if (vectorConfig.update_base_url) {
console.log(`Starting auto update with base URL: ${vectorConfig.update_base_url}`);
startAutoUpdate(vectorConfig.update_base_url);
if (vectorConfig['update_base_url']) {
console.log(`Starting auto update with base URL: ${vectorConfig['update_base_url']}`);
updater.start(vectorConfig['update_base_url']);
} else {
console.log('No update_base_url is defined: auto update is disabled');
}
@ -246,7 +191,7 @@ electron.app.on('ready', () => {
defaultHeight: 768,
});
mainWindow = new electron.BrowserWindow({
mainWindow = global.mainWindow = new BrowserWindow({
icon: iconPath,
show: false,
autoHideMenuBar: true,
@ -257,29 +202,34 @@ electron.app.on('ready', () => {
height: mainWindowState.height,
});
mainWindow.loadURL(`file://${__dirname}/../../webapp/index.html`);
electron.Menu.setApplicationMenu(vectorMenu);
Menu.setApplicationMenu(vectorMenu);
// explicitly hide because setApplicationMenu on Linux otherwise shows...
// https://github.com/electron/electron/issues/9621
mainWindow.hide();
// Create trayIcon icon
tray.create(mainWindow, {
tray.create({
icon_path: iconPath,
brand: vectorConfig.brand || 'Riot',
});
if (!argv.hidden) {
mainWindow.once('ready-to-show', () => {
mainWindow.once('ready-to-show', () => {
mainWindowState.manage(mainWindow);
if (!argv['hidden']) {
mainWindow.show();
});
}
} else {
// hide here explicitly because window manage above sometimes shows it
mainWindow.hide();
}
});
mainWindow.on('closed', () => {
mainWindow = null;
mainWindow = global.mainWindow = null;
});
mainWindow.on('close', (e) => {
if (!appQuitting && (tray.hasTray() || process.platform === 'darwin')) {
if (!global.appQuitting && (tray.hasTray() || process.platform === 'darwin')) {
// On Mac, closing the window just hides it
// (this is generally how single-window Mac apps
// behave, eg. Mail.app)
@ -289,24 +239,37 @@ electron.app.on('ready', () => {
}
});
if (process.platform === 'win32') {
// Handle forward/backward mouse buttons in Windows
mainWindow.on('app-command', (e, cmd) => {
if (cmd === 'browser-backward' && mainWindow.webContents.canGoBack()) {
mainWindow.webContents.goBack();
} else if (cmd === 'browser-forward' && mainWindow.webContents.canGoForward()) {
mainWindow.webContents.goForward();
}
});
}
webContentsHandler(mainWindow.webContents);
mainWindowState.manage(mainWindow);
});
electron.app.on('window-all-closed', () => {
electron.app.quit();
app.on('window-all-closed', () => {
app.quit();
});
electron.app.on('activate', () => {
app.on('activate', () => {
mainWindow.show();
});
electron.app.on('before-quit', () => {
appQuitting = true;
app.on('before-quit', () => {
global.appQuitting = true;
if (mainWindow) {
mainWindow.webContents.send('before-quit');
}
});
// Set the App User Model ID to match what the squirrel
// installer uses for the shortcut icon.
// This makes notifications work on windows 8.1 (and is
// a noop on other platforms).
electron.app.setAppUserModelId('com.squirrel.riot-web.Riot');
app.setAppUserModelId('com.squirrel.riot-web.Riot');

View File

@ -26,17 +26,17 @@ exports.hasTray = function hasTray() {
return (trayIcon !== null);
};
exports.create = function(win, config) {
exports.create = function(config) {
// no trays on darwin
if (process.platform === 'darwin' || trayIcon) return;
const toggleWin = function() {
if (win.isVisible() && !win.isMinimized()) {
win.hide();
if (global.mainWindow.isVisible() && !global.mainWindow.isMinimized()) {
global.mainWindow.hide();
} else {
if (win.isMinimized()) win.restore();
if (!win.isVisible()) win.show();
win.focus();
if (global.mainWindow.isMinimized()) global.mainWindow.restore();
if (!global.mainWindow.isVisible()) global.mainWindow.show();
global.mainWindow.focus();
}
};
@ -54,41 +54,46 @@ exports.create = function(win, config) {
},
]);
trayIcon = new Tray(config.icon_path);
const defaultIcon = nativeImage.createFromPath(config.icon_path);
trayIcon = new Tray(defaultIcon);
trayIcon.setToolTip(config.brand);
trayIcon.setContextMenu(contextMenu);
trayIcon.on('click', toggleWin);
let lastFavicon = null;
win.webContents.on('page-favicon-updated', async function(ev, favicons) {
let newFavicon = config.icon_path;
if (favicons && favicons.length > 0 && favicons[0].startsWith('data:')) {
newFavicon = favicons[0];
global.mainWindow.webContents.on('page-favicon-updated', async function(ev, favicons) {
if (!favicons || favicons.length <= 0 || !favicons[0].startsWith('data:')) {
if (lastFavicon !== null) {
win.setIcon(defaultIcon);
trayIcon.setImage(defaultIcon);
lastFavicon = null;
}
return;
}
// No need to change, shortcut
if (newFavicon === lastFavicon) return;
lastFavicon = newFavicon;
if (favicons[0] === lastFavicon) return;
lastFavicon = favicons[0];
// if its not default we have to construct into nativeImage
if (newFavicon !== config.icon_path) {
newFavicon = nativeImage.createFromDataURL(favicons[0]);
let newFavicon = nativeImage.createFromDataURL(favicons[0]);
if (process.platform === 'win32') {
try {
const icoPath = path.join(app.getPath('temp'), 'win32_riot_icon.ico')
const icoBuf = await pngToIco(newFavicon.toPNG());
fs.writeFileSync(icoPath, icoBuf);
newFavicon = icoPath;
} catch (e) {console.error(e);}
// Windows likes ico's too much.
if (process.platform === 'win32') {
try {
const icoPath = path.join(app.getPath('temp'), 'win32_riot_icon.ico');
fs.writeFileSync(icoPath, await pngToIco(newFavicon.toPNG()));
newFavicon = nativeImage.createFromPath(icoPath);
} catch (e) {
console.error("Failed to make win32 ico", e);
}
}
trayIcon.setImage(newFavicon);
win.setIcon(newFavicon);
global.mainWindow.setIcon(newFavicon);
});
win.webContents.on('page-title-updated', function(ev, title) {
global.mainWindow.webContents.on('page-title-updated', function(ev, title) {
trayIcon.setToolTip(title);
});
};

View File

@ -0,0 +1,84 @@
const { app, autoUpdater, ipcMain } = require('electron');
const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000;
const INITIAL_UPDATE_DELAY_MS = 30 * 1000;
function installUpdate() {
// for some reason, quitAndInstall does not fire the
// before-quit event, so we need to set the flag here.
global.appQuitting = true;
autoUpdater.quitAndInstall();
}
function pollForUpdates() {
try {
autoUpdater.checkForUpdates();
} catch (e) {
console.log('Couldn\'t check for update', e);
}
}
module.exports = {};
module.exports.start = function startAutoUpdate(updateBaseUrl) {
if (updateBaseUrl.slice(-1) !== '/') {
updateBaseUrl = updateBaseUrl + '/';
}
try {
let url;
// For reasons best known to Squirrel, the way it checks for updates
// is completely different between macOS and windows. On macOS, it
// hits a URL that either gives it a 200 with some json or
// 204 No Content. On windows it takes a base path and looks for
// files under that path.
if (process.platform === 'darwin') {
// include the current version in the URL we hit. Electron doesn't add
// it anywhere (apart from the User-Agent) so it's up to us. We could
// (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}/`;
} else {
// 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
// implemented it on Linux, or if it would be different again.
console.log('Auto update not supported on this platform');
}
if (url) {
autoUpdater.setFeedURL(url);
// 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:
// every hour should be just fine for a desktop app)
// However, we still let the main window listen for the update events.
// We also wait a short time before checking for updates the first time because
// of squirrel on windows and it taking a small amount of time to release a
// lock file.
setTimeout(pollForUpdates, INITIAL_UPDATE_DELAY_MS);
setInterval(pollForUpdates, UPDATE_POLL_INTERVAL_MS);
}
} catch (err) {
// will fail if running in debug mode
console.log('Couldn\'t enable update checking', err);
}
}
ipcMain.on('install_update', installUpdate);
ipcMain.on('check_updates', pollForUpdates);
function ipcChannelSendUpdateStatus(status) {
if (global.mainWindow) {
global.mainWindow.webContents.send('check_updates', status);
}
}
autoUpdater.on('update-available', function() {
ipcChannelSendUpdateStatus(true);
}).on('update-not-available', function() {
ipcChannelSendUpdateStatus(false);
}).on('error', function(error) {
ipcChannelSendUpdateStatus(error.message);
});

View File

@ -19,7 +19,7 @@ const {app, shell, Menu} = require('electron');
// Menu template from http://electron.atom.io/docs/api/menu/, edited
const template = [
{
label: 'Edit',
label: '&Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
@ -33,7 +33,7 @@ const template = [
],
},
{
label: 'View',
label: '&View',
submenu: [
{ type: 'separator' },
{ role: 'resetzoom' },
@ -45,6 +45,7 @@ const template = [
],
},
{
label: '&Window',
role: 'window',
submenu: [
{ role: 'minimize' },
@ -52,6 +53,7 @@ const template = [
],
},
{
label: '&Help',
role: 'help',
submenu: [
{
@ -122,7 +124,7 @@ if (process.platform === 'darwin') {
];
} else {
template.unshift({
label: 'File',
label: '&File',
submenu: [
// For some reason, 'about' does not seem to work on windows.
/*{

View File

@ -35,12 +35,15 @@ function onLinkContextMenu(ev, params) {
const url = params.linkURL || params.srcURL;
const popupMenu = new Menu();
popupMenu.append(new MenuItem({
label: url,
click() {
safeOpenURL(url);
},
}));
// No point trying to open blob: URLs in an external browser: it ain't gonna work.
if (!url.startsWith('blob:')) {
popupMenu.append(new MenuItem({
label: url,
click() {
safeOpenURL(url);
},
}));
}
if (params.mediaType && params.mediaType === 'image' && !url.startsWith('file://')) {
popupMenu.append(new MenuItem({
@ -55,13 +58,17 @@ function onLinkContextMenu(ev, params) {
}));
}
popupMenu.append(new MenuItem({
label: 'Copy Link Address',
click() {
clipboard.writeText(url);
},
}));
popupMenu.popup();
// No point offerring to copy a blob: URL either
if (!url.startsWith('blob:')) {
popupMenu.append(new MenuItem({
label: 'Copy Link Address',
click() {
clipboard.writeText(url);
},
}));
}
// popup() requires an options object even for no options
popupMenu.popup({});
ev.preventDefault();
}
@ -88,7 +95,8 @@ function onSelectedContextMenu(ev, params) {
const items = _CutCopyPasteSelectContextMenus(params);
const popupMenu = Menu.buildFromTemplate(items);
popupMenu.popup();
// popup() requires an options object even for no options
popupMenu.popup({});
ev.preventDefault();
}
@ -101,7 +109,8 @@ function onEditableContextMenu(ev, params) {
const popupMenu = Menu.buildFromTemplate(items);
popupMenu.popup();
// popup() requires an options object even for no options
popupMenu.popup({});
ev.preventDefault();
}

View File

@ -32,9 +32,12 @@ const olm_entry = webpack_config.entry['olm'];
// 'preprocessors' config below)
delete webpack_config['entry'];
// make sure we're flagged as development to avoid wasting time optimising
webpack_config.mode = 'development';
// add ./test as a search path for js
webpack_config.module.loaders.unshift({
test: /\.js$/, loader: "babel",
webpack_config.module.rules.unshift({
test: /\.js$/, use: "babel-loader",
include: [path.resolve('./src'), path.resolve('./test')],
});
@ -46,8 +49,9 @@ webpack_config.module.noParse.push(/sinon\/pkg\/sinon\.js$/);
// ?
webpack_config.resolve.alias['sinon'] = 'sinon/pkg/sinon.js';
webpack_config.resolve.root = [
webpack_config.resolve.modules = [
path.resolve('./test'),
"node_modules"
];
webpack_config.devtool = 'inline-source-map';
@ -70,27 +74,42 @@ module.exports = function (config) {
// This isn't required by any of the tests, but it stops karma
// logging warnings when it serves a 404 for them.
{
pattern: 'src/skins/vector/img/*',
pattern: 'node_modules/matrix-react-sdk/res/img/*',
watched: false, included: false, served: true, nocache: false,
},
{
pattern: 'res/themes/**',
watched: false, included: false, served: true, nocache: false,
},
],
proxies: {
// redirect img links to the karma server. See above.
"/img/": "/base/src/skins/vector/img/",
"/img/": "/base/node_modules/matrix-react-sdk/res/img/",
"/themes/": "/base/res/themes/",
},
// preprocess matching files before serving them to the browser
// available preprocessors:
// https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'{src,test}/**/*.js': ['webpack'],
'{src,test}/**/*.js': ['webpack', 'sourcemap'],
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress', 'junit'],
reporters: ['logcapture', 'spec', 'junit', 'summary'],
specReporter: {
suppressErrorSummary: false, // do print error summary
suppressFailed: false, // do print information about failed tests
suppressPassed: false, // do print information about passed tests
showSpecTiming: true, // print the time elapsed for each spec
},
client: {
captureLogs: true,
},
// web server port
port: 9876,
@ -113,8 +132,23 @@ module.exports = function (config) {
browsers: [
'Chrome',
//'PhantomJS',
//'ChromeHeadless'
],
customLaunchers: {
'ChromeHeadless': {
base: 'Chrome',
flags: [
// '--no-sandbox',
// See https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md
'--headless',
'--disable-gpu',
// Without a remote debugging port, Google Chrome exits immediately.
'--remote-debugging-port=9222',
],
}
},
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
// singleRun: false,

17261
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,9 +2,9 @@
"name": "riot-web",
"productName": "Riot",
"main": "electron_app/src/electron-main.js",
"version": "0.10.2",
"version": "0.16.5",
"description": "A feature-rich client for Matrix.org",
"author": "Vector Creations Ltd.",
"author": "New Vector Ltd.",
"repository": {
"type": "git",
"url": "https://github.com/vector-im/riot-web"
@ -28,117 +28,122 @@
"scripts": {
"reskindex": "reskindex -h src/header",
"reskindex:watch": "reskindex -h src/header -w",
"i18n": "matrix-gen-i18n",
"prunei18n": "matrix-prune-i18n",
"build:res": "node scripts/copy-res.js",
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
"build:compile": "npm run reskindex && babel --source-maps -d lib src",
"build:bundle": "cross-env NODE_ENV=production webpack -p --progress",
"build:bundle:dev": "webpack --optimize-occurence-order --progress",
"build:bundle": "cross-env NODE_ENV=production webpack-cli -p --progress --bail --mode production",
"build:bundle:dev": "webpack-cli --progress --bail --mode development",
"build:electron": "npm run clean && npm run build && npm run install:electron && build -wml --ia32 --x64",
"build": "npm run reskindex && npm run build:res && npm run build:bundle",
"build:dev": "npm run reskindex && npm run build:res && npm run build:bundle:dev",
"build:react-sdk": "node scripts/npm-sub.js matrix-react-sdk run build",
"build:js-sdk": "node scripts/npm-sub.js matrix-js-sdk run start:init",
"build": "npm run build:js-sdk && npm run build:react-sdk && npm run reskindex && npm run build:res && npm run build:bundle",
"build:dev": "npm run build:js-sdk && npm run build:react-sdk && npm run reskindex && npm run build:res && npm run build:bundle:dev",
"dist": "scripts/package.sh",
"install:electron": "install-app-deps",
"electron": "npm run install:electron && electron .",
"start:res": "node scripts/copy-res.js -w",
"start:js": "webpack-dev-server --output-filename=bundles/_dev_/[name].js --output-chunk-file=bundles/_dev_/[name].js -w --progress",
"start:js": "webpack-dev-server --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js -w --progress --mode development",
"start:js:prod": "cross-env NODE_ENV=production webpack-dev-server -w --progress",
"start": "parallelshell \"npm run reskindex:watch\" \"npm run start:res\" \"npm run start:js\"",
"start:prod": "parallelshell \"npm run reskindex:watch\" \"npm run start:res\" \"npm run start:js:prod\"",
"start:js-sdk": "node scripts/npm-sub.js matrix-js-sdk run start:watch",
"start:js-sdk:prod": "cross-env NODE_ENV=production node scripts/npm-sub.js matrix-js-sdk run start:watch",
"start:react-sdk": "node scripts/npm-sub.js matrix-react-sdk run start:all",
"start:react-sdk:prod": "cross-env NODE_ENV=production node scripts/npm-sub.js matrix-react-sdk run start:all",
"start": "npm run build:js-sdk && npm run build:react-sdk && concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n js-sdk,react-sdk,reskindex,res,riot-js \"npm run start:js-sdk\" \"npm run start:react-sdk\" \"npm run reskindex:watch\" \"npm run start:res\" \"npm run start:js\"",
"start:prod": "npm run build:js-sdk && npm run build:react-sdk && concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n js-sdk,react-sdk,reskindex,res,riot-js \"npm run start:js-sdk:prod\" \"npm run start:react-sdk:prod\" \"npm run reskindex:watch\" \"npm run start:res\" \"npm run start:js:prod\"",
"lint": "eslint src/",
"lintall": "eslint src/ test/",
"clean": "rimraf lib webapp electron/dist",
"prepublish": "npm run build:compile",
"test": "karma start --single-run=true --autoWatch=false --browsers PhantomJS --colors=false",
"clean": "rimraf lib webapp electron_app/dist",
"prepublish": "npm run clean && npm run build:compile",
"test": "karma start --single-run=true --autoWatch=false --browsers ChromeHeadless",
"test-multi": "karma start"
},
"dependencies": {
"babel-polyfill": "^6.5.0",
"babel-runtime": "^6.11.6",
"babel-polyfill": "^6.26.0",
"babel-runtime": "^6.26.0",
"bluebird": "^3.5.2",
"browser-request": "^0.3.3",
"classnames": "^2.1.2",
"draft-js": "^0.8.1",
"extract-text-webpack-plugin": "^0.9.1",
"draft-js": "^0.11.0-alpha",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"favico.js": "^0.3.10",
"filesize": "3.5.6",
"flux": "~2.0.3",
"gemini-scrollbar": "matrix-org/gemini-scrollbar#b302279",
"gfm.css": "^1.1.1",
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#b302279",
"gfm.css": "^1.1.2",
"highlight.js": "^9.0.0",
"linkifyjs": "^2.1.3",
"matrix-js-sdk": "0.7.10",
"matrix-react-sdk": "0.9.2",
"modernizr": "^3.1.0",
"pako": "^1.0.5",
"q": "^1.4.1",
"react": "^15.4.0",
"react-dnd": "^2.1.4",
"react-dnd-html5-backend": "^2.1.2",
"react-dom": "^15.4.0",
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
"sanitize-html": "^1.11.1",
"ua-parser-js": "^0.7.10",
"url": "^0.11.0",
"velocity-vector": "vector-im/velocity#059e3b2"
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#develop",
"modernizr": "^3.6.0",
"prop-types": "^15.6.2",
"react": "^15.6.0",
"react-dom": "^15.6.0",
"sanitize-html": "^1.18.4",
"ua-parser-js": "^0.7.18",
"url": "^0.11.0"
},
"devDependencies": {
"autoprefixer": "^6.6.0",
"babel-cli": "^6.5.2",
"babel-core": "^6.14.0",
"babel-eslint": "^6.1.0",
"babel-loader": "^6.2.5",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-eslint": "^6.1.2",
"babel-loader": "^7.1.5",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-transform-async-to-generator": "^6.16.0",
"babel-plugin-transform-class-properties": "^6.16.0",
"babel-plugin-transform-object-rest-spread": "^6.16.0",
"babel-plugin-transform-runtime": "^6.15.0",
"babel-preset-es2015": "^6.16.0",
"babel-preset-es2016": "^6.16.0",
"babel-preset-es2017": "^6.16.0",
"babel-preset-react": "^6.16.0",
"babel-preset-stage-2": "^6.17.0",
"babel-plugin-transform-async-to-bluebird": "^1.1.1",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-es2016": "^6.24.1",
"babel-preset-es2017": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"chokidar": "^1.6.1",
"concurrently": "^4.0.1",
"cpx": "^1.3.2",
"cross-env": "^4.0.0",
"css-raw-loader": "^0.1.1",
"electron-builder": "^11.2.4",
"electron-builder-squirrel-windows": "^11.2.1",
"electron-devtools-installer": "^2.2.0",
"electron-devtools-installer": "^2.2.4",
"emojione": "^2.2.7",
"eslint": "^3.14.0",
"eslint": "^5.6.0",
"eslint-config-google": "^0.7.1",
"eslint-plugin-babel": "^4.1.1",
"eslint-plugin-flowtype": "^2.30.0",
"eslint-plugin-react": "^6.9.0",
"eslint-plugin-babel": "^4.1.2",
"eslint-plugin-flowtype": "^2.50.3",
"eslint-plugin-react": "^7.11.1",
"expect": "^1.16.0",
"fs-extra": "^0.30.0",
"html-webpack-plugin": "^2.24.0",
"html-webpack-plugin": "^3.2.0",
"json-loader": "^0.5.3",
"karma": "^0.13.22",
"karma": "^3.0.0",
"karma-chrome-launcher": "^0.2.3",
"karma-cli": "^0.1.2",
"karma-junit-reporter": "^0.4.1",
"karma-mocha": "^0.2.2",
"karma-phantomjs-launcher": "^1.0.0",
"karma-webpack": "^1.7.0",
"karma-cli": "^1.0.1",
"karma-junit-reporter": "^2.0.0",
"karma-logcapture-reporter": "0.0.1",
"karma-mocha": "^1.3.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-spec-reporter": "0.0.31",
"karma-summary-reporter": "^1.5.1",
"karma-webpack": "4.0.0-beta.0",
"matrix-mock-request": "^1.2.0",
"matrix-react-test-utils": "^0.2.0",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"mocha": "^2.4.5",
"parallelshell": "^1.2.0",
"phantomjs-prebuilt": "^2.1.7",
"mocha": "^5.2.0",
"postcss-extend": "^1.0.5",
"postcss-import": "^9.0.0",
"postcss-loader": "^1.2.2",
"postcss-mixins": "^5.4.1",
"postcss-nested": "^1.0.0",
"postcss-scss": "^0.4.0",
"postcss-simple-vars": "^3.0.0",
"postcss-import": "^11.1.0",
"postcss-loader": "^2.1.6",
"postcss-mixins": "^6.2.0",
"postcss-nested": "^3.0.0",
"postcss-scss": "^1.0.6",
"postcss-simple-vars": "^4.1.0",
"postcss-strip-inline-comments": "^0.1.5",
"raw-loader": "^0.5.1",
"react-addons-perf": "^15.4.0",
"react-addons-test-utils": "^15.4.0",
"react-addons-test-utils": "^15.6.0",
"rimraf": "^2.4.3",
"source-map-loader": "^0.1.5",
"webpack": "^1.12.14",
"webpack-dev-server": "^1.16.2"
"source-map-loader": "^0.2.4",
"webpack": "^4.20.2",
"webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.1.9"
},
"optionalDependencies": {
"olm": "https://matrix.org/packages/npm/olm/olm-2.2.1.tgz"
@ -146,7 +151,7 @@
"build": {
"appId": "im.riot.app",
"category": "Network",
"electronVersion": "1.6.8",
"electronVersion": "2.0.8",
"//asar=false": "https://github.com/electron-userland/electron-builder/issues/675",
"asar": false,
"dereference": true,

View File

@ -7,11 +7,20 @@
set -e
orig_args=$@
# chomp any args starting with '-' as these need to go
# through to the release script and otherwise we'll get
# confused about what the version arg is.
while [[ "$1" == -* ]]; do
shift
done
cd `dirname $0`
for i in matrix-js-sdk matrix-react-sdk
do
depver=`cat package.json | jq -r .dependencies.\"$i\"`
depver=`cat package.json | jq -r .dependencies[\"$i\"]`
latestver=`npm show $i version`
if [ "$depver" != "$latestver" ]
then
@ -33,9 +42,9 @@ echo "electron npm version"
cd electron_app
npm version --no-git-tag-version "$release"
git commit package.json -m "$tag"
git commit package.json package-lock.json -m "$tag"
cd ..
exec ./node_modules/matrix-js-sdk/release.sh -z "$@"
exec ./node_modules/matrix-js-sdk/release.sh -z "$orig_args"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1015 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 811 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 785 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Some files were not shown because too many files have changed in this diff Show More