mirror of
https://github.com/element-hq/element-web.git
synced 2025-09-17 11:04:05 +02:00
Compare commits
200 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c04db7ece4 | ||
|
|
8daa0fced8 | ||
|
|
65a160f24a | ||
|
|
43a70c2bfb | ||
|
|
a14bc9a9b1 | ||
|
|
ba0073ca71 | ||
|
|
60703bdcfc | ||
|
|
cfe9e762ad | ||
|
|
9969d6095d | ||
|
|
38b1ca9b90 | ||
|
|
b4b33ae06f | ||
|
|
1e569e67b1 | ||
|
|
48889b51b0 | ||
|
|
5312c9ad4c | ||
|
|
bd1196716a | ||
|
|
cb17cb0535 | ||
|
|
39ff11fe82 | ||
|
|
5d4b98d4f6 | ||
|
|
6fcf504a4b | ||
|
|
515f8de2c5 | ||
|
|
2ea69624af | ||
|
|
176f7f1515 | ||
|
|
f36c1ca207 | ||
|
|
83353e9419 | ||
|
|
c614bc2356 | ||
|
|
f7f5255378 | ||
|
|
23a0e9a96c | ||
|
|
8b89a1770d | ||
|
|
bfe43d841f | ||
|
|
423a047687 | ||
|
|
cc56ef3abd | ||
|
|
fbcdc5adad | ||
|
|
b75180d959 | ||
|
|
fab50bc1f1 | ||
|
|
cfe7fbb23e | ||
|
|
8477ed2967 | ||
|
|
a1132ca7aa | ||
|
|
6e52ab6182 | ||
|
|
e33e0effa3 | ||
|
|
0788826a71 | ||
|
|
c79c0bd333 | ||
|
|
e4c3a63375 | ||
|
|
a9c8429d2e | ||
|
|
9af8cfd1dc | ||
|
|
93502b3c2a | ||
|
|
46ff43e9ba | ||
|
|
954d56e151 | ||
|
|
f040a72765 | ||
|
|
8b65b96adf | ||
|
|
cf5cf02529 | ||
|
|
26ea8a7494 | ||
|
|
27d83ba440 | ||
|
|
c380e7566a | ||
|
|
81518069ed | ||
|
|
a06eb5dfe1 | ||
|
|
7d3f75ef87 | ||
|
|
54ef2dd39b | ||
|
|
b51101038f | ||
|
|
79ad89c673 | ||
|
|
e7dd6fa714 | ||
|
|
a46a0c0949 | ||
|
|
a13b766bad | ||
|
|
969c7c93d6 | ||
|
|
d1c97d8f9a | ||
|
|
ae8ad6969a | ||
|
|
6d62bf0e25 | ||
|
|
26a27a6de4 | ||
|
|
51cfad5b9f | ||
|
|
ca3399df77 | ||
|
|
c4a3211cfc | ||
|
|
d9f662c06e | ||
|
|
abf21675d5 | ||
|
|
8a17e3543a | ||
|
|
c70ad2fa65 | ||
|
|
8f8b28425d | ||
|
|
6b5bfe24e6 | ||
|
|
ae23664a41 | ||
|
|
bbbb82351f | ||
|
|
67a9b6502b | ||
|
|
fe1b0b5cd8 | ||
|
|
8dade0d84e | ||
|
|
499b1c2e70 | ||
|
|
047b255e14 | ||
|
|
3926e4dd98 | ||
|
|
639b17c663 | ||
|
|
ea8a49f1b7 | ||
|
|
eda3f20b26 | ||
|
|
6ef32f182a | ||
|
|
6bae293bdf | ||
|
|
d5d29c1c92 | ||
|
|
556ff24c5f | ||
|
|
f5d9af9b0b | ||
|
|
3ff0ed52b2 | ||
|
|
4ae6e9fdfd | ||
|
|
b9fc46fa41 | ||
|
|
133f3d24f5 | ||
|
|
241ebe50d2 | ||
|
|
7fda26f268 | ||
|
|
0729d50e47 | ||
|
|
f6cbb708b0 | ||
|
|
8b7692cf3b | ||
|
|
7523251098 | ||
|
|
5b5e6d83d9 | ||
|
|
db70272902 | ||
|
|
78068bfaa0 | ||
|
|
9f7cf463b9 | ||
|
|
9eca0d2fde | ||
|
|
45bb74d8f6 | ||
|
|
07103431a9 | ||
|
|
3588ce7bc5 | ||
|
|
557d4ae4c1 | ||
|
|
563d1dc3f1 | ||
|
|
5a8a41ad50 | ||
|
|
60be24c665 | ||
|
|
4a968cd34f | ||
|
|
0608c67767 | ||
|
|
bd1b06c9b4 | ||
|
|
98f8be45b7 | ||
|
|
641f20a4f6 | ||
|
|
7cbfa228ee | ||
|
|
1c82b7b0c3 | ||
|
|
b7368fa933 | ||
|
|
ecf163d4e7 | ||
|
|
1a72d3134c | ||
|
|
612cad5d9e | ||
|
|
9b1b5b3a4b | ||
|
|
6b1ffaff7a | ||
|
|
ffa24eac84 | ||
|
|
82eb69324d | ||
|
|
eaf61526fb | ||
|
|
47906d23f0 | ||
|
|
2fa05f8d61 | ||
|
|
6bee3f22da | ||
|
|
396ecf6570 | ||
|
|
f9ef5e8498 | ||
|
|
cf9cd503e5 | ||
|
|
0e00f708f1 | ||
|
|
a4abccdcef | ||
|
|
6ace710a65 | ||
|
|
23b99f83ce | ||
|
|
8bc2b4a122 | ||
|
|
2b3ec55850 | ||
|
|
9a203aab67 | ||
|
|
5cc1f62839 | ||
|
|
ad57dd3169 | ||
|
|
0635a344ae | ||
|
|
12dab61a67 | ||
|
|
7ae1a70a8d | ||
|
|
4afec6fb7f | ||
|
|
886475af7e | ||
|
|
4d89e997b3 | ||
|
|
93a6e03f70 | ||
|
|
851f4d4a87 | ||
|
|
f2fa326c0c | ||
|
|
d9b63a482c | ||
|
|
c585d80e3e | ||
|
|
9c8be72574 | ||
|
|
e6551e4590 | ||
|
|
7cdb73d74b | ||
|
|
3d0da059dc | ||
|
|
274536bf13 | ||
|
|
6df1574b8b | ||
|
|
490a696567 | ||
|
|
55fad1910a | ||
|
|
aff2a50cdd | ||
|
|
5bc676a042 | ||
|
|
f5b2a92e84 | ||
|
|
1fcd462660 | ||
|
|
152e3c6258 | ||
|
|
b6d2ba2019 | ||
|
|
53e2ce33b8 | ||
|
|
9493f4a872 | ||
|
|
a32ce4fbf6 | ||
|
|
89533706a8 | ||
|
|
deb7ed660c | ||
|
|
a520f0bfed | ||
|
|
c4fd139586 | ||
|
|
4c8ff0955d | ||
|
|
6592526109 | ||
|
|
3a86e659af | ||
|
|
b032accdfa | ||
|
|
272d36995b | ||
|
|
9302c60b47 | ||
|
|
76880e0de7 | ||
|
|
0ef800073b | ||
|
|
93f148fca3 | ||
|
|
5981887705 | ||
|
|
a4c1aee5ea | ||
|
|
f5ba6fa952 | ||
|
|
3ebd90565c | ||
|
|
b95ad701af | ||
|
|
efc68c078e | ||
|
|
0e6012ad45 | ||
|
|
113533ad61 | ||
|
|
ac9075a82a | ||
|
|
998739a7dc | ||
|
|
8afc9f9a09 | ||
|
|
7a63cfd717 | ||
|
|
7ea6157b67 | ||
|
|
553c53e7e8 |
@@ -1,3 +1,10 @@
|
||||
# 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.
|
||||
sudo: false
|
||||
|
||||
language: node_js
|
||||
node_js:
|
||||
# make sure we work with a range of node versions.
|
||||
@@ -16,6 +23,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.
|
||||
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,3 +1,20 @@
|
||||
Changes in [0.11.4](https://github.com/vector-im/riot-web/releases/tag/v0.11.4) (2017-06-22)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.11.3...v0.11.4)
|
||||
|
||||
* Update matrix-js-sdk and react-sdk to fix a regression where the
|
||||
background indexedb worker was disabled, failures to open indexeddb
|
||||
causing the app to fail to start, a race when starting that could break
|
||||
switching to rooms, and the inability to invite users with mixed case
|
||||
usernames.
|
||||
|
||||
Changes in [0.11.3](https://github.com/vector-im/riot-web/releases/tag/v0.11.3) (2017-06-20)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.11.2...v0.11.3)
|
||||
|
||||
* Update to matrix-react-sdk 0.9.6 to fix infinite spinner bugs
|
||||
and some parts of the app that had missed translation.
|
||||
|
||||
Changes in [0.11.2](https://github.com/vector-im/riot-web/releases/tag/v0.11.2) (2017-06-19)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.11.2-rc.2...v0.11.2)
|
||||
|
||||
38
README.md
38
README.md
@@ -81,7 +81,7 @@ to build.
|
||||
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
|
||||
up a dev 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.
|
||||
@@ -253,7 +253,6 @@ Finally, build and start Riot itself:
|
||||
1. `rm -r node_modules/matrix-react-sdk; ln -s ../../matrix-react-sdk node_modules/`
|
||||
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
|
||||
@@ -282,19 +281,34 @@ 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
|
||||
-----------------
|
||||
|
||||
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`
|
||||
|
||||
The above will run the tests under Chrome in a `headless` mode.
|
||||
|
||||
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.
|
||||
|
||||
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)
|
||||
|
||||
|
||||
Head to the [translating doc](docs/translating.md)
|
||||
|
||||
Adding Strings to the translations (Developer Guide)
|
||||
====================================================
|
||||
|
||||
Head to the [translating dev doc](docs/translating-dev.md)
|
||||
|
||||
Triaging issues
|
||||
===============
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "riot-web",
|
||||
"productName": "Riot",
|
||||
"main": "src/electron-main.js",
|
||||
"version": "0.11.2",
|
||||
"version": "0.11.4",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "Vector Creations Ltd.",
|
||||
"dependencies": {
|
||||
|
||||
@@ -29,6 +29,7 @@ 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');
|
||||
|
||||
@@ -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,8 +61,6 @@ 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);
|
||||
@@ -233,7 +172,7 @@ electron.app.on('ready', () => {
|
||||
|
||||
if (vectorConfig.update_base_url) {
|
||||
console.log(`Starting auto update with base URL: ${vectorConfig.update_base_url}`);
|
||||
startAutoUpdate(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 +185,7 @@ electron.app.on('ready', () => {
|
||||
defaultHeight: 768,
|
||||
});
|
||||
|
||||
mainWindow = new electron.BrowserWindow({
|
||||
mainWindow = global.mainWindow = new electron.BrowserWindow({
|
||||
icon: iconPath,
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
@@ -264,7 +203,7 @@ electron.app.on('ready', () => {
|
||||
mainWindow.hide();
|
||||
|
||||
// Create trayIcon icon
|
||||
tray.create(mainWindow, {
|
||||
tray.create({
|
||||
icon_path: iconPath,
|
||||
brand: vectorConfig.brand || 'Riot',
|
||||
});
|
||||
@@ -276,10 +215,10 @@ electron.app.on('ready', () => {
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -302,7 +241,10 @@ electron.app.on('activate', () => {
|
||||
});
|
||||
|
||||
electron.app.on('before-quit', () => {
|
||||
appQuitting = true;
|
||||
global.appQuitting = true;
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('before-quit');
|
||||
}
|
||||
});
|
||||
|
||||
// Set the App User Model ID to match what the squirrel
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
};
|
||||
|
||||
84
electron_app/src/updater.js
Normal file
84
electron_app/src/updater.js
Normal 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);
|
||||
});
|
||||
@@ -113,8 +113,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,
|
||||
|
||||
11
package.json
11
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "riot-web",
|
||||
"productName": "Riot",
|
||||
"main": "electron_app/src/electron-main.js",
|
||||
"version": "0.11.2",
|
||||
"version": "0.11.4",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "Vector Creations Ltd.",
|
||||
"repository": {
|
||||
@@ -48,7 +48,7 @@
|
||||
"lintall": "eslint src/ test/",
|
||||
"clean": "rimraf lib webapp electron_app/dist",
|
||||
"prepublish": "npm run build:compile",
|
||||
"test": "karma start --single-run=true --autoWatch=false --browsers PhantomJS --colors=false",
|
||||
"test": "karma start --single-run=true --autoWatch=false --browsers ChromeHeadless --colors=false",
|
||||
"test-multi": "karma start"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -65,8 +65,8 @@
|
||||
"gfm.css": "^1.1.1",
|
||||
"highlight.js": "^9.0.0",
|
||||
"linkifyjs": "^2.1.3",
|
||||
"matrix-js-sdk": "0.7.12",
|
||||
"matrix-react-sdk": "0.9.5",
|
||||
"matrix-js-sdk": "0.7.13",
|
||||
"matrix-react-sdk": "0.9.7",
|
||||
"modernizr": "^3.1.0",
|
||||
"pako": "^1.0.5",
|
||||
"q": "^1.4.1",
|
||||
@@ -119,13 +119,12 @@
|
||||
"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",
|
||||
"matrix-mock-request": "^1.0.0",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mocha": "^2.4.5",
|
||||
"parallelshell": "^1.2.0",
|
||||
"phantomjs-prebuilt": "^2.1.7",
|
||||
"postcss-extend": "^1.0.5",
|
||||
"postcss-import": "^9.0.0",
|
||||
"postcss-loader": "^1.2.2",
|
||||
|
||||
@@ -70,9 +70,11 @@ module.exports = React.createClass({
|
||||
|
||||
this.setState({protocolsLoading: true});
|
||||
MatrixClientPeg.get().getThirdpartyProtocols().done((response) => {
|
||||
console.log('got 3p networks: '+response);
|
||||
this.protocols = response;
|
||||
this.setState({protocolsLoading: false});
|
||||
}, (err) => {
|
||||
console.warn(`error loading thirdparty protocols: ${err}`);
|
||||
this.setState({protocolsLoading: false});
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
// Guests currently aren't allowed to use this API, so
|
||||
@@ -497,6 +499,9 @@ module.exports = React.createClass({
|
||||
);
|
||||
}
|
||||
|
||||
console.log('renderiong with 3p networks: '+ this.protocols);
|
||||
|
||||
|
||||
let content;
|
||||
if (this.state.loading) {
|
||||
content = <div className="mx_RoomDirectory">
|
||||
|
||||
@@ -67,7 +67,7 @@ module.exports = React.createClass({
|
||||
|
||||
onResendClick: function() {
|
||||
Resend.resend(this.props.mxEvent);
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
this.closeMenu();
|
||||
},
|
||||
|
||||
onViewSourceClick: function() {
|
||||
@@ -75,7 +75,7 @@ module.exports = React.createClass({
|
||||
Modal.createDialog(ViewSource, {
|
||||
content: this.props.mxEvent.event,
|
||||
}, 'mx_Dialog_viewsource');
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
this.closeMenu();
|
||||
},
|
||||
|
||||
onViewClearSourceClick: function() {
|
||||
@@ -84,7 +84,7 @@ module.exports = React.createClass({
|
||||
// FIXME: _clearEvent is private
|
||||
content: this.props.mxEvent._clearEvent,
|
||||
}, 'mx_Dialog_viewsource');
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
this.closeMenu();
|
||||
},
|
||||
|
||||
onRedactClick: function() {
|
||||
@@ -106,12 +106,12 @@ module.exports = React.createClass({
|
||||
}).done();
|
||||
},
|
||||
}, 'mx_Dialog_confirmredact');
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
this.closeMenu();
|
||||
},
|
||||
|
||||
onCancelSendClick: function() {
|
||||
Resend.removeFromQueue(this.props.mxEvent);
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
this.closeMenu();
|
||||
},
|
||||
|
||||
onForwardClick: function() {
|
||||
@@ -130,7 +130,7 @@ module.exports = React.createClass({
|
||||
if (this.props.eventTileOps) {
|
||||
this.props.eventTileOps.unhideWidget();
|
||||
}
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
this.closeMenu();
|
||||
},
|
||||
|
||||
onQuoteClick: function() {
|
||||
@@ -139,6 +139,7 @@ module.exports = React.createClass({
|
||||
action: 'quote',
|
||||
event: this.props.mxEvent,
|
||||
});
|
||||
this.closeMenu();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
||||
@@ -158,7 +158,7 @@ module.exports = React.createClass({
|
||||
var eventRedact;
|
||||
if(showEventMeta) {
|
||||
eventRedact = (<div className="mx_ImageView_button" onClick={this.onRedactClick}>
|
||||
{ _t('Redact') }
|
||||
{ _t('Remove') }
|
||||
</div>);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ module.exports = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_MatrixToolbar">
|
||||
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/>
|
||||
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="Warning"/>
|
||||
<div className="mx_MatrixToolbar_content">
|
||||
{ _t('You are not receiving desktop notifications') } <a className="mx_MatrixToolbar_link" onClick={ this.onClick }> { _t('Enable them now') }</a>
|
||||
</div>
|
||||
|
||||
@@ -96,7 +96,7 @@ export default React.createClass({
|
||||
}
|
||||
return (
|
||||
<div className="mx_MatrixToolbar">
|
||||
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/>
|
||||
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="Warning"/>
|
||||
<div className="mx_MatrixToolbar_content">
|
||||
{_t("A new version of Riot is available.")}
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ import React from 'react';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import Modal from 'matrix-react-sdk/lib/Modal';
|
||||
import dis from 'matrix-react-sdk/lib/dispatcher';
|
||||
import { _t, _tJsx } from 'matrix-react-sdk/lib/languageHandler';
|
||||
|
||||
export default React.createClass({
|
||||
onUpdateClicked: function() {
|
||||
@@ -33,12 +34,11 @@ export default React.createClass({
|
||||
dis.dispatch({
|
||||
action: 'password_changed',
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
const toolbarClasses = "mx_MatrixToolbar mx_MatrixToolbar_clickable";
|
||||
return (
|
||||
<div className={toolbarClasses} onClick={this.onUpdateClicked}>
|
||||
@@ -49,12 +49,16 @@ export default React.createClass({
|
||||
alt="Warning"
|
||||
/>
|
||||
<div className="mx_MatrixToolbar_content">
|
||||
To return to your account in future you need to <u>set a password</u>
|
||||
{ _tJsx(
|
||||
"To return to your account in future you need to <u>set a password</u>",
|
||||
/<u>(.*?)<\/u>/,
|
||||
(sub) => { return <u>{ sub }</u>; },
|
||||
) }
|
||||
</div>
|
||||
<button className="mx_MatrixToolbar_action">
|
||||
Set Password
|
||||
{ _t("Set Password") }
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
85
src/components/views/globals/UpdateCheckBar.js
Normal file
85
src/components/views/globals/UpdateCheckBar.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
|
||||
import {updateCheckStatusEnum} from '../../../vector/platform/VectorBasePlatform';
|
||||
import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton';
|
||||
|
||||
const doneStatuses = [
|
||||
updateCheckStatusEnum.ERROR,
|
||||
updateCheckStatusEnum.NOTAVAILABLE,
|
||||
];
|
||||
|
||||
export default React.createClass({
|
||||
propTypes: {
|
||||
status: React.PropTypes.oneOf(Object.values(updateCheckStatusEnum)).isRequired,
|
||||
// Currently for error detail but will be usable for download progress
|
||||
// once that is a thing that squirrel passes through electron.
|
||||
detail: React.PropTypes.string,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
detail: '',
|
||||
}
|
||||
},
|
||||
|
||||
getStatusText: function() {
|
||||
switch(this.props.status) {
|
||||
case updateCheckStatusEnum.ERROR:
|
||||
return _t('Error encountered (%(errorDetail)s).', { errorDetail: this.props.detail });
|
||||
case updateCheckStatusEnum.CHECKING:
|
||||
return _t('Checking for an update...');
|
||||
case updateCheckStatusEnum.NOTAVAILABLE:
|
||||
return _t('No update available.');
|
||||
case updateCheckStatusEnum.DOWNLOADING:
|
||||
return _t('Downloading update...');
|
||||
}
|
||||
}
|
||||
,
|
||||
|
||||
hideToolbar: function() {
|
||||
PlatformPeg.get().stopUpdateCheck();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const message = this.getStatusText();
|
||||
const warning = _t('Warning');
|
||||
|
||||
let image;
|
||||
if (doneStatuses.includes(this.props.status)) {
|
||||
image = <img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt={warning}/>;
|
||||
} else {
|
||||
image = <img className="mx_MatrixToolbar_warning" src="img/spinner.gif" width="24" height="23" alt={message}/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_MatrixToolbar">
|
||||
{image}
|
||||
<div className="mx_MatrixToolbar_content">
|
||||
{message}
|
||||
</div>
|
||||
<AccessibleButton className="mx_MatrixToolbar_close" onClick={this.hideToolbar}>
|
||||
<img src="img/cancel.svg" width="18" height="18" />
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -86,7 +86,7 @@
|
||||
"Cancel Sending": "Senden abbrechen",
|
||||
"Close": "Schließen",
|
||||
"Delete the room alias %(alias)s and remove %(name)s from the directory?": "Soll der Raum-Alias %(alias)s gelöscht und der %(name)s aus dem Verzeichnis entfernt werden?",
|
||||
"Download this file": "Diese Datei herunterladen",
|
||||
"Download this file": "Datei herunterladen",
|
||||
"Failed to add tag %(tagName)s to room": "Das Hinzufügen des Tags %(tagName)s für den Raum ist fehlgeschlagen",
|
||||
"Failed to forget room %(errCode)s": "Das Entfernen des Raums ist fehlgeschlagen %(errCode)s",
|
||||
"Failed to remove tag %(tagName)s from room": "Das Entfernen des Tags %(tagName)s für den Raum ist fehlgeschlagen",
|
||||
@@ -95,14 +95,14 @@
|
||||
"Mute": "Stummschalten",
|
||||
"Permalink": "Permanenter Link",
|
||||
"Quote": "Zitat",
|
||||
"Redact": "Redaktionell entfernen",
|
||||
"Redact": "Löschen",
|
||||
"Remove %(name)s from the directory?": "Soll der Raum %(name)s aus dem Verzeichnis entfernt werden?",
|
||||
"remove %(name)s from the directory.": "entferne %(name)s aus dem Verzeichnis.",
|
||||
"Resend": "Erneut senden",
|
||||
"Source URL": "Quell-URL",
|
||||
"Unable to look up room ID from server": "Es ist nicht möglich, die Raum-ID auf dem Server nachzuschlagen",
|
||||
"Unhide Preview": "Vorschau wieder anzeigen",
|
||||
"Uploaded on %(date)s by %(user)s": "Hochgeladen am %(date)s durch %(user)s",
|
||||
"Uploaded on %(date)s by %(user)s": "Hochgeladen am %(date)s von %(user)s",
|
||||
"View Decrypted Source": "Entschlüsselten Quellcode ansehen",
|
||||
"View Source": "Quellcode ansehen",
|
||||
"You cannot delete this image. (%(code)s)": "Das Bild kann nicht gelöscht werden. (%(code)s)",
|
||||
@@ -207,5 +207,7 @@
|
||||
"General discussion about Matrix and Riot": "Allgemeine Diskussion über Matrix und Riot",
|
||||
"(HTTP status %(httpStatus)s)": "(HTTP-Status %(httpStatus)s)",
|
||||
"You have successfully set a password and an email address!": "Du hast erfolgreich ein Passwort und eine E-Mail-Adresse gesetzt!",
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Denk daran, dass du in den Benutzereinstellungen jederzeit eine E-Mail-Adresse setzen kannst."
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Denk daran, dass du in den Benutzereinstellungen jederzeit eine E-Mail-Adresse setzen kannst.",
|
||||
"To return to your account in future you need to <u>set a password</u>": "Um in Zukunft zu deinem Konto zurückkehren zu können, musst du <u>ein Passwort setzen</u>",
|
||||
"Set Password": "Setze ein Passwort"
|
||||
}
|
||||
|
||||
@@ -198,5 +198,7 @@
|
||||
"Failed to set Direct Message status of room": "Δεν ήταν δυνατός ο ορισμός της κατάστασης Direct Message του δωματίου",
|
||||
"Support for those using, running and writing other bridges": "Υποστήριξη για τους χρήστες που χρησιμοποιούν ή αναπτύσσουν εφαρμογές ενσωμάτωσης για το Matrix",
|
||||
"You have successfully set a password and an email address!": "Ο κωδικός πρόσβασης και η διεύθυνση ηλεκτρονικής αλληλογραφίας ορίστηκαν επιτυχώς!",
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Να θυμάστε ότι μπορείτε πάντα να ορίσετε μια διεύθυνση ηλεκτρονικής αλληλογραφίας στις ρυθμίσεις χρήστη αν αλλάξετε γνώμη."
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Να θυμάστε ότι μπορείτε πάντα να ορίσετε μια διεύθυνση ηλεκτρονικής αλληλογραφίας στις ρυθμίσεις χρήστη αν αλλάξετε γνώμη.",
|
||||
"To return to your account in future you need to <u>set a password</u>": "Για να επιστρέψετε στον λογαριασμό σας μελλοντικα πρέπει να ορίσετε έναν <u>κωδικό πρόσβασης</u>",
|
||||
"Set Password": "Ορισμός κωδικού πρόσβασης"
|
||||
}
|
||||
|
||||
@@ -98,7 +98,6 @@
|
||||
"Please Register": "Please Register",
|
||||
"powered by Matrix": "powered by Matrix",
|
||||
"Quote": "Quote",
|
||||
"Redact": "Redact",
|
||||
"Reject": "Reject",
|
||||
"Remove %(name)s from the directory?": "Remove %(name)s from the directory?",
|
||||
"Remove": "Remove",
|
||||
@@ -160,6 +159,11 @@
|
||||
"Today": "Today",
|
||||
"Yesterday": "Yesterday",
|
||||
"OK": "OK",
|
||||
"Warning": "Warning",
|
||||
"Checking for an update...": "Checking for an update...",
|
||||
"Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).",
|
||||
"No update available.": "No update available.",
|
||||
"Downloading update...": "Downloading update...",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "You need to be using HTTPS to place a screen-sharing call.",
|
||||
"Welcome page": "Welcome page",
|
||||
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!",
|
||||
@@ -198,5 +202,7 @@
|
||||
"Please set a password!": "Please set a password!",
|
||||
"This will allow you to return to your account after signing out, and sign in on other devices.": "This will allow you to return to your account after signing out, and sign in on other devices.",
|
||||
"You have successfully set a password and an email address!": "You have successfully set a password and an email address!",
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Remember, you can always set an email address in user settings if you change your mind."
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Remember, you can always set an email address in user settings if you change your mind.",
|
||||
"To return to your account in future you need to <u>set a password</u>": "To return to your account in future you need to <u>set a password</u>",
|
||||
"Set Password": "Set Password"
|
||||
}
|
||||
|
||||
@@ -96,7 +96,6 @@
|
||||
"Please Register": "Please Register",
|
||||
"powered by Matrix": "powered by Matrix",
|
||||
"Quote": "Quote",
|
||||
"Redact": "Redact",
|
||||
"Reject": "Reject",
|
||||
"Remove %(name)s from the directory?": "Remove %(name)s from the directory?",
|
||||
"Remove": "Remove",
|
||||
|
||||
@@ -198,5 +198,11 @@
|
||||
"Lots of rooms already exist in Matrix, linked to existing networks (Slack, IRC, Gitter etc) or independent. Check out the directory!": "Muchas salas ya están disponibles en Matrix, enlazadas a redes existentes (Slack, IRC, Gitter, etc) o independientes. ¡Revisa el directorio!",
|
||||
"You can now return to your account after signing out, and sign in on other devices.": "Ahora puedes regresar a tu cuenta después de cerrar tu sesión, e iniciar sesión en otros dispositivos.",
|
||||
"Please set a password!": "¡Por favor establece una contraseña!",
|
||||
"This will allow you to return to your account after signing out, and sign in on other devices.": "Esto le permitirá regresar a su cuenta después de cerrar sesión, así como iniciar sesión en otros dispositivos."
|
||||
"This will allow you to return to your account after signing out, and sign in on other devices.": "Esto le permitirá regresar a su cuenta después de cerrar sesión, así como iniciar sesión en otros dispositivos.",
|
||||
"Warning": "Advertencia",
|
||||
"Checking for an update...": "Comprobando actualizaciones...",
|
||||
"No update available.": "No hay actualizaciones disponibles.",
|
||||
"Downloading update...": "Descargando actualizaciones...",
|
||||
"To return to your account in future you need to <u>set a password</u>": "Para regresar a su cuenta en el futuro Ud. debe <u>establecer una contraseña</u>",
|
||||
"Set Password": "Establezca la contraseña"
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
"Messages containing <span>keywords</span>": "Az üzenet <span>kulcsszavakat</span> tartalmaz",
|
||||
"%(appName)s via %(browserName)s on %(osName)s": "%(appName)s alkalmazás %(browserName)s böngészőn %(osName)s rendszeren",
|
||||
"A new version of Riot is available.": "Új verzió érhető el a Riot-ból.",
|
||||
"All Rooms": "Minden szoba",
|
||||
"All Rooms": "Minden szobában",
|
||||
"Cancel": "Mégse",
|
||||
"Changelog": "Változások",
|
||||
"Collecting app version information": "Alkalmazás verzió információk összegyűjtése",
|
||||
@@ -148,7 +148,7 @@
|
||||
"Search…": "Keresés…",
|
||||
"Send": "Küld",
|
||||
"Send logs": "Naplók elküldése",
|
||||
"This Room": "Ez a szoba",
|
||||
"This Room": "Ebben a szobában",
|
||||
"Unavailable": "Elérhetetlen",
|
||||
"Unknown device": "Ismeretlen eszköz",
|
||||
"Update": "Frissítés",
|
||||
@@ -198,5 +198,7 @@
|
||||
"This will allow you to return to your account after signing out, and sign in on other devices.": "Ezzel visszatérhetsz kijelentkezés után a fiókodhoz és más eszközökkel is be tudsz jelentkezni.",
|
||||
"(HTTP status %(httpStatus)s)": "(HTTP állapot %(httpStatus)s)",
|
||||
"You have successfully set a password and an email address!": "Sikeresen beállítottad a jelszavad és e-mail címed!",
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Ha meggondolod magad, bármikor beállíthatod az e-mail címed a felhasználói beállításoknál."
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Ha meggondolod magad, bármikor beállíthatod az e-mail címed a felhasználói beállításoknál.",
|
||||
"To return to your account in future you need to <u>set a password</u>": "A fiókba való visszalépéshez <u>jelszót</u> kell beállítanod",
|
||||
"Set Password": "Jelszó beállítása"
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"Directory": "목록",
|
||||
"Dismiss": "없애기",
|
||||
"Download this file": "이 파일 받기",
|
||||
"Enable desktop notifications": "데스크탑에서 알림 받기",
|
||||
"Enable desktop notifications": "컴퓨터에서 알림 받기",
|
||||
"Enable email notifications": "이메일로 알림 받기",
|
||||
"Enable notifications for this account": "이 계정의 알림 받기",
|
||||
"Error": "오류",
|
||||
@@ -62,7 +62,7 @@
|
||||
"Remove from Directory": "목록에서 지우기",
|
||||
"Report a bug": "오류 보고하기",
|
||||
"Resend": "다시 보내기",
|
||||
"Riot Desktop on %(platformName)s": "%(platformName)s에서 라이엇 데스크탑",
|
||||
"Riot Desktop on %(platformName)s": "%(platformName)s에서 라이엇 컴퓨터판",
|
||||
"Riot is not supported on mobile web. Install the app?": "라이엇은 모바일 사이트를 지원하지 않아요. 앱을 설치하시겠어요?",
|
||||
"Room directory": "방 목록",
|
||||
"Room not found": "방을 찾지 못했어요",
|
||||
@@ -87,7 +87,7 @@
|
||||
"Waiting for response from server": "서버에서 응답을 기다리는 중",
|
||||
"You cannot delete this image. (%(code)s)": "이 사진을 지우실 수 없어요. (%(code)s)",
|
||||
"You cannot delete this message. (%(code)s)": "이 메시지를 지우실 수 없어요. (%(code)s)",
|
||||
"You are not receiving desktop notifications": "데스크탑 알림을 받지 않고 있어요",
|
||||
"You are not receiving desktop notifications": "컴퓨터 알림을 받지 않고 있어요",
|
||||
"Sunday": "일요일",
|
||||
"Monday": "월요일",
|
||||
"Tuesday": "화요일",
|
||||
@@ -171,7 +171,7 @@
|
||||
"Get started with some tips from Riot Bot!": "라이엇 봇에게 조언을 받고 시작하세요!",
|
||||
"General discussion about Matrix and Riot": "매트릭스와 라이엇에 대한 일반 논의",
|
||||
"Discussion of all things Matrix!": "매트릭스의 모든 것에 대한 토론!",
|
||||
"Riot/Web & Desktop chat": "라이엇/웹 & 데스크탑 대화",
|
||||
"Riot/Web & Desktop chat": "라이엇/웹 & 컴퓨터 이야기",
|
||||
"Riot/iOS & matrix-ios-sdk chat": "라이엇/IOS & matrix-ios-sdk 대화",
|
||||
"Riot/Android & matrix-android-sdk chat": "매트릭스/안드로이드 & matrix-ios-sdk 대화",
|
||||
"Matrix technical discussions": "매트릭스 기술 논의",
|
||||
@@ -198,5 +198,7 @@
|
||||
"You have successfully set a password and an email address!": "비밀번호와 이메일 주소를 설정했어요!",
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "잊지마세요, 마음이 바뀌면 언제라도 사용자 설정에서 이메일 주소를 바꾸실 수 있다는 걸요.",
|
||||
"You are Rioting as a guest. <a>Register</a> or <a>sign in</a> to access more rooms and features!": "손님으로 라이엇에 들어오셨네요. <a>계정을 등록하거나</a> <a>로그인하시고</a> 더 많은 방과 기능을 즐기세요!",
|
||||
"You might have configured them in a client other than Riot. You cannot tune them in Riot but they still apply": "라이엇이 아닌 다른 클라이언트에서 구성하셨을 수도 있어요. 라이엇에서 조정할 수는 없지만 여전히 적용되있을 거에요"
|
||||
"You might have configured them in a client other than Riot. You cannot tune them in Riot but they still apply": "라이엇이 아닌 다른 클라이언트에서 구성하셨을 수도 있어요. 라이엇에서 조정할 수는 없지만 여전히 적용되있을 거에요",
|
||||
"To return to your account in future you need to <u>set a password</u>": "나중에 계정으로 돌아가려면 <u>비밀번호 설정</u>을 해야만 해요",
|
||||
"Set Password": "비밀번호 설정"
|
||||
}
|
||||
|
||||
@@ -198,5 +198,7 @@
|
||||
"Please set a password!": "Proszę, ustaw hasło!",
|
||||
"This will allow you to return to your account after signing out, and sign in on other devices.": "To pozwoli Ci powrócić do Twojego konta po wylogowaniu i ponownym zalogowaniu się na innych urządzeniach.",
|
||||
"You have successfully set a password and an email address!": "Z powodzeniem ustawiono hasło i adres e-mail dla Twojego konta!",
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Pamiętaj, że zawsze możesz zmienić swój e-mail lub hasło w panelu ustawień użytkownika."
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Pamiętaj, że zawsze możesz zmienić swój e-mail lub hasło w panelu ustawień użytkownika.",
|
||||
"To return to your account in future you need to <u>set a password</u>": "Aby wrócić do swojego konta w przyszłości musisz <u> ustawić hasło </u>",
|
||||
"Set Password": "Ustaw hasło"
|
||||
}
|
||||
|
||||
@@ -199,5 +199,9 @@
|
||||
"Please set a password!": "Por favor, defina uma senha!",
|
||||
"This will allow you to return to your account after signing out, and sign in on other devices.": "Isso permitirá que você possa retornar à sua conta após fazer logout, e também fazer login em outros dispositivos.",
|
||||
"(HTTP status %(httpStatus)s)": "(Status HTTP %(httpStatus)s)",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Chat descentralizado, criptografado e colaborativo impulsionado por [matrix]"
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Chat descentralizado, criptografado e colaborativo impulsionado por [matrix]",
|
||||
"You have successfully set a password and an email address!": "Você definiu uma senha e um endereço de e-mail com sucesso!",
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Lembre-se: você pode sempre definir um endereço de e-mail nas configurações de usuário, se mudar de ideia.",
|
||||
"To return to your account in future you need to <u>set a password</u>": "Para poder, futuramente, retornar à sua conta, você precisa <u>definir uma senha</u>",
|
||||
"Set Password": "Definir senha"
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"and remove": "и удалить",
|
||||
"Can't update user notification settings": "Не возможно обновить пользовательские настройки оповещения",
|
||||
"Create new room": "Создать новую комнату",
|
||||
"Couldn't find a matching Matrix room": "Не возможно найти подходящую Матриксу комнату",
|
||||
"Custom Server Options": "Расширенные настройки сервера",
|
||||
"Couldn't find a matching Matrix room": "Не возможно найти подходящую комнату Matrix",
|
||||
"Custom Server Options": "Собственные настройки сервера",
|
||||
"delete the alias.": "удалить псевдоним.",
|
||||
"Delete the room alias": "Удалить привязку комнаты",
|
||||
"Direct Chat": "Приватный чат",
|
||||
@@ -30,16 +30,16 @@
|
||||
"Failed to get public room list": "Не удалось получить список открытых комнат",
|
||||
"Failed to join the room": "Не удалось войти в комнату",
|
||||
"Failed to remove tag ": "Не удалось удалить тег ",
|
||||
"Failed to set Direct Message status of room": "Не удалось задать статус комнаты Персональное Сообщение",
|
||||
"Failed to set Direct Message status of room": "Не удалось установить статус прямого сообщения в комнате",
|
||||
"Favourite": "Избранное",
|
||||
"Fetching third party location failed": "Не удалось получить местоположение",
|
||||
"Fetching third party location failed": "Не удалось получить другую локацию",
|
||||
"Files": "Файлы",
|
||||
"Filter room names": "Фильтр по назв. комнаты",
|
||||
"Filter room names": "Фильтр по названию комнат",
|
||||
"Forget": "Удалить",
|
||||
"from the directory": "из каталога",
|
||||
" from room": " из комнаты",
|
||||
"Guests can join": "Гость может присоединиться",
|
||||
"Guest users can't invite users. Please register to invite.": "Гость не может приглашать пользователей. Зарегистрируйтесь для приглашений.",
|
||||
"Guest users can't invite users. Please register to invite.": "Гость не может приглашать пользователей. Пожалуйста зарегистрируйтесь.",
|
||||
"Invite to this room": "Пригласить",
|
||||
"Keywords": "Ключевые слова",
|
||||
"Leave": "Покинуть",
|
||||
@@ -51,12 +51,12 @@
|
||||
"Notifications": "Уведомления",
|
||||
"Notifications on the following keywords follow rules which can’t be displayed here:": "Уведомления по следующим ключевым словам соответствуют правилам, которые нельзя отобразить здесь:",
|
||||
"Notify for all other messages/rooms": "Уведомить обо всех других сообщениях/комнатах",
|
||||
"Notify me for anything else": "Уведомить меня обо всем кроме",
|
||||
"Notify me for anything else": "Уведомить меня о чем либо еще",
|
||||
"Off": "Выключить",
|
||||
"On": "Включить",
|
||||
"Operation failed": "Действие не удалось",
|
||||
"Please Register": "Пожалуйста, зарегистрируйтесь",
|
||||
"powered by Matrix": "управляемый с Matrix",
|
||||
"powered by Matrix": "Основано на Matrix",
|
||||
"Reject": "Отклонить",
|
||||
"Remove": "Удалить",
|
||||
"remove": "удалить",
|
||||
@@ -86,14 +86,14 @@
|
||||
"Close": "Закрыть",
|
||||
"Download this file": "Скачать этот файл",
|
||||
"Drop here %(toAction)s": "Вставить сюда: %(toAction)s",
|
||||
"Delete the room alias %(alias)s and remove %(name)s from the directory?": "Удалить псевдоним комнаты %(alias)s и очистить %(name)s из каталога?",
|
||||
"Delete the room alias %(alias)s and remove %(name)s from the directory?": "Удалить псевдоним комнаты %(alias)s и удалить %(name)s из каталога?",
|
||||
"Failed to add tag %(tagName)s to room": "Не удалось добавить тег %(tagName)s в комнату",
|
||||
"Failed to forget room %(errCode)s": "Не удалось удалить комнату %(errCode)s",
|
||||
"Failed to remove tag %(tagName)s from room": "Не удалось убрать пометку %(tagName)s из комнаты",
|
||||
"Failed to set direct chat tag": "Не удалось пометить прямую беседу",
|
||||
"Failed to set direct chat tag": "Не удалось установить тег прямого чата",
|
||||
"Unhide Preview": "Показать анонс",
|
||||
"Uploaded on %(date)s by %(user)s": "Загружено %(date)s %(user)s",
|
||||
"View Decrypted Source": "Просмотр зашифрованного источника",
|
||||
"View Decrypted Source": "Просмотр расшифрованного источника",
|
||||
"View Source": "Просмотр источника",
|
||||
"You cannot delete this image. (%(code)s)": "Вы не можете удалить это изображение. (%(code)s)",
|
||||
"You cannot delete this message. (%(code)s)": "Вы не можете удалить это сообщение. (%(code)s)",
|
||||
@@ -113,7 +113,7 @@
|
||||
"Redact": "Удалить",
|
||||
"Remove %(name)s from the directory?": "Удалить %(name)s из каталога?",
|
||||
"remove %(name)s from the directory.": "удалить %(name)s из каталога.",
|
||||
"Resend": "Переслать снова",
|
||||
"Resend": "Отправить снова",
|
||||
"Source URL": "Исходный URL",
|
||||
"Welcome page": "Домашняя страница",
|
||||
"Advanced notification settings": "Настройки уведомлений",
|
||||
@@ -131,31 +131,31 @@
|
||||
"Cancel": "Отмена",
|
||||
"Changelog": "История изменений",
|
||||
"Collapse panel": "Свернуть панель",
|
||||
"Collecting app version information": "Сбор информации о версиях программы",
|
||||
"Collecting app version information": "Сбор информации о версии приложения",
|
||||
"Collecting logs": "Сбор протоколов",
|
||||
"%(appName)s via %(browserName)s on %(osName)s": "%(appName)s с %(browserName)s на %(osName)s",
|
||||
"<a href=\"http://apple.com/safari\">Safari</a> and <a href=\"http://opera.com\">Opera</a> work too.": "<a href=\"http://apple.com/safari\">Safari</a> и <a href=\"http://opera.com\">Opera</a> работают тоже.",
|
||||
"Describe your problem here.": "Опиши здесь свою проблему.",
|
||||
"Expand panel": "Открыть панель",
|
||||
"Expand panel": "Развернуть панель",
|
||||
"Failed to send report: ": "Не удалось отослать отчет: ",
|
||||
"Forward Message": "Переслать сообщение дальше",
|
||||
"Forward Message": "Переслать сообщение",
|
||||
"Hide panel": "Скрыть панель",
|
||||
"I understand the risks and wish to continue": "Я понимаю риск и хочу продолжать",
|
||||
"In order to diagnose problems, logs from this client will be sent with this bug report. If you would prefer to only send the text above, please untick:": "Что бы выявить проблему, будет отослан журнал этого клиента с сообщением о ошибке. Если Вы только верхний текст отослать хотите, отключите следующее:",
|
||||
"Loading bug report module": "Загрузи Модуль ошибок",
|
||||
"I understand the risks and wish to continue": "Я понимаю риск и хочу продолжить",
|
||||
"In order to diagnose problems, logs from this client will be sent with this bug report. If you would prefer to only send the text above, please untick:": "Чтобы диагностировать проблемы, логи этого клиента будут отправляться с этим сообщением об ошибке. Если вы предпочитаете отправить только текст выше, пожалуйста, отключите:",
|
||||
"Loading bug report module": "Загрузка Модуля отчета об ошибках",
|
||||
"Messages containing <span>keywords</span>": "Сообщения, которые содержат определенные <span>ключевые слова</span>",
|
||||
"Please describe the bug. What did you do? What did you expect to happen? What actually happened?": "Пожалуйста опишите (на Английском) ошибку. Что Вы делали? Что Вы ожидали получить? Что произошло?",
|
||||
"Please describe the bug and/or send logs.": "Пожалуйста опишите ошибку и/или перешлите протоколы.",
|
||||
"Please install <a href=\"https://www.google.com/chrome\">Chrome</a> or <a href=\"https://getfirefox.com\">Firefox</a> for the best experience.": "Пожалуйста установите <a href=\"https://www.google.com/chrome\">Chrome</a> или <a href=\"https://getfirefox.com\">Firefox</a> для лучшего результата.",
|
||||
"Report a bug": "Отчет о ошибке",
|
||||
"Please describe the bug and/or send logs.": "Пожалуйста опишите ошибку и/или перешлите логи.",
|
||||
"Please install <a href=\"https://www.google.com/chrome\">Chrome</a> or <a href=\"https://getfirefox.com\">Firefox</a> for the best experience.": "Пожалуйста установите <a href=\"https://www.google.com/chrome\">Chrome</a> или <a href=\"https://getfirefox.com\">Firefox</a> для корректной работы чата.",
|
||||
"Report a bug": "Отчет об ошибке",
|
||||
"Riot Desktop on %(platformName)s": "Riot Desktop на %(platformName)s",
|
||||
"Riot is not supported on mobile web. Install the app?": "Riot не будет на мобильном Интернете работать. Программу инсталлировать?",
|
||||
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot использует некоторые расширенные функции интернет-проводника - некоторые из них отсутствуют или экспериментальные в этом проводнике.",
|
||||
"Riot is not supported on mobile web. Install the app?": "Riot может некорректно работать в мобильном браузере. Установить мобильное приложение?",
|
||||
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot использует много расширенных функции интернет-браузера - некоторые из них отсутствуют или экспериментальные в этом браузере.",
|
||||
"Search": "Поиск",
|
||||
"Search…": "Поиск.…",
|
||||
"Send": "Отослать",
|
||||
"Send logs": "Отослать протокол",
|
||||
"Sorry, your browser is <b>not</b> able to run Riot.": "Извините, ваш браузер <b>не может</b> Riot запустить.",
|
||||
"Send logs": "Отослать логи",
|
||||
"Sorry, your browser is <b>not</b> able to run Riot.": "Извините, ваш браузер <b>не может</b> запустить Riot.",
|
||||
"This Room": "Эта комната",
|
||||
"Unavailable": "Недоступен",
|
||||
"Unknown device": "Неизвестное устройство",
|
||||
@@ -166,28 +166,28 @@
|
||||
"Waiting for response from server": "Подождите ответа от сервера",
|
||||
"You are Rioting as a guest. <a>Register</a> or <a>sign in</a> to access more rooms and features!": "Вы вошли в Riot как гость. <a>Зарегистрируйтесь</a> или <a>войдите в систему</a> и получите доступ к огромному количеству комнат и функций!",
|
||||
"OK": "ОК",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Вы должны пользоваться HTTPS чтобы пользоваться видео звонком.",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Используйте протокол HTTPS чтобы совершать видео вызов.",
|
||||
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Если Ваш браузер не корректно отображает информацию и все или некоторые функции отключены, Вы можете и дальше этим браузером пользоваться но ваши проблемы останутся с вами!",
|
||||
"Login": "Войти",
|
||||
"Welcome to Riot.im": "Добро пожаловать на Riot.im",
|
||||
"Welcome to Riot.im": "Добро пожаловать в Riot.im",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Децентрализованное, шифрованное общение и сотрудничество на основе [matrix]",
|
||||
"Search the room directory": "Поиск по директории комнат",
|
||||
"Chat with Riot Bot": "Пообщаться с Riot Bot",
|
||||
"Chat with Riot Bot": "Пообщаться с ботом Riot",
|
||||
"Get started with some tips from Riot Bot!": "Начните с некоторых советов от Riot бота!",
|
||||
"General discussion about Matrix and Riot": "Общая дискуссия о Matrix и Riot",
|
||||
"Discussion of all things Matrix!": "Дискуссия обо всем Matrix!",
|
||||
"Riot/Web & Desktop chat": "Riot-Web & Desktop-Чат",
|
||||
"Riot/Web & Desktop chat": "Riot-Web & Desktop чат",
|
||||
"Matrix technical discussions": "Техническая дискуссия о Matrix",
|
||||
"Running Matrix services": "Предлагать Matrix-Сервис",
|
||||
"Running Matrix services": "Запуск сервиса Matrix",
|
||||
"Community-run support for Synapse": "Поддержка Synapse от сообщества",
|
||||
"Admin support for Dendrite": "Админ. помощь для Dendrite",
|
||||
"Building services on Matrix": "Построить услуги для Matrix",
|
||||
"Building services on Matrix": "Разработка сервисов на Matrix",
|
||||
"Implementing VoIP services with Matrix": "Внедрение услуги VoIP с Matrix",
|
||||
"(HTTP status %(httpStatus)s)": "(HTTP-Состояние %(httpStatus)s)",
|
||||
"Riot/iOS & matrix-ios-sdk chat": "Riot-iOS & \"matrix-ios-sdk\"-Чат",
|
||||
"Riot/Android & matrix-android-sdk chat": "Riot-Android & matrix-android-sdk-Чат",
|
||||
"(HTTP status %(httpStatus)s)": "(HTTP-статус %(httpStatus)s)",
|
||||
"Riot/iOS & matrix-ios-sdk chat": "Riot-iOS & matrix-ios-sdk чат",
|
||||
"Riot/Android & matrix-android-sdk chat": "Riot-Android & matrix-android-sdk чат",
|
||||
"Announcements about Synapse releases": "Объявления релизов Synapse",
|
||||
"Support for those using and running matrix-appservice-irc": "Поддержка тех, кто matrix-appservice-irc эксплуатирует и использует",
|
||||
"Support for those using and running matrix-appservice-irc": "Поддержка тех, кто использует matrix-appservice-irc",
|
||||
"You have successfully set a password!": "Вы успешно установили пароль!",
|
||||
"Continue": "Продолжить",
|
||||
"Please set a password!": "Задайте пароль!",
|
||||
@@ -205,5 +205,7 @@
|
||||
"Co-ordination for Riot/Web translators": "Координирование для переводчиков Riot / Web",
|
||||
"This will allow you to return to your account after signing out, and sign in on other devices.": "Это позволит Вам вернуться в свою учетную запись после выхода, и войти в систему на других устройствах.",
|
||||
"You have successfully set a password and an email address!": "Пароль и адрес электронной почты успешно сохранены!",
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Помните, Вы всегда можете указать адрес электронной почты в пользовательских настройках, если передумаете."
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Помните, Вы всегда можете указать адрес электронной почты в пользовательских настройках, если передумаете.",
|
||||
"Set Password": "Задать пароль",
|
||||
"To return to your account in future you need to <u>set a password</u>": "Чтобы в будущем вернутся к вашей учётной записи, вы должны <u>задать пароль</u>"
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
"Riot/Web & Desktop chat": "แชทเกี่ยวกับ Riot บนเว็บและเดสก์ทอป",
|
||||
"Riot/iOS & matrix-ios-sdk chat": "แชทเกี่ยวกับ Riot บน iOS และ matrix-ios-sdk",
|
||||
"Riot/Android & matrix-android-sdk chat": "แชทเกี่ยวกับ Riot บน Android และ matrix-android-sdk",
|
||||
"Matrix technical discussions": "พูดคุยเรื่อง Matrix ทางเทคนิค",
|
||||
"Matrix technical discussions": "พูดคุยเรื่อง Matrix เชิงเทคนิค",
|
||||
"Running Matrix services": "การติดตั้งบริการ Matrix",
|
||||
"Community-run support for Synapse": "ฝ่ายสนับสนุน Synapse โดยชุมชนผู้ใช้",
|
||||
"Admin support for Dendrite": "ฝ่ายสนับสนุน Dendrite จากผู้ดูแล",
|
||||
@@ -182,7 +182,7 @@
|
||||
"Implementing VR services with Matrix": "การอิมพลีเมนต์บริการ VR ด้วย Matrix",
|
||||
"Implementing VoIP services with Matrix": "การอิมพลีเมนต์บริการ VoIP ด้วย Matrix",
|
||||
"Support for those using, running and writing other bridges": "ฝ่ายสนับสนุนสำหรับผู้ใช้หรือพัฒนาตัวเชื่อมอื่น ๆ",
|
||||
"Contributing code to Matrix and Riot": "สมทบโค๊ดกับ Matrix และ Riot",
|
||||
"Contributing code to Matrix and Riot": "สมทบโค๊ดให้ Matrix และ Riot",
|
||||
"Dev chat for the Riot/Web dev team": "แชทสำหรับทีมพัฒนา Riot บนเว็บ",
|
||||
"Dev chat for the Dendrite dev team": "แชทสำหรับทีมพัฒนา Dendrite",
|
||||
"Co-ordination for Riot/Web translators": "แชทสำหรับประสานงานการแปล Riot บนเว็บ",
|
||||
@@ -199,5 +199,12 @@
|
||||
"General discussion about Matrix and Riot": "พูดคุยเรื่องทั่วไป ทั้ง Matrix และ Riot",
|
||||
"(HTTP status %(httpStatus)s)": "(สถานะ HTTP %(httpStatus)s)",
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "อย่าลืม คุณสามารถตั้งที่อยู่อีเมลในการตั้งค่าผู้ใช้ได้ทุกเมื่อหากคุณเปลี่ยนใจ",
|
||||
"You have successfully set a password and an email address!": "ตั้งรหัสผ่านและที่อยู่อีเมลสำเร็จแล้ว!"
|
||||
"You have successfully set a password and an email address!": "ตั้งรหัสผ่านและที่อยู่อีเมลสำเร็จแล้ว!",
|
||||
"Warning": "คำเตือน",
|
||||
"Checking for an update...": "กำลังตรวจหาอัปเดต...",
|
||||
"Error encountered (%(errorDetail)s).": "เกิดข้อผิดพลาด (%(errorDetail)s)",
|
||||
"No update available.": "ไม่มีอัปเดตที่ใหม่กว่า",
|
||||
"Downloading update...": "กำลังดาวน์โหลดอัปเดต...",
|
||||
"To return to your account in future you need to <u>set a password</u>": "คุณต้อง<u>ตั้งรหัสผ่าน</u>เพื่อจะกลับมาที่บัญชีนี้ในอนาคต",
|
||||
"Set Password": "ตั้งรหัสผ่าน"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"Collapse panel": "Katlanır panel",
|
||||
"Collecting app version information": "Uygulama sürümü bilgileri toplanıyor",
|
||||
"Collecting logs": "Kayıtlar toplanıyor",
|
||||
"Create new room": "Yeni oda oluştur",
|
||||
"Create new room": "Yeni Oda Oluştur",
|
||||
"Couldn't find a matching Matrix room": "Eşleşen bir Matrix odası bulunamadı",
|
||||
"Custom Server Options": "Özel Sunucu Seçenekleri",
|
||||
"customServer_text": "Farklı bir Ana Sunucu URL'si belirleyerek başka bir Matrix sunucusunda oturum açmak için Özel Sunucu Seçeneklerini kullanabilirsiniz. <br/> Bu , Riot'u mevcut Matrix hesabı ile farklı bir Ana Sunucuda kullanmanıza olanak tanır.<br/> <br/> Ayrıca Özel Kimlik Sunucu'da ayarlayabilirsiniz ama kullanıcıları e-posta adresleriyle veya kendi e-posta adresinizle davet edemezsiniz.",
|
||||
@@ -27,9 +27,9 @@
|
||||
"Describe your problem here.": "Probleminizi burada açıklayın.",
|
||||
"Direct Chat": "Doğrudan Sohbet",
|
||||
"Directory": "Dizin",
|
||||
"Dismiss": "Reddet",
|
||||
"Dismiss": "Uzaklaştır",
|
||||
"Download this file": "Bu dosyayı indir",
|
||||
"Drop here %(toAction)s": "Burayı terket %(toAction)s",
|
||||
"Drop here %(toAction)s": "%(toAction)s'ı buraya bırak",
|
||||
"Enable audible notifications in web client": "Web istemcisinde sesli bildirimleri etkinleştir",
|
||||
"Enable desktop notifications": "Masaüstü bildirimlerini etkinleştir",
|
||||
"Enable email notifications": "E-posta bildirimlerini etkinleştir",
|
||||
@@ -43,11 +43,11 @@
|
||||
"Failed to": "Başaramadı",
|
||||
"Failed to add tag %(tagName)s to room": "%(tagName)s etiketi odaya eklenemedi",
|
||||
"Failed to change settings": "Ayarlar değiştirilemedi",
|
||||
"Failed to forget room %(errCode)s": "Odayı unutma başarısız oldu %(errCode)s",
|
||||
"Failed to forget room %(errCode)s": "Oda unutulması başarısız oldu %(errCode)s",
|
||||
"Failed to update keywords": "Anahtar kelimeler güncellenemedi",
|
||||
"Failed to get protocol list from Home Server": "Ana Sunucu'dan protokol listesi alınamadı",
|
||||
"Failed to get public room list": "Genel odalar listesi alınamadı",
|
||||
"Failed to join the room": "Odaya girilemedi",
|
||||
"Failed to join the room": "Odaya girme başarısız oldu",
|
||||
"Failed to remove tag %(tagName)s from room": "Odadan %(tagName)s etiketi kaldırılamadı",
|
||||
"Failed to send report: ": "Rapor gönderilemedi: ",
|
||||
"Failed to set direct chat tag": "Direkt sohbet etiketi ayarlanamadı",
|
||||
@@ -90,7 +90,7 @@
|
||||
"Notify me for anything else": "Başka herhangi bir şey için bana bildirim yap",
|
||||
"Off": "Kapalı",
|
||||
"On": "Açık",
|
||||
"Operation failed": "Operasyon başarısız",
|
||||
"Operation failed": "Operasyon başarısız oldu",
|
||||
"Permalink": "Kalıcı Bağlantı(permalink)",
|
||||
"Please describe the bug. What did you do? What did you expect to happen? What actually happened?": "Lütfen hatayı tanımlayın. Ne yaptınız ? Ne gerçekleşmesini beklediniz ? Ne gerçekleşti ?",
|
||||
"Please describe the bug and/or send logs.": "Lütfen hatayı tanımlayın ve/veya kayıtları gönderin.",
|
||||
@@ -110,7 +110,7 @@
|
||||
"Riot does not know how to join a room on this network": "Riot bu ağdaki bir odaya nasıl gireceğini bilmiyor",
|
||||
"Riot is not supported on mobile web. Install the app?": "Riot mobil web'de desteklenmiyor . Uygulamayı yükle ?",
|
||||
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot geçerli tarayıcınızda mevcut olmayan veya denemelik olan birçok gelişmiş tarayıcı özelliği kullanıyor.",
|
||||
"Room directory": "Oda dizini",
|
||||
"Room directory": "Oda Rehberi",
|
||||
"Room not found": "Oda bulunamadı",
|
||||
"Search": "Ara",
|
||||
"Search…": "Arama…",
|
||||
@@ -120,7 +120,7 @@
|
||||
"Settings": "Ayarlar",
|
||||
"Source URL": "Kaynak URL",
|
||||
"Sorry, your browser is <b>not</b> able to run Riot.": "Üzgünüz , tarayıcınız Riot'u <b> çalıştıramıyor </b>.",
|
||||
"Start chat": "Sohbet başlat",
|
||||
"Start chat": "Sohbet Başlat",
|
||||
"The Home Server may be too old to support third party networks": "Ana Sunucu 3. parti ağları desteklemek için çok eski olabilir",
|
||||
"There are advanced notifications which are not shown here": "Burada gösterilmeyen gelişmiş bildirimler var",
|
||||
"The server may be unavailable or overloaded": "Sunucu kullanılamıyor veya aşırı yüklenmiş olabilir",
|
||||
@@ -133,7 +133,7 @@
|
||||
"Unavailable": "Kullanım dışı",
|
||||
"Unhide Preview": "Önizlemeyi Göster",
|
||||
"Unknown device": "Bilinmeyen aygıt",
|
||||
"unknown error code": "Bilinmeyen hata kodu",
|
||||
"unknown error code": "bilinmeyen hata kodu",
|
||||
"Unnamed room": "İsimsiz oda",
|
||||
"Update": "Güncelleştirme",
|
||||
"Uploaded on %(date)s by %(user)s": "%(user)s tarafında %(date)s e yüklendi",
|
||||
@@ -191,10 +191,10 @@
|
||||
"Dev chat for the Dendrite dev team": "Dendrite Geliştirici Takımı için Geliştirici sohbeti",
|
||||
"Co-ordination for Riot/Web translators": "Riot/Web çevirmenleri için koordinasyon",
|
||||
"Lots of rooms already exist in Matrix, linked to existing networks (Slack, IRC, Gitter etc) or independent. Check out the directory!": "Matrix'te varolan ağlara (Slack , IRC , Gitter vb.) bağlı ya da bağımsız bir çok oda var . Dizini kontrol edin!",
|
||||
"Failed to change password. Is your password correct?": "Şifre değiştirme başarısız oldu . Şifreniz doğru mu ?",
|
||||
"Failed to change password. Is your password correct?": "Şifreniz değiştirilemedi . Şifreniz doğru mu ?",
|
||||
"You have successfully set a password!": "Başarıyla bir şifre ayarladınız!",
|
||||
"You can now return to your account after signing out, and sign in on other devices.": "Şimdi oturumunuzu iptal ettikten sonra başka cihazda oturum açarak hesabınıza dönebilirsiniz.",
|
||||
"Continue": "Devam",
|
||||
"Continue": "Devam Et",
|
||||
"Please set a password!": "Lütfen bir şifre ayarlayın !",
|
||||
"This will allow you to return to your account after signing out, and sign in on other devices.": "Bu oturumunuzu kapattıktan sonra hesabınıza dönmenizi ve diğer cihazlarda oturum açmanızı sağlar.",
|
||||
"You have successfully set a password and an email address!": "Başarıyla bir şifre ve e-posta adresi ayarladın !",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"All messages": "Усі повідомлення",
|
||||
"All messages (loud)": "Усі повідомлення (гучно)",
|
||||
"All Rooms": "Усі кімнати",
|
||||
"All notifications are currently disabled for all targets.": "Повідомлення для усіх цілей на даний момент вимкнені.",
|
||||
"All notifications are currently disabled for all targets.": "Сповіщення для усіх цілей на даний момент вимкнені.",
|
||||
"An error occurred whilst saving your email notification preferences.": "Під час збереження налаштувань сповіщень е-поштою трапилася помилка.",
|
||||
"Cancel": "Скасувати",
|
||||
"Cancel Sending": "Скасувати надсилання",
|
||||
@@ -21,7 +21,7 @@
|
||||
"%(appName)s via %(browserName)s on %(osName)s": "%(appName)s через %(browserName)s на %(osName)s",
|
||||
"<a href=\"http://apple.com/safari\">Safari</a> and <a href=\"http://opera.com\">Opera</a> work too.": "<a href=\"http://apple.com/safari\">Safari</a> та <a href=\"http://opera.com\">Opera</a> також підтримуються.",
|
||||
"Add an email address above to configure email notifications": "Додайте вище адресу е-пошти щоб налаштувати сповіщення е-поштою",
|
||||
"Advanced notification settings": "Додаткові налаштування повідомлень",
|
||||
"Advanced notification settings": "Додаткові налаштування сповіщень",
|
||||
"Delete the room alias %(alias)s and remove %(name)s from the directory?": "Видалити псевдонім %(alias)s та прибрати з каталогу %(name)s?",
|
||||
"Describe your problem here.": "Опишіть вашу проблему тут.",
|
||||
"Direct Chat": "Прямий чат",
|
||||
@@ -194,5 +194,11 @@
|
||||
"No rooms to show": "Кімнати відсутні",
|
||||
"Noisy": "Шумний",
|
||||
"Unavailable": "Нема в наявності",
|
||||
"Unhide Preview": "Відкрити попередній перегляд"
|
||||
"Unhide Preview": "Відкрити попередній перегляд",
|
||||
"Failed to set Direct Message status of room": "Не вдалось встановити статус прямого спілкування в кімнаті",
|
||||
"Messages containing my display name": "Повідомлення, вміщає моє ім'я",
|
||||
"Running Matrix services": "Запуск служби Matrix",
|
||||
"Set Password": "Задати пароль",
|
||||
"Notifications on the following keywords follow rules which can’t be displayed here:": "Сповіщення з наступних ключових слів дотримуються правил, що не можуть бути показані тут:",
|
||||
"To return to your account in future you need to <u>set a password</u>": "Щоб мати змогу використовувати вашу обліковку у майбутньому, <u>зазначте пароль</u>"
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
@import "./matrix-react-sdk/views/rooms/_SearchableEntityList.scss";
|
||||
@import "./matrix-react-sdk/views/rooms/_TabCompleteBar.scss";
|
||||
@import "./matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss";
|
||||
@import "./matrix-react-sdk/views/rooms/_AppsDrawer.scss";
|
||||
@import "./matrix-react-sdk/views/settings/_DevicesPanel.scss";
|
||||
@import "./matrix-react-sdk/views/settings/_IntegrationsManager.scss";
|
||||
@import "./matrix-react-sdk/views/voip/_CallView.scss";
|
||||
|
||||
@@ -102,6 +102,13 @@ limitations under the License.
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.mx_UserSettings_passwordWarning {
|
||||
/* To move the "Sign out" button out of the way */
|
||||
clear: both;
|
||||
color: $warning-color;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.mx_UserSettings_importExportButtons {
|
||||
padding-top: 10px;
|
||||
padding-left: 40px;
|
||||
|
||||
@@ -14,6 +14,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_MEmoteBody {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.mx_MEmoteBody_sender {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket 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.
|
||||
*/
|
||||
|
||||
.mx_AppsDrawer {
|
||||
}
|
||||
|
||||
.mx_AppsContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mx_AddWidget_button {
|
||||
order: 2;
|
||||
cursor: pointer;
|
||||
padding-right: 12px;
|
||||
padding: 0;
|
||||
margin: 0 0 5px 0;
|
||||
color: $accent-color;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.mx_SetAppURLDialog_input {
|
||||
border-radius: 3px;
|
||||
border: 1px solid $input-border-color;
|
||||
padding: 9px;
|
||||
color: $primary-hairline-color;
|
||||
background-color: $primary-bg-color;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.mx_AppTile {
|
||||
width: 50%;
|
||||
margin: 0 5px 2px 0;
|
||||
border: 1px solid $primary-hairline-color;
|
||||
border-radius: 2px;
|
||||
// height: 350px;
|
||||
// display: inline-block;
|
||||
}
|
||||
|
||||
.mx_AppTileFullWidth {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 1px solid $primary-hairline-color;
|
||||
border-radius: 2px;
|
||||
// height: 350px;
|
||||
// display: inline-block;
|
||||
}
|
||||
|
||||
.mx_AppTileMenuBar {
|
||||
// height: 15px;
|
||||
margin: 0;
|
||||
padding: 2px 10px;
|
||||
// background-color: $e2e-verified-color;
|
||||
border-bottom: 1px solid $primary-hairline-color;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.mx_AppTileMenuBarWidgets {
|
||||
float: right;
|
||||
}
|
||||
.mx_AppTileMenuBarWidget {
|
||||
// pointer-events: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_AppTileBody iframe {
|
||||
width: 100%;
|
||||
height: 350px;
|
||||
overflow: hidden;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mx_CloseAppWidget {
|
||||
}
|
||||
|
||||
.mx_AppTileMenuBarWidgetPadding {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.mx_AppIconTile {
|
||||
background-color: $lightbox-bg-color;
|
||||
border: 1px solid rgba(0, 0, 0, 0);
|
||||
width: 200px;
|
||||
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
|
||||
transition: 0.3s;
|
||||
border-radius: 3px;
|
||||
margin: 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.mx_AppIconTile.mx_AppIconTile_active {
|
||||
color: $accent-color;
|
||||
border-color: $accent-color;
|
||||
}
|
||||
|
||||
.mx_AppIconTile:hover {
|
||||
border: 1px solid $accent-color;
|
||||
box-shadow: 0 0 10px 5px rgba(200,200,200,0.5);
|
||||
}
|
||||
|
||||
.mx_AppIconTile_content {
|
||||
padding: 2px 16px;
|
||||
height: 60px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mx_AppIconTile_content h4 {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.mx_AppIconTile_content p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 5px;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.mx_AppIconTile_image {
|
||||
padding: 10px;
|
||||
width: 75%;
|
||||
max-width:100px;
|
||||
max-height:100px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.mx_AppIconTile_imageContainer {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
border-radius: 3px 3px 0 0;
|
||||
height: 155px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
form.mx_Custom_Widget_Form div {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@@ -38,6 +38,7 @@
|
||||
.mx_Autocomplete_Completion_pill {
|
||||
border-radius: 17px;
|
||||
height: 34px;
|
||||
padding: 0px 5px;
|
||||
display: flex;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
@@ -45,10 +46,22 @@
|
||||
color: $primary-fg-color;
|
||||
}
|
||||
|
||||
.mx_Autocomplete_Completion_pill * {
|
||||
.mx_Autocomplete_Completion_pill > * {
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
.mx_Autocomplete_Completion_container_truncate {
|
||||
.mx_Autocomplete_Completion_title,
|
||||
.mx_Autocomplete_Completion_subtitle,
|
||||
.mx_Autocomplete_Completion_description {
|
||||
/* Ellipsis for long names/subtitles/descriptions*/
|
||||
max-width: 150px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
/* container for pill-style completions */
|
||||
.mx_Autocomplete_Completion_container_pill {
|
||||
margin: 12px;
|
||||
|
||||
@@ -128,7 +128,8 @@ limitations under the License.
|
||||
.mx_MessageComposer_upload,
|
||||
.mx_MessageComposer_hangup,
|
||||
.mx_MessageComposer_voicecall,
|
||||
.mx_MessageComposer_videocall {
|
||||
.mx_MessageComposer_videocall,
|
||||
.mx_MessageComposer_apps {
|
||||
/*display: table-cell;*/
|
||||
/*vertical-align: middle;*/
|
||||
/*padding-left: 10px;*/
|
||||
@@ -140,7 +141,8 @@ limitations under the License.
|
||||
.mx_MessageComposer_upload object,
|
||||
.mx_MessageComposer_hangup object,
|
||||
.mx_MessageComposer_voicecall object,
|
||||
.mx_MessageComposer_videocall object {
|
||||
.mx_MessageComposer_videocall object,
|
||||
.mx_MessageComposer_apps object {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
13
src/skins/vector/img/edit.svg
Normal file
13
src/skins/vector/img/edit.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
|
||||
<rect x="178.846" y="92.087" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 224.3476 631.1498)" width="128.085" height="354.049"/>
|
||||
<path d="M471.723,88.393l-48.115-48.114c-11.723-11.724-31.558-10.896-44.304,1.85l-45.202,45.203l90.569,90.568l45.202-45.202
|
||||
C482.616,119.952,483.445,100.116,471.723,88.393z"/>
|
||||
<polygon points="64.021,363.252 32,480 148.737,447.979 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 876 B |
24
src/skins/vector/img/icons-apps-active.svg
Normal file
24
src/skins/vector/img/icons-apps-active.svg
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="35px"
|
||||
height="35px" viewBox="0 0 35 35" enable-background="new 0 0 35 35" xml:space="preserve">
|
||||
<g id="Layer_1">
|
||||
<path id="Oval-109-Copy" fill="#76CFA6" enable-background="new " d="M17.5,35C27.165,35,35,27.165,35,17.5S27.165,0,17.5,0
|
||||
S0,7.835,0,17.5S7.835,35,17.5,35z"/>
|
||||
<g id="Icon">
|
||||
<g>
|
||||
<path fill="none" stroke="#FFFFFF" d="M7.5,12.5h5v-5h-5V12.5z M15,27.5h5v-5h-5V27.5z M7.5,27.5h5v-5h-5V27.5z M7.5,20h5v-5h-5
|
||||
V20z M15,20h5v-5h-5V20z M22.5,7.5v5h5v-5H22.5z M15,12.5h5v-5h-5V12.5z M22.5,20h5v-5h-5V20z M22.5,27.5h5v-5h-5V27.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g id="Icon_1_" opacity="0.15">
|
||||
<g>
|
||||
<path fill="none" stroke="#76CFA6" d="M7.5,12.5h5v-5h-5V12.5z M15,27.5h5v-5h-5V27.5z M7.5,27.5h5v-5h-5V27.5z M7.5,20h5v-5h-5
|
||||
V20z M15,20h5v-5h-5V20z M22.5,7.5v5h5v-5H22.5z M15,12.5h5v-5h-5V12.5z M22.5,20h5v-5h-5V20z M22.5,27.5h5v-5h-5V27.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
14
src/skins/vector/img/icons-apps.svg
Normal file
14
src/skins/vector/img/icons-apps.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="35px" height="35px" viewBox="0 0 35 35" enable-background="new 0 0 35 35" xml:space="preserve">
|
||||
<path id="Oval-109-Copy" opacity="0.15" fill="#76CFA6" enable-background="new " d="M17.5,35C27.165,35,35,27.165,35,17.5
|
||||
S27.165,0,17.5,0S0,7.835,0,17.5S7.835,35,17.5,35z"/>
|
||||
<g id="Icon">
|
||||
<g>
|
||||
<path fill="none" stroke="#76CFA6" d="M7.5,12.5h5v-5h-5V12.5z M15,27.5h5v-5h-5V27.5z M7.5,27.5h5v-5h-5V27.5z M7.5,20h5v-5h-5
|
||||
V20z M15,20h5v-5h-5V20z M22.5,7.5v5h5v-5H22.5z M15,12.5h5v-5h-5V12.5z M22.5,20h5v-5h-5V20z M22.5,27.5h5v-5h-5V27.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 941 B |
@@ -45,6 +45,12 @@ rageshake.init().then(() => {
|
||||
console.error("Failed to initialise rageshake: " + err);
|
||||
});
|
||||
|
||||
window.addEventListener('beforeunload', (e) => {
|
||||
console.log('riot-web closing');
|
||||
// try to flush the logs to indexeddb
|
||||
rageshake.flush();
|
||||
});
|
||||
|
||||
|
||||
// add React and ReactPerf to the global namespace, to make them easier to
|
||||
// access via the console
|
||||
@@ -59,7 +65,6 @@ var sdk = require("matrix-react-sdk");
|
||||
const PlatformPeg = require("matrix-react-sdk/lib/PlatformPeg");
|
||||
sdk.loadSkin(require('../component-index'));
|
||||
var VectorConferenceHandler = require('../VectorConferenceHandler');
|
||||
var UpdateChecker = require("./updater");
|
||||
var q = require('q');
|
||||
var request = require('browser-request');
|
||||
import * as UserSettingsStore from 'matrix-react-sdk/lib/UserSettingsStore';
|
||||
@@ -216,18 +221,16 @@ function getConfig() {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function onLoadCompleted() {
|
||||
function onTokenLoginCompleted() {
|
||||
// if we did a token login, we're now left with the token, hs and is
|
||||
// url as query params in the url; a little nasty but let's redirect to
|
||||
// clear them.
|
||||
if (window.location.search) {
|
||||
var parsedUrl = url.parse(window.location.href);
|
||||
parsedUrl.search = "";
|
||||
var formatted = url.format(parsedUrl);
|
||||
console.log("Redirecting to " + formatted + " to drop loginToken " +
|
||||
"from queryparams");
|
||||
window.location.href = formatted;
|
||||
}
|
||||
var parsedUrl = url.parse(window.location.href);
|
||||
parsedUrl.search = "";
|
||||
var formatted = url.format(parsedUrl);
|
||||
console.log("Redirecting to " + formatted + " to drop loginToken " +
|
||||
"from queryparams");
|
||||
window.location.href = formatted;
|
||||
}
|
||||
|
||||
async function loadApp() {
|
||||
@@ -277,7 +280,9 @@ async function loadApp() {
|
||||
Unable to load config file: please refresh the page to try again.
|
||||
</div>, document.getElementById('matrixchat'));
|
||||
} else if (validBrowser) {
|
||||
UpdateChecker.start();
|
||||
const platform = PlatformPeg.get();
|
||||
platform.startUpdater();
|
||||
|
||||
const MatrixChat = sdk.getComponent('structures.MatrixChat');
|
||||
window.matrixChat = ReactDOM.render(
|
||||
<MatrixChat
|
||||
@@ -288,9 +293,9 @@ async function loadApp() {
|
||||
realQueryParams={params}
|
||||
startingFragmentQueryParams={fragparts.params}
|
||||
enableGuest={true}
|
||||
onLoadCompleted={onLoadCompleted}
|
||||
onTokenLoginCompleted={onTokenLoginCompleted}
|
||||
initialScreenAfterLogin={getScreenFromLocation(window.location)}
|
||||
defaultDeviceDisplayName={PlatformPeg.get().getDefaultDeviceDisplayName()}
|
||||
defaultDeviceDisplayName={platform.getDefaultDeviceDisplayName()}
|
||||
/>,
|
||||
document.getElementById('matrixchat')
|
||||
);
|
||||
|
||||
@@ -17,14 +17,21 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import VectorBasePlatform from './VectorBasePlatform';
|
||||
import VectorBasePlatform, {updateCheckStatusEnum} from './VectorBasePlatform';
|
||||
import dis from 'matrix-react-sdk/lib/dispatcher';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
import q from 'q';
|
||||
import electron, {remote, ipcRenderer} from 'electron';
|
||||
import {remote, ipcRenderer} from 'electron';
|
||||
import rageshake from '../rageshake';
|
||||
|
||||
remote.autoUpdater.on('update-downloaded', onUpdateDownloaded);
|
||||
|
||||
// try to flush the rageshake logs to indexeddb before quit.
|
||||
ipcRenderer.on('before-quit', function () {
|
||||
console.log('riot-desktop closing');
|
||||
rageshake.flush();
|
||||
});
|
||||
|
||||
function onUpdateDownloaded(ev: Event, releaseNotes: string, ver: string, date: Date, updateURL: string) {
|
||||
dis.dispatch({
|
||||
action: 'new_version',
|
||||
@@ -62,10 +69,42 @@ function _onAction(payload: Object) {
|
||||
}
|
||||
}
|
||||
|
||||
function getUpdateCheckStatus(status) {
|
||||
if (status === true) {
|
||||
return { status: updateCheckStatusEnum.DOWNLOADING };
|
||||
} else if (status === false) {
|
||||
return { status: updateCheckStatusEnum.NOTAVAILABLE };
|
||||
} else {
|
||||
return {
|
||||
status: updateCheckStatusEnum.ERROR,
|
||||
detail: status,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default class ElectronPlatform extends VectorBasePlatform {
|
||||
constructor() {
|
||||
super();
|
||||
dis.register(_onAction);
|
||||
this.updatable = Boolean(remote.autoUpdater.getFeedURL());
|
||||
|
||||
/*
|
||||
IPC Call `check_updates` returns:
|
||||
true if there is an update available
|
||||
false if there is not
|
||||
or the error if one is encountered
|
||||
*/
|
||||
ipcRenderer.on('check_updates', (event, status) => {
|
||||
if (!this.showUpdateCheck) return;
|
||||
dis.dispatch({
|
||||
action: 'check_updates',
|
||||
value: getUpdateCheckStatus(status),
|
||||
});
|
||||
this.showUpdateCheck = false;
|
||||
});
|
||||
|
||||
this.startUpdateCheck = this.startUpdateCheck.bind(this);
|
||||
this.stopUpdateCheck = this.stopUpdateCheck.bind(this);
|
||||
}
|
||||
|
||||
getHumanReadableName(): string {
|
||||
@@ -137,17 +176,18 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
return q(remote.app.getVersion());
|
||||
}
|
||||
|
||||
pollForUpdate() {
|
||||
// In electron we control the update process ourselves, since
|
||||
// it needs to run in the main process, so we just run the timer
|
||||
// loop in the main electron process instead.
|
||||
startUpdateCheck() {
|
||||
if (this.showUpdateCheck) return;
|
||||
super.startUpdateCheck();
|
||||
|
||||
ipcRenderer.send('check_updates');
|
||||
}
|
||||
|
||||
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.ipcRenderer.send('install_update');
|
||||
ipcRenderer.send('install_update');
|
||||
}
|
||||
|
||||
getDefaultDeviceDisplayName(): string {
|
||||
|
||||
@@ -19,9 +19,18 @@ limitations under the License.
|
||||
|
||||
import BasePlatform from 'matrix-react-sdk/lib/BasePlatform';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
import dis from 'matrix-react-sdk/lib/dispatcher';
|
||||
|
||||
import Favico from 'favico.js';
|
||||
|
||||
export const updateCheckStatusEnum = {
|
||||
CHECKING: 'CHECKING',
|
||||
ERROR: 'ERROR',
|
||||
NOTAVAILABLE: 'NOTAVAILABLE',
|
||||
DOWNLOADING: 'DOWNLOADING',
|
||||
READY: 'READY',
|
||||
};
|
||||
|
||||
/**
|
||||
* Vector-specific extensions to the BasePlatform template
|
||||
*/
|
||||
@@ -34,7 +43,12 @@ export default class VectorBasePlatform extends BasePlatform {
|
||||
// and we set the state each time, even if the value hasn't changed,
|
||||
// so we'd need to fix that if enabling the animation.
|
||||
this.favicon = new Favico({animation: 'none'});
|
||||
this.showUpdateCheck = false;
|
||||
this._updateFavicon();
|
||||
this.updatable = true;
|
||||
|
||||
this.startUpdateCheck = this.startUpdateCheck.bind(this);
|
||||
this.stopUpdateCheck = this.stopUpdateCheck.bind(this);
|
||||
}
|
||||
|
||||
getHumanReadableName(): string {
|
||||
@@ -75,12 +89,32 @@ export default class VectorBasePlatform extends BasePlatform {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for the availability of an update to the version of the
|
||||
* app that's currently running.
|
||||
* If an update is available, this function should dispatch the
|
||||
* 'new_version' action.
|
||||
* Begin update polling, if applicable
|
||||
*/
|
||||
pollForUpdate() {
|
||||
startUpdater() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we can call checkForUpdate on this platform build
|
||||
*/
|
||||
canSelfUpdate(): boolean {
|
||||
return this.updatable;
|
||||
}
|
||||
|
||||
startUpdateCheck() {
|
||||
this.showUpdateCheck = true;
|
||||
dis.dispatch({
|
||||
action: 'check_updates',
|
||||
value: { status: updateCheckStatusEnum.CHECKING },
|
||||
});
|
||||
}
|
||||
|
||||
stopUpdateCheck() {
|
||||
this.showUpdateCheck = false;
|
||||
dis.dispatch({
|
||||
action: 'check_updates',
|
||||
value: false,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import VectorBasePlatform from './VectorBasePlatform';
|
||||
import VectorBasePlatform, {updateCheckStatusEnum} from './VectorBasePlatform';
|
||||
import request from 'browser-request';
|
||||
import dis from 'matrix-react-sdk/lib/dispatcher.js';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
@@ -26,10 +26,15 @@ import q from 'q';
|
||||
import url from 'url';
|
||||
import UAParser from 'ua-parser-js';
|
||||
|
||||
var POKE_RATE_MS = 10 * 60 * 1000; // 10 min
|
||||
|
||||
export default class WebPlatform extends VectorBasePlatform {
|
||||
constructor() {
|
||||
super();
|
||||
this.runningVersion = null;
|
||||
|
||||
this.startUpdateCheck = this.startUpdateCheck.bind(this);
|
||||
this.stopUpdateCheck = this.stopUpdateCheck.bind(this);
|
||||
}
|
||||
|
||||
getHumanReadableName(): string {
|
||||
@@ -132,8 +137,13 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
return this._getVersion();
|
||||
}
|
||||
|
||||
startUpdater() {
|
||||
this.pollForUpdate();
|
||||
setInterval(this.pollForUpdate.bind(this), POKE_RATE_MS);
|
||||
}
|
||||
|
||||
pollForUpdate() {
|
||||
this._getVersion().done((ver) => {
|
||||
return this._getVersion().then((ver) => {
|
||||
if (this.runningVersion === null) {
|
||||
this.runningVersion = ver;
|
||||
} else if (this.runningVersion !== ver) {
|
||||
@@ -142,9 +152,29 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
currentVersion: this.runningVersion,
|
||||
newVersion: ver,
|
||||
});
|
||||
// Return to skip a MatrixChat state update
|
||||
return;
|
||||
}
|
||||
return { status: updateCheckStatusEnum.NOTAVAILABLE };
|
||||
}, (err) => {
|
||||
console.error("Failed to poll for update", err);
|
||||
return {
|
||||
status: updateCheckStatusEnum.ERROR,
|
||||
detail: err.message || err.status ? err.status.toString() : 'Unknown Error',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
startUpdateCheck() {
|
||||
if (this.showUpdateCheck) return;
|
||||
super.startUpdateCheck();
|
||||
this.pollForUpdate().then((updateState) => {
|
||||
if (!this.showUpdateCheck) return;
|
||||
if (!updateState) return;
|
||||
dis.dispatch({
|
||||
action: 'check_updates',
|
||||
value: updateState,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -410,8 +410,16 @@ module.exports = {
|
||||
}
|
||||
logger = new ConsoleLogger();
|
||||
logger.monkeyPatch(window.console);
|
||||
if (window.indexedDB) {
|
||||
store = new IndexedDBLogStore(window.indexedDB, logger);
|
||||
|
||||
// just *accessing* indexedDB throws an exception in firefox with
|
||||
// indexeddb disabled.
|
||||
let indexedDB;
|
||||
try {
|
||||
indexedDB = window.indexedDB;
|
||||
} catch(e) {}
|
||||
|
||||
if (indexedDB) {
|
||||
store = new IndexedDBLogStore(indexedDB, logger);
|
||||
initPromise = store.connect();
|
||||
return initPromise;
|
||||
}
|
||||
@@ -419,6 +427,13 @@ module.exports = {
|
||||
return initPromise;
|
||||
},
|
||||
|
||||
flush: function() {
|
||||
if (!store) {
|
||||
return;
|
||||
}
|
||||
store.flush();
|
||||
},
|
||||
|
||||
/**
|
||||
* Clean up old logs.
|
||||
* @return Promise Resolves if cleaned logs.
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket 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 PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
|
||||
|
||||
var POKE_RATE_MS = 10 * 60 * 1000; // 10 min
|
||||
|
||||
module.exports = {
|
||||
start: function() {
|
||||
module.exports.poll();
|
||||
setInterval(module.exports.poll, POKE_RATE_MS);
|
||||
},
|
||||
|
||||
poll: function() {
|
||||
PlatformPeg.get().pollForUpdate();
|
||||
}
|
||||
};
|
||||
@@ -36,7 +36,7 @@ var expect = require('expect');
|
||||
var q = require('q');
|
||||
|
||||
var test_utils = require('../test-utils');
|
||||
var MockHttpBackend = require('../mock-request');
|
||||
var MockHttpBackend = require('matrix-mock-request');
|
||||
|
||||
var HS_URL='http://localhost';
|
||||
var IS_URL='http://localhost';
|
||||
|
||||
@@ -29,9 +29,11 @@ import jssdk from 'matrix-js-sdk';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
||||
import * as languageHandler from 'matrix-react-sdk/lib/languageHandler';
|
||||
import {VIEWS} from 'matrix-react-sdk/lib/components/structures/MatrixChat';
|
||||
import dis from 'matrix-react-sdk/lib/dispatcher';
|
||||
|
||||
import * as test_utils from '../test-utils';
|
||||
import MockHttpBackend from '../mock-request';
|
||||
import MockHttpBackend from 'matrix-mock-request';
|
||||
import {parseQs, parseQsFromFragment} from '../../src/vector/url_utils';
|
||||
|
||||
var DEFAULT_HS_URL='http://my_server';
|
||||
@@ -47,8 +49,8 @@ describe('loading:', function () {
|
||||
// the mounted MatrixChat
|
||||
let matrixChat;
|
||||
|
||||
// a promise which resolves when the MatrixChat calls onLoadCompleted
|
||||
let loadCompletePromise;
|
||||
// a promise which resolves when the MatrixChat calls onTokenLoginCompleted
|
||||
let tokenLoginCompletePromise;
|
||||
|
||||
beforeEach(function() {
|
||||
test_utils.beforeEach(this);
|
||||
@@ -68,7 +70,8 @@ describe('loading:', function () {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async function() {
|
||||
afterEach(async function () {
|
||||
console.log(`${Date.now()}: loading: afterEach`);
|
||||
if (parentDiv) {
|
||||
ReactDOM.unmountComponentAtNode(parentDiv);
|
||||
parentDiv.remove();
|
||||
@@ -81,6 +84,7 @@ describe('loading:', function () {
|
||||
// clear the indexeddbs so we can start from a clean slate next time.
|
||||
await test_utils.deleteIndexedDB('matrix-js-sdk:crypto');
|
||||
await test_utils.deleteIndexedDB('matrix-js-sdk:riot-web-sync');
|
||||
console.log(`${Date.now()}: loading: afterEach complete`);
|
||||
});
|
||||
|
||||
/* simulate the load process done by index.js
|
||||
@@ -99,8 +103,8 @@ describe('loading:', function () {
|
||||
toString: function() { return this.search + this.hash; },
|
||||
};
|
||||
|
||||
let loadCompleteDefer = q.defer();
|
||||
loadCompletePromise = loadCompleteDefer.promise;
|
||||
let tokenLoginCompleteDefer = q.defer();
|
||||
tokenLoginCompletePromise = tokenLoginCompleteDefer.promise;
|
||||
|
||||
function onNewScreen(screen) {
|
||||
console.log(Date.now() + " newscreen "+screen);
|
||||
@@ -135,7 +139,7 @@ describe('loading:', function () {
|
||||
realQueryParams={params}
|
||||
startingFragmentQueryParams={fragParts.params}
|
||||
enableGuest={true}
|
||||
onLoadCompleted={loadCompleteDefer.resolve}
|
||||
onTokenLoginCompleted={tokenLoginCompleteDefer.resolve}
|
||||
initialScreenAfterLogin={getScreenFromLocation(windowLocation)}
|
||||
makeRegistrationUrl={() => {throw new Error('Not implemented');}}
|
||||
/>, parentDiv
|
||||
@@ -153,8 +157,8 @@ describe('loading:', function () {
|
||||
.check((r) => {syncRequest = r;})
|
||||
.respond(200, response);
|
||||
|
||||
console.log("waiting for /sync");
|
||||
for (let attempts = 10; attempts > 0; attempts--) {
|
||||
console.log(Date.now() + " waiting for /sync");
|
||||
if (syncRequest) {
|
||||
return syncRequest;
|
||||
}
|
||||
@@ -179,12 +183,12 @@ describe('loading:', function () {
|
||||
return httpBackend.flush();
|
||||
}).then(() => {
|
||||
// Wait for another trip around the event loop for the UI to update
|
||||
return q.delay(1);
|
||||
return q.delay(10);
|
||||
}).then(() => {
|
||||
// we expect a single <Login> component following session load
|
||||
ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, sdk.getComponent('structures.login.Login'));
|
||||
expect(windowLocation.hash).toEqual("");
|
||||
expect(windowLocation.hash).toEqual("#/login");
|
||||
}).done(done, done);
|
||||
});
|
||||
|
||||
@@ -205,7 +209,7 @@ describe('loading:', function () {
|
||||
return httpBackend.flush();
|
||||
}).then(() => {
|
||||
// Wait for another trip around the event loop for the UI to update
|
||||
return q.delay(1);
|
||||
return q.delay(10);
|
||||
}).then(() => {
|
||||
return completeLogin(matrixChat);
|
||||
}).then(() => {
|
||||
@@ -360,6 +364,9 @@ describe('loading:', function () {
|
||||
loadApp({
|
||||
uriFragment: "#/login",
|
||||
});
|
||||
|
||||
// give the UI a chance to display
|
||||
return q.delay(50);
|
||||
});
|
||||
|
||||
it('shows a login view', function() {
|
||||
@@ -489,6 +496,76 @@ describe('loading:', function () {
|
||||
expect(windowLocation.hash).toEqual("#/room/!room:id");
|
||||
}).done(done, done);
|
||||
});
|
||||
|
||||
describe('Login as user', function() {
|
||||
beforeEach(function() {
|
||||
// first we have to load the homepage
|
||||
loadApp();
|
||||
|
||||
httpBackend.when('POST', '/register').check(function(req) {
|
||||
expect(req.queryParams.kind).toEqual('guest');
|
||||
}).respond(200, {
|
||||
user_id: "@guest:localhost",
|
||||
access_token: "secret_token",
|
||||
});
|
||||
|
||||
return httpBackend.flush().then(() => {
|
||||
return awaitSyncingSpinner(matrixChat);
|
||||
}).then(() => {
|
||||
// we got a sync spinner - let the sync complete
|
||||
return expectAndAwaitSync();
|
||||
}).then(() => {
|
||||
// once the sync completes, we should have a home page
|
||||
ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, sdk.getComponent('structures.HomePage'));
|
||||
|
||||
// we simulate a click on the 'login' button by firing off
|
||||
// the relevant dispatch.
|
||||
//
|
||||
// XXX: is it an anti-pattern to access the react-sdk's
|
||||
// dispatcher in this way? Is it better to find the login
|
||||
// button and simulate a click? (we might have to arrange
|
||||
// for it to be shown - it's not always, due to the
|
||||
// collapsing left panel
|
||||
|
||||
dis.dispatch({ action: 'start_login' });
|
||||
|
||||
return q.delay(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should give us a login page', function() {
|
||||
expect(windowLocation.hash).toEqual("#/login");
|
||||
|
||||
// we expect a single <Login> component
|
||||
ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, sdk.getComponent('structures.login.Login')
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow us to return to the app', function() {
|
||||
const login = ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, sdk.getComponent('structures.login.Login')
|
||||
);
|
||||
|
||||
const linkText = 'Return to app';
|
||||
|
||||
const returnToApp = ReactTestUtils.scryRenderedDOMComponentsWithTag(
|
||||
login, 'a').find((e) => e.innerText === linkText);
|
||||
|
||||
if (!returnToApp) {
|
||||
throw new Error(`Couldn't find '${linkText}' link`);
|
||||
}
|
||||
|
||||
ReactTestUtils.Simulate.click(returnToApp);
|
||||
|
||||
return q.delay(1).then(() => {
|
||||
// we should be straight back into the home page
|
||||
ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, sdk.getComponent('structures.HomePage'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Token login:', function() {
|
||||
@@ -513,12 +590,12 @@ describe('loading:', function () {
|
||||
|
||||
return httpBackend.flush();
|
||||
}).then(() => {
|
||||
// at this point, MatrixChat should fire onLoadCompleted, which
|
||||
// at this point, MatrixChat should fire onTokenLoginCompleted, which
|
||||
// makes index.js reload the app. We're not going to attempt to
|
||||
// simulate the reload - just check that things are left in the
|
||||
// right state for the reloaded app.
|
||||
|
||||
return loadCompletePromise;
|
||||
return tokenLoginCompletePromise;
|
||||
}).then(() => {
|
||||
// check that the localstorage has been set up in such a way that
|
||||
// the reloaded app can pick up where we leave off.
|
||||
@@ -539,7 +616,6 @@ describe('loading:', function () {
|
||||
matrixChat, sdk.getComponent('structures.login.Login'));
|
||||
|
||||
httpBackend.when('POST', '/login').check(function(req) {
|
||||
console.log(req);
|
||||
expect(req.data.type).toEqual('m.login.password');
|
||||
expect(req.data.identifier.type).toEqual('m.id.user');
|
||||
expect(req.data.identifier.user).toEqual('user');
|
||||
@@ -589,7 +665,8 @@ function awaitSyncingSpinner(matrixChat, retryLimit, retryCount) {
|
||||
retryCount = 0;
|
||||
}
|
||||
|
||||
if (matrixChat.state.loading || matrixChat.state.loggingIn) {
|
||||
if (matrixChat.state.view === VIEWS.LOADING ||
|
||||
matrixChat.state.view === VIEWS.LOGGING_IN) {
|
||||
console.log(Date.now() + " Awaiting sync spinner: still loading.");
|
||||
if (retryCount >= retryLimit) {
|
||||
throw new Error("MatrixChat still not loaded after " +
|
||||
@@ -628,8 +705,7 @@ function awaitRoomView(matrixChat, retryLimit, retryCount) {
|
||||
retryCount = 0;
|
||||
}
|
||||
|
||||
if (matrixChat.state.loading ||
|
||||
!(matrixChat.state.loggedIn && matrixChat.state.ready)) {
|
||||
if (matrixChat.state.view !== VIEWS.LOGGED_IN || !matrixChat.state.ready) {
|
||||
console.log(Date.now() + " Awaiting room view: not ready yet.");
|
||||
if (retryCount >= retryLimit) {
|
||||
throw new Error("MatrixChat still not ready after " +
|
||||
|
||||
@@ -1,336 +0,0 @@
|
||||
"use strict";
|
||||
const q = require("q");
|
||||
import expect from 'expect';
|
||||
|
||||
/**
|
||||
* Construct a mock HTTP backend, heavily inspired by Angular.js.
|
||||
* @constructor
|
||||
*/
|
||||
function HttpBackend() {
|
||||
this.requests = [];
|
||||
this.expectedRequests = [];
|
||||
const self = this;
|
||||
// the request function dependency that the SDK needs.
|
||||
this.requestFn = function(opts, callback) {
|
||||
const req = new Request(opts, callback);
|
||||
console.log(`${Date.now()} HTTP backend received request: ${req}`);
|
||||
self.requests.push(req);
|
||||
|
||||
const abort = function() {
|
||||
const idx = self.requests.indexOf(req);
|
||||
if (idx >= 0) {
|
||||
console.log("Aborting HTTP request: %s %s", opts.method,
|
||||
opts.uri);
|
||||
self.requests.splice(idx, 1);
|
||||
req.callback("aborted");
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
abort: abort,
|
||||
};
|
||||
};
|
||||
|
||||
// very simplistic mapping from the whatwg fetch interface onto the request
|
||||
// interface, so we can use the same mock backend for both.
|
||||
this.fetchFn = function(input, init) {
|
||||
init = init || {};
|
||||
const requestOpts = {
|
||||
uri: input,
|
||||
method: init.method || 'GET',
|
||||
body: init.body,
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
function callback(err, response, body) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve({
|
||||
ok: response.statusCode >= 200 && response.statusCode < 300,
|
||||
json: () => body,
|
||||
});
|
||||
};
|
||||
|
||||
const req = new Request(requestOpts, callback);
|
||||
console.log(`HTTP backend received request: ${req}`);
|
||||
self.requests.push(req);
|
||||
});
|
||||
};
|
||||
}
|
||||
HttpBackend.prototype = {
|
||||
/**
|
||||
* Respond to all of the requests (flush the queue).
|
||||
* @param {string} path The path to flush (optional) default: all.
|
||||
* @param {integer} numToFlush The number of things to flush (optional), default: all.
|
||||
* @param {integer=} waitTime The time (in ms) to wait for a request to happen.
|
||||
* default: 100
|
||||
*
|
||||
* @return {Promise} resolves when there is nothing left to flush, with the
|
||||
* number of requests flushed
|
||||
*/
|
||||
flush: function(path, numToFlush, waitTime) {
|
||||
const defer = q.defer();
|
||||
const self = this;
|
||||
let flushed = 0;
|
||||
if (waitTime === undefined) {
|
||||
waitTime = 100;
|
||||
}
|
||||
|
||||
function log(msg) {
|
||||
console.log(`${Date.now()} flush[${path || ''}]: ${msg}`);
|
||||
}
|
||||
|
||||
log("HTTP backend flushing... (path=" + path
|
||||
+ " numToFlush=" + numToFlush
|
||||
+ " waitTime=" + waitTime
|
||||
+ ")",
|
||||
);
|
||||
const endTime = waitTime + Date.now();
|
||||
|
||||
const tryFlush = function() {
|
||||
// if there's more real requests and more expected requests, flush 'em.
|
||||
log(` trying to flush => reqs=[${self.requests}] ` +
|
||||
`expected=[${self.expectedRequests}]`,
|
||||
);
|
||||
if (self._takeFromQueue(path)) {
|
||||
// try again on the next tick.
|
||||
flushed += 1;
|
||||
if (numToFlush && flushed === numToFlush) {
|
||||
log(`Flushed assigned amount: ${numToFlush}`);
|
||||
defer.resolve(flushed);
|
||||
} else {
|
||||
log(` flushed. Trying for more.`);
|
||||
setTimeout(tryFlush, 0);
|
||||
}
|
||||
} else if (flushed === 0 && Date.now() < endTime) {
|
||||
// we may not have made the request yet, wait a generous amount of
|
||||
// time before giving up.
|
||||
log(` nothing to flush yet; waiting for requests.`);
|
||||
setTimeout(tryFlush, 5);
|
||||
} else {
|
||||
if (flushed === 0) {
|
||||
log("nothing to flush; giving up");
|
||||
} else {
|
||||
log(`no more flushes after flushing ${flushed} requests`);
|
||||
}
|
||||
defer.resolve(flushed);
|
||||
}
|
||||
};
|
||||
|
||||
setTimeout(tryFlush, 0);
|
||||
|
||||
return defer.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Attempts to resolve requests/expected requests.
|
||||
* @param {string} path The path to flush (optional) default: all.
|
||||
* @return {boolean} true if something was resolved.
|
||||
*/
|
||||
_takeFromQueue: function(path) {
|
||||
let req = null;
|
||||
let i;
|
||||
let j;
|
||||
let matchingReq = null;
|
||||
let expectedReq = null;
|
||||
let testResponse = null;
|
||||
for (i = 0; i < this.requests.length; i++) {
|
||||
req = this.requests[i];
|
||||
for (j = 0; j < this.expectedRequests.length; j++) {
|
||||
expectedReq = this.expectedRequests[j];
|
||||
if (path && path !== expectedReq.path) {
|
||||
continue;
|
||||
}
|
||||
if (expectedReq.method === req.method &&
|
||||
req.path.indexOf(expectedReq.path) !== -1) {
|
||||
if (!expectedReq.data || (JSON.stringify(expectedReq.data) ===
|
||||
JSON.stringify(req.data))) {
|
||||
matchingReq = expectedReq;
|
||||
this.expectedRequests.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingReq) {
|
||||
// remove from request queue
|
||||
this.requests.splice(i, 1);
|
||||
i--;
|
||||
|
||||
for (j = 0; j < matchingReq.checks.length; j++) {
|
||||
matchingReq.checks[j](req);
|
||||
}
|
||||
testResponse = matchingReq.response;
|
||||
console.log(`${Date.now()} responding to ${matchingReq.path}`);
|
||||
let body = testResponse.body;
|
||||
if (Object.prototype.toString.call(body) == "[object Function]") {
|
||||
body = body(req.path, req.data);
|
||||
}
|
||||
req.callback(
|
||||
testResponse.err, testResponse.response, body,
|
||||
);
|
||||
matchingReq = null;
|
||||
}
|
||||
}
|
||||
if (testResponse) { // flushed something
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes sure that the SDK hasn't sent any more requests to the backend.
|
||||
*/
|
||||
verifyNoOutstandingRequests: function() {
|
||||
const firstOutstandingReq = this.requests[0] || {};
|
||||
expect(this.requests.length).toEqual(0,
|
||||
"Expected no more HTTP requests but received request to " +
|
||||
firstOutstandingReq.path,
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes sure that the test doesn't have any unresolved requests.
|
||||
*/
|
||||
verifyNoOutstandingExpectation: function() {
|
||||
const firstOutstandingExpectation = this.expectedRequests[0] || {};
|
||||
expect(this.expectedRequests.length).toEqual(0,
|
||||
"Expected to see HTTP request for " + firstOutstandingExpectation.path,
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create an expected request.
|
||||
* @param {string} method The HTTP method
|
||||
* @param {string} path The path (which can be partial)
|
||||
* @param {Object} data The expected data.
|
||||
* @return {Request} An expected request.
|
||||
*/
|
||||
when: function(method, path, data) {
|
||||
const pendingReq = new ExpectedRequest(method, path, data);
|
||||
this.expectedRequests.push(pendingReq);
|
||||
return pendingReq;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the expectation of a request.
|
||||
*
|
||||
* <p>Includes the conditions to be matched against, the checks to be made,
|
||||
* and the response to be returned.
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} method
|
||||
* @param {string} path
|
||||
* @param {object?} data
|
||||
*/
|
||||
function ExpectedRequest(method, path, data) {
|
||||
this.method = method;
|
||||
this.path = path;
|
||||
this.data = data;
|
||||
this.response = null;
|
||||
this.checks = [];
|
||||
}
|
||||
|
||||
ExpectedRequest.prototype = {
|
||||
toString: function() {
|
||||
return this.method + " " + this.path
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute a check when this request has been satisfied.
|
||||
* @param {Function} fn The function to execute.
|
||||
* @return {Request} for chaining calls.
|
||||
*/
|
||||
check: function(fn) {
|
||||
this.checks.push(fn);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Respond with the given data when this request is satisfied.
|
||||
* @param {Number} code The HTTP status code.
|
||||
* @param {Object|Function} data The HTTP JSON body. If this is a function,
|
||||
* it will be invoked when the JSON body is required (which should be returned).
|
||||
*/
|
||||
respond: function(code, data) {
|
||||
this.response = {
|
||||
response: {
|
||||
statusCode: code,
|
||||
headers: {},
|
||||
},
|
||||
body: data,
|
||||
err: null,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Fail with an Error when this request is satisfied.
|
||||
* @param {Number} code The HTTP status code.
|
||||
* @param {Error} err The error to throw (e.g. Network Error)
|
||||
*/
|
||||
fail: function(code, err) {
|
||||
this.response = {
|
||||
response: {
|
||||
statusCode: code,
|
||||
headers: {},
|
||||
},
|
||||
body: null,
|
||||
err: err,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a request made by the app.
|
||||
*
|
||||
* @constructor
|
||||
* @param {object} opts opts passed to request()
|
||||
* @param {function} callback
|
||||
*/
|
||||
function Request(opts, callback) {
|
||||
this.opts = opts;
|
||||
this.callback = callback;
|
||||
|
||||
Object.defineProperty(this, 'method', {
|
||||
get: function() {
|
||||
return opts.method;
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'path', {
|
||||
get: function() {
|
||||
return opts.uri;
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'data', {
|
||||
get: function() {
|
||||
return opts.body;
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'queryParams', {
|
||||
get: function() {
|
||||
return opts.qs;
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'headers', {
|
||||
get: function() {
|
||||
return opts.headers || {};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Request.prototype = {
|
||||
toString: function() {
|
||||
return this.method + " " + this.path;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* The HttpBackend class.
|
||||
*/
|
||||
module.exports = HttpBackend;
|
||||
@@ -34,22 +34,25 @@ export function deleteIndexedDB(dbName) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Removing indexeddb instance: ${dbName}`);
|
||||
console.log(`${Date.now()}: Removing indexeddb instance: ${dbName}`);
|
||||
const req = window.indexedDB.deleteDatabase(dbName);
|
||||
|
||||
req.onblocked = () => {
|
||||
console.log(`can't yet delete indexeddb because it is open elsewhere`);
|
||||
console.log(`${Date.now()}: can't yet delete indexeddb ${dbName} because it is open elsewhere`);
|
||||
};
|
||||
|
||||
req.onerror = (ev) => {
|
||||
reject(new Error(
|
||||
"unable to delete indexeddb: " + ev.target.error,
|
||||
`${Date.now()}: unable to delete indexeddb ${dbName}: ${ev.target.error}`,
|
||||
));
|
||||
};
|
||||
|
||||
req.onsuccess = () => {
|
||||
console.log(`Removed indexeddb instance: ${dbName}`);
|
||||
console.log(`${Date.now()}: Removed indexeddb instance: ${dbName}`);
|
||||
resolve();
|
||||
};
|
||||
}).catch((e) => {
|
||||
console.error(`${Date.now()}: Error removing indexeddb instance ${dbName}: ${e}`);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user