From b8a3ee1841f3b06a5c97016d4b75a03ab1456ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 11 Oct 2019 16:07:59 +0200 Subject: [PATCH 001/116] BasePlatform: Add prototype methods for event indexing. --- src/BasePlatform.js | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/BasePlatform.js b/src/BasePlatform.js index a97c14bf90..7f5df822e4 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -151,4 +151,44 @@ export default class BasePlatform { async setMinimizeToTrayEnabled(enabled: boolean): void { throw new Error("Unimplemented"); } + + supportsEventIndexing(): boolean { + return false; + } + + async initEventIndex(userId: string): boolean { + throw new Error("Unimplemented"); + } + + async addEventToIndex(ev: {}, profile: {}): void { + throw new Error("Unimplemented"); + } + + indexIsEmpty(): Promise { + throw new Error("Unimplemented"); + } + + async commitLiveEvents(): void { + throw new Error("Unimplemented"); + } + + async searchEventIndex(term: string): Promise<{}> { + throw new Error("Unimplemented"); + } + + async addHistoricEvents(events: [], checkpoint: {} = null, oldCheckpoint: {} = null): Promise { + throw new Error("Unimplemented"); + } + + async addCrawlerCheckpoint(checkpoint: {}): Promise<> { + throw new Error("Unimplemented"); + } + + async removeCrawlerCheckpoint(checkpoint: {}): Promise<> { + throw new Error("Unimplemented"); + } + + async deleteEventIndex(): Promise<> { + throw new Error("Unimplemented"); + } } From 9ce478cb0e29fca8bf0815c7f7a13ef29fe573fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 11 Oct 2019 16:43:53 +0200 Subject: [PATCH 002/116] MatrixChat: Create an event index and start crawling for events. This patch adds support to create an event index if the clients platform supports it and starts an event crawler. The event crawler goes through the room history of encrypted rooms and eventually indexes the whole room history of such rooms. It does this by first creating crawling checkpoints and storing them inside a database. A checkpoint consists of a room_id, direction and token. After the checkpoints are added the client starts a crawler method in the background. The crawler goes through checkpoints in a round-robin way and uses them to fetch historic room messages using the rooms/roomId/messages API endpoint. Every time messages are fetched a new checkpoint is created that will be stored in the database with the fetched events in an atomic way, the old checkpoint is deleted at the same time as well. --- src/MatrixClientPeg.js | 4 + src/components/structures/MatrixChat.js | 231 ++++++++++++++++++++++++ 2 files changed, 235 insertions(+) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index bebb254afc..5c5ee6e4ec 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -30,6 +30,7 @@ import {verificationMethods} from 'matrix-js-sdk/lib/crypto'; import MatrixClientBackedSettingsHandler from "./settings/handlers/MatrixClientBackedSettingsHandler"; import * as StorageManager from './utils/StorageManager'; import IdentityAuthClient from './IdentityAuthClient'; +import PlatformPeg from "./PlatformPeg"; interface MatrixClientCreds { homeserverUrl: string, @@ -222,6 +223,9 @@ class MatrixClientPeg { this.matrixClient = createMatrixClient(opts); + const platform = PlatformPeg.get(); + if (platform.supportsEventIndexing()) platform.initEventIndex(creds.userId); + // we're going to add eventlisteners for each matrix event tile, so the // potential number of event listeners is quite high. this.matrixClient.setMaxListeners(500); diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index da67416400..218b7e4d4e 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1262,6 +1262,7 @@ export default createReactClass({ // to do the first sync this.firstSyncComplete = false; this.firstSyncPromise = Promise.defer(); + this.crawlerChekpoints = []; const cli = MatrixClientPeg.get(); const IncomingSasDialog = sdk.getComponent('views.dialogs.IncomingSasDialog'); @@ -1287,6 +1288,75 @@ export default createReactClass({ return self._loggedInView.child.canResetTimelineInRoom(roomId); }); + cli.on('sync', async (state, prevState, data) => { + const platform = PlatformPeg.get(); + if (!platform.supportsEventIndexing()) return; + + if (prevState === null && state === "PREPARED") { + /// Load our stored checkpoints, if any. + self.crawlerChekpoints = await platform.loadCheckpoints(); + console.log("Seshat: Loaded checkpoints", + self.crawlerChekpoints); + return; + } + + if (prevState === "PREPARED" && state === "SYNCING") { + const addInitialCheckpoints = async () => { + const client = MatrixClientPeg.get(); + const rooms = client.getRooms(); + + const isRoomEncrypted = (room) => { + return client.isRoomEncrypted(room.roomId); + }; + + // We only care to crawl the encrypted rooms, non-encrytped + // rooms can use the search provided by the Homeserver. + const encryptedRooms = rooms.filter(isRoomEncrypted); + + console.log("Seshat: Adding initial crawler checkpoints"); + + // Gather the prev_batch tokens and create checkpoints for + // our message crawler. + await Promise.all(encryptedRooms.map(async (room) => { + const timeline = room.getLiveTimeline(); + const token = timeline.getPaginationToken("b"); + + console.log("Seshat: Got token for indexer", + room.roomId, token); + + const backCheckpoint = { + roomId: room.roomId, + token: token, + direction: "b", + }; + + const forwardCheckpoint = { + roomId: room.roomId, + token: token, + direction: "f", + }; + + await platform.addCrawlerCheckpoint(backCheckpoint); + await platform.addCrawlerCheckpoint(forwardCheckpoint); + self.crawlerChekpoints.push(backCheckpoint); + self.crawlerChekpoints.push(forwardCheckpoint); + })); + }; + + // If our indexer is empty we're most likely running Riot the + // first time with indexing support or running it with an + // initial sync. Add checkpoints to crawl our encrypted rooms. + const eventIndexWasEmpty = await platform.isEventIndexEmpty(); + if (eventIndexWasEmpty) await addInitialCheckpoints(); + + // Start our crawler. + const crawlerHandle = {}; + self.crawlerFunc(crawlerHandle); + self.crawlerRef = crawlerHandle; + return; + } + }); + cli.on('sync', function(state, prevState, data) { // LifecycleStore and others cannot directly subscribe to matrix client for // events because flux only allows store state changes during flux dispatches. @@ -1930,4 +2000,165 @@ export default createReactClass({ {view} ; }, + + async crawlerFunc(handle) { + // TODO either put this in a better place or find a library provided + // method that does this. + const sleep = async (ms) => { + return new Promise(resolve => setTimeout(resolve, ms)); + }; + + let cancelled = false; + + console.log("Seshat: Started crawler function"); + + const client = MatrixClientPeg.get(); + const platform = PlatformPeg.get(); + + handle.cancel = () => { + cancelled = true; + }; + + while (!cancelled) { + // This is a low priority task and we don't want to spam our + // Homeserver with /messages requests so we set a hefty 3s timeout + // here. + await sleep(3000); + + if (cancelled) { + break; + } + + const checkpoint = this.crawlerChekpoints.shift(); + + /// There is no checkpoint available currently, one may appear if + // a sync with limited room timelines happens, so go back to sleep. + if (checkpoint === undefined) { + continue; + } + + console.log("Seshat: crawling using checkpoint", checkpoint); + + // We have a checkpoint, let us fetch some messages, again, very + // conservatively to not bother our Homeserver too much. + const eventMapper = client.getEventMapper(); + // TODO we need to ensure to use member lazy loading with this + // request so we get the correct profiles. + const res = await client._createMessagesRequest(checkpoint.roomId, + checkpoint.token, 100, checkpoint.direction); + + if (res.chunk.length === 0) { + // We got to the start/end of our timeline, lets just + // delete our checkpoint and go back to sleep. + await platform.removeCrawlerCheckpoint(checkpoint); + continue; + } + + // Convert the plain JSON events into Matrix events so they get + // decrypted if necessary. + const matrixEvents = res.chunk.map(eventMapper); + const stateEvents = res.state.map(eventMapper); + + const profiles = {}; + + stateEvents.forEach(ev => { + if (ev.event.content && + ev.event.content.membership === "join") { + profiles[ev.event.sender] = { + displayname: ev.event.content.displayname, + avatar_url: ev.event.content.avatar_url, + }; + } + }); + + const decryptionPromises = []; + + matrixEvents.forEach(ev => { + if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) { + // TODO the decryption promise is a private property, this + // should either be made public or we should convert the + // event that gets fired when decryption is done into a + // promise using the once event emitter method: + // https://nodejs.org/api/events.html#events_events_once_emitter_name + decryptionPromises.push(ev._decryptionPromise); + } + }); + + // Let us wait for all the events to get decrypted. + await Promise.all(decryptionPromises); + + // We filter out events for which decryption failed, are redacted + // or aren't of a type that we know how to index. + const isValidEvent = (value) => { + return ([ + "m.room.message", + "m.room.name", + "m.room.topic", + ].indexOf(value.getType()) >= 0 + && !value.isRedacted() && !value.isDecryptionFailure() + ); + // TODO do we need to check if the event has all the valid + // attributes? + }; + + // TODO if there ar no events at this point we're missing a lot + // decryption keys, do we wan't to retry this checkpoint at a later + // stage? + const filteredEvents = matrixEvents.filter(isValidEvent); + + // Let us convert the events back into a format that Seshat can + // consume. + const events = filteredEvents.map((ev) => { + const jsonEvent = ev.toJSON(); + + let e; + if (ev.isEncrypted()) e = jsonEvent.decrypted; + else e = jsonEvent; + + let profile = {}; + if (e.sender in profiles) profile = profiles[e.sender]; + const object = { + event: e, + profile: profile, + }; + return object; + }); + + // Create a new checkpoint so we can continue crawling the room for + // messages. + const newCheckpoint = { + roomId: checkpoint.roomId, + token: res.end, + fullCrawl: checkpoint.fullCrawl, + direction: checkpoint.direction, + }; + + console.log( + "Seshat: Crawled room", + client.getRoom(checkpoint.roomId).name, + "and fetched", events.length, "events.", + ); + + try { + const eventsAlreadyAdded = await platform.addHistoricEvents( + events, newCheckpoint, checkpoint); + // If all events were already indexed we assume that we catched + // up with our index and don't need to crawl the room further. + // Let us delete the checkpoint in that case, otherwise push + // the new checkpoint to be used by the crawler. + if (eventsAlreadyAdded === true && newCheckpoint.fullCrawl !== true) { + await platform.removeCrawlerCheckpoint(newCheckpoint); + } else { + this.crawlerChekpoints.push(newCheckpoint); + } + } catch (e) { + console.log("Seshat: Error durring a crawl", e); + // An error occured, put the checkpoint back so we + // can retry. + this.crawlerChekpoints.push(checkpoint); + } + } + + console.log("Seshat: Stopping crawler function"); + }, }); From b23ba5f8811488c16412b6ebe2d141f1b9e18f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 11 Oct 2019 16:27:01 +0200 Subject: [PATCH 003/116] MatrixChat: Stop the crawler function and delete the index when logging out. --- src/components/structures/MatrixChat.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 218b7e4d4e..7eda69ad9b 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1221,7 +1221,15 @@ export default createReactClass({ /** * Called when the session is logged out */ - _onLoggedOut: function() { + _onLoggedOut: async function() { + const platform = PlatformPeg.get(); + + if (platform.supportsEventIndexing()) { + console.log("Seshat: Deleting event index."); + this.crawlerRef.cancel(); + await platform.deleteEventIndex(); + } + this.notifyNewScreen('login'); this.setStateForNewView({ view: VIEWS.LOGIN, From 5e7076e985fd95a7978099322457823f96daff8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 11 Oct 2019 16:28:36 +0200 Subject: [PATCH 004/116] MatrixChat: Add live events to the event index as well. --- src/components/structures/MatrixChat.js | 65 +++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 7eda69ad9b..5c4db4a562 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1271,6 +1271,7 @@ export default createReactClass({ this.firstSyncComplete = false; this.firstSyncPromise = Promise.defer(); this.crawlerChekpoints = []; + this.liveEventsForIndex = new Set(); const cli = MatrixClientPeg.get(); const IncomingSasDialog = sdk.getComponent('views.dialogs.IncomingSasDialog'); @@ -1363,6 +1364,14 @@ export default createReactClass({ self.crawlerRef = crawlerHandle; return; } + + if (prevState === "SYNCING" && state === "SYNCING") { + // A sync was done, presumably we queued up some live events, + // commit them now. + console.log("Seshat: Committing events"); + await platform.commitLiveEvents(); + return; + } }); cli.on('sync', function(state, prevState, data) { @@ -1447,6 +1456,44 @@ export default createReactClass({ }, null, true); }); + cli.on("Room.timeline", async (ev, room, toStartOfTimeline, removed, data) => { + const platform = PlatformPeg.get(); + if (!platform.supportsEventIndexing()) return; + + // We only index encrypted rooms locally. + if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; + + // If it isn't a live event or if it's redacted there's nothing to + // do. + if (toStartOfTimeline || !data || !data.liveEvent + || ev.isRedacted()) { + return; + } + + // If the event is not yet decrypted mark it for the + // Event.decrypted callback. + if (ev.isBeingDecrypted()) { + const eventId = ev.getId(); + self.liveEventsForIndex.add(eventId); + } else { + // If the event is decrypted or is unencrypted add it to the + // index now. + await self.addLiveEventToIndex(ev); + } + }); + + cli.on("Event.decrypted", async (ev, err) => { + const platform = PlatformPeg.get(); + if (!platform.supportsEventIndexing()) return; + + const eventId = ev.getId(); + + // If the event isn't in our live event set, ignore it. + if (!self.liveEventsForIndex.delete(eventId)) return; + if (err) return; + await self.addLiveEventToIndex(ev); + }); + cli.on("accountData", function(ev) { if (ev.getType() === 'im.vector.web.settings') { if (ev.getContent() && ev.getContent().theme) { @@ -2009,6 +2056,24 @@ export default createReactClass({ ; }, + async addLiveEventToIndex(ev) { + const platform = PlatformPeg.get(); + if (!platform.supportsEventIndexing()) return; + + if (["m.room.message", "m.room.name", "m.room.topic"] + .indexOf(ev.getType()) == -1) { + return; + } + + const e = ev.toJSON().decrypted; + const profile = { + displayname: ev.sender.rawDisplayName, + avatar_url: ev.sender.getMxcAvatarUrl(), + }; + + platform.addEventToIndex(e, profile); + }, + async crawlerFunc(handle) { // TODO either put this in a better place or find a library provided // method that does this. From 4acec19d40ba57f789f4c1293ebeb6774babc6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 11 Oct 2019 16:32:55 +0200 Subject: [PATCH 005/116] MatrixChat: Add new crawler checkpoints if there was a limited timeline. A sync call may not have all events that happened since the last time the client synced. In such a case the room is marked as limited and events need to be fetched separately. When such a sync call happens our event index will have a gap. To close the gap checkpoints are added to start crawling our room again. Unnecessary full re-crawls are prevented by checking if our current /room/roomId/messages request contains only events that were already present in our event index. --- src/components/structures/MatrixChat.js | 42 ++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 5c4db4a562..d423bbd592 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1281,8 +1281,11 @@ export default createReactClass({ // particularly noticeable when there are lots of 'limited' /sync responses // such as when laptops unsleep. // https://github.com/vector-im/riot-web/issues/3307#issuecomment-282895568 - cli.setCanResetTimelineCallback(function(roomId) { + cli.setCanResetTimelineCallback(async function(roomId) { console.log("Request to reset timeline in room ", roomId, " viewing:", self.state.currentRoomId); + // TODO is there a better place to plug this in + await self.addCheckpointForLimitedRoom(roomId); + if (roomId !== self.state.currentRoomId) { // It is safe to remove events from rooms we are not viewing. return true; @@ -2234,4 +2237,41 @@ export default createReactClass({ console.log("Seshat: Stopping crawler function"); }, + + async addCheckpointForLimitedRoom(roomId) { + const platform = PlatformPeg.get(); + if (!platform.supportsEventIndexing()) return; + if (!MatrixClientPeg.get().isRoomEncrypted(roomId)) return; + + const client = MatrixClientPeg.get(); + const room = client.getRoom(roomId); + + if (room === null) return; + + const timeline = room.getLiveTimeline(); + const token = timeline.getPaginationToken("b"); + + const backwardsCheckpoint = { + roomId: room.roomId, + token: token, + fullCrawl: false, + direction: "b", + }; + + const forwardsCheckpoint = { + roomId: room.roomId, + token: token, + fullCrawl: false, + direction: "f", + }; + + console.log("Seshat: Added checkpoint because of a limited timeline", + backwardsCheckpoint, forwardsCheckpoint); + + await platform.addCrawlerCheckpoint(backwardsCheckpoint); + await platform.addCrawlerCheckpoint(forwardsCheckpoint); + + this.crawlerChekpoints.push(backwardsCheckpoint); + this.crawlerChekpoints.push(forwardsCheckpoint); + }, }); From 3f5369183404be057af45fa248556572804727b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 11 Oct 2019 16:40:10 +0200 Subject: [PATCH 006/116] RoomView: Use platform specific search if our platform supports it. This patch extends our search to include our platform specific event index. There are 3 search scenarios and are handled differently when platform support for indexing is present: - Search a single non-encrypted room: Use the server-side search like before. - Search a single encrypted room: Search using our platform specific event index. - Search across all rooms: Search encrypted rooms using our local event index. Search non-encrypted rooms using the classic server-side search. Combine the results. The combined search will result in having twice the amount of search results since comparing the scores fairly wasn't deemed sensible. --- src/components/structures/RoomView.js | 115 ++++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 5 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 4de573479d..1b44335f51 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -34,6 +34,7 @@ import { _t } from '../../languageHandler'; import {RoomPermalinkCreator} from '../../utils/permalinks/Permalinks'; import MatrixClientPeg from '../../MatrixClientPeg'; +import PlatformPeg from "../../PlatformPeg"; import ContentMessages from '../../ContentMessages'; import Modal from '../../Modal'; import sdk from '../../index'; @@ -1140,12 +1141,116 @@ module.exports = createReactClass({ } debuglog("sending search request"); + const platform = PlatformPeg.get(); - const searchPromise = MatrixClientPeg.get().searchRoomEvents({ - filter: filter, - term: term, - }); - this._handleSearchResult(searchPromise).done(); + if (platform.supportsEventIndexing()) { + const combinedSearchFunc = async (searchTerm) => { + // Create two promises, one for the local search, one for the + // server-side search. + const client = MatrixClientPeg.get(); + const serverSidePromise = client.searchRoomEvents({ + term: searchTerm, + }); + const localPromise = localSearchFunc(searchTerm); + + // Wait for both promises to resolve. + await Promise.all([serverSidePromise, localPromise]); + + // Get both search results. + const localResult = await localPromise; + const serverSideResult = await serverSidePromise; + + // Combine the search results into one result. + const result = {}; + + // Our localResult and serverSideResult are both ordered by + // recency separetly, when we combine them the order might not + // be the right one so we need to sort them. + const compare = (a, b) => { + const aEvent = a.context.getEvent().event; + const bEvent = b.context.getEvent().event; + + if (aEvent.origin_server_ts > + bEvent.origin_server_ts) return -1; + if (aEvent.origin_server_ts < + bEvent.origin_server_ts) return 1; + return 0; + }; + + result.count = localResult.count + serverSideResult.count; + result.results = localResult.results.concat( + serverSideResult.results).sort(compare); + result.highlights = localResult.highlights.concat( + serverSideResult.highlights); + + return result; + }; + + const localSearchFunc = async (searchTerm, roomId = undefined) => { + const searchArgs = { + search_term: searchTerm, + before_limit: 1, + after_limit: 1, + order_by_recency: true, + }; + + if (roomId !== undefined) { + searchArgs.room_id = roomId; + } + + const localResult = await platform.searchEventIndex( + searchArgs); + + const response = { + search_categories: { + room_events: localResult, + }, + }; + + const emptyResult = { + results: [], + highlights: [], + }; + + // TODO is there a better way to convert our result into what + // is expected by the handler method. + const result = MatrixClientPeg.get()._processRoomEventsSearch( + emptyResult, response); + + return result; + }; + + let searchPromise; + + if (scope === "Room") { + const roomId = this.state.room.roomId; + + if (MatrixClientPeg.get().isRoomEncrypted(roomId)) { + // The search is for a single encrypted room, use our local + // search method. + searchPromise = localSearchFunc(term, roomId); + } else { + // The search is for a single non-encrypted room, use the + // server-side search. + searchPromise = MatrixClientPeg.get().searchRoomEvents({ + filter: filter, + term: term, + }); + } + } else { + // Search across all rooms, combine a server side search and a + // local search. + searchPromise = combinedSearchFunc(term); + } + + this._handleSearchResult(searchPromise).done(); + } else { + const searchPromise = MatrixClientPeg.get().searchRoomEvents({ + filter: filter, + term: term, + }); + this._handleSearchResult(searchPromise).done(); + } }, _handleSearchResult: function(searchPromise) { From 1b63886a6baca1a4191f83992609e58e5e6dc43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 18 Oct 2019 16:31:39 +0200 Subject: [PATCH 007/116] MatrixChat: Add more detailed logging to the event crawler. --- src/components/structures/MatrixChat.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index d423bbd592..3558cda586 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -2101,7 +2101,10 @@ export default createReactClass({ // here. await sleep(3000); + console.log("Seshat: Running the crawler loop."); + if (cancelled) { + console.log("Seshat: Cancelling the crawler."); break; } @@ -2124,6 +2127,7 @@ export default createReactClass({ checkpoint.token, 100, checkpoint.direction); if (res.chunk.length === 0) { + console.log("Seshat: Done with the checkpoint", checkpoint) // We got to the start/end of our timeline, lets just // delete our checkpoint and go back to sleep. await platform.removeCrawlerCheckpoint(checkpoint); @@ -2223,6 +2227,7 @@ export default createReactClass({ // Let us delete the checkpoint in that case, otherwise push // the new checkpoint to be used by the crawler. if (eventsAlreadyAdded === true && newCheckpoint.fullCrawl !== true) { + console.log("Seshat: Checkpoint had already all events added, stopping the crawl", checkpoint); await platform.removeCrawlerCheckpoint(newCheckpoint); } else { this.crawlerChekpoints.push(newCheckpoint); From 89f14e55a2bb31959893f138813957acd957e032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 18 Oct 2019 16:32:43 +0200 Subject: [PATCH 008/116] MatrixChat: Catch errors when fetching room messages in the crawler. --- src/components/structures/MatrixChat.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 3558cda586..2f9e64efa9 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -2123,8 +2123,17 @@ export default createReactClass({ const eventMapper = client.getEventMapper(); // TODO we need to ensure to use member lazy loading with this // request so we get the correct profiles. - const res = await client._createMessagesRequest(checkpoint.roomId, - checkpoint.token, 100, checkpoint.direction); + let res; + + try { + res = await client._createMessagesRequest( + checkpoint.roomId, checkpoint.token, 100, + checkpoint.direction); + } catch (e) { + console.log("Seshat: Error crawling events:", e) + this.crawlerChekpoints.push(checkpoint); + continue + } if (res.chunk.length === 0) { console.log("Seshat: Done with the checkpoint", checkpoint) From 64061173e19507ce40241989a1fb55ac705cd648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 18 Oct 2019 16:33:07 +0200 Subject: [PATCH 009/116] MatrixChat: Check if our state array is empty in the crawled messages response. --- src/components/structures/MatrixChat.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 2f9e64efa9..51cf92da5f 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -2146,7 +2146,10 @@ export default createReactClass({ // Convert the plain JSON events into Matrix events so they get // decrypted if necessary. const matrixEvents = res.chunk.map(eventMapper); - const stateEvents = res.state.map(eventMapper); + let stateEvents = []; + if (res.state !== undefined) { + stateEvents = res.state.map(eventMapper); + } const profiles = {}; From 446e21c2e129ae8387b5eaf6f6fce198731887a2 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 1 Nov 2019 10:44:30 +0000 Subject: [PATCH 010/116] Relax identity server discovery error handling If discovery results in a warning for the identity server (as in can't be found or is malformed), this allows you to continue signing in and shows the warning above the form. Fixes https://github.com/vector-im/riot-web/issues/11102 --- src/components/structures/auth/Login.js | 15 ++++++++++-- src/utils/AutoDiscoveryUtils.js | 31 +++++++++++++++---------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index ad77ed49a5..af308e1407 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -378,8 +378,19 @@ module.exports = createReactClass({ // Do a quick liveliness check on the URLs try { - await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(hsUrl, isUrl); - this.setState({serverIsAlive: true, errorText: ""}); + const { warning } = + await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(hsUrl, isUrl); + if (warning) { + this.setState({ + ...AutoDiscoveryUtils.authComponentStateForError(warning), + errorText: "", + }); + } else { + this.setState({ + serverIsAlive: true, + errorText: "", + }); + } } catch (e) { this.setState({ busy: false, diff --git a/src/utils/AutoDiscoveryUtils.js b/src/utils/AutoDiscoveryUtils.js index e94c454a3e..49898aae90 100644 --- a/src/utils/AutoDiscoveryUtils.js +++ b/src/utils/AutoDiscoveryUtils.js @@ -1,5 +1,6 @@ /* Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -33,6 +34,8 @@ export class ValidatedServerConfig { isUrl: string; isDefault: boolean; + + warning: string; } export default class AutoDiscoveryUtils { @@ -56,7 +59,14 @@ export default class AutoDiscoveryUtils { * implementation for known values. * @returns {*} The state for the component, given the error. */ - static authComponentStateForError(err: Error, pageName="login"): Object { + static authComponentStateForError(err: string | Error | null, pageName = "login"): Object { + if (!err) { + return { + serverIsAlive: true, + serverErrorIsFatal: false, + serverDeadError: null, + }; + } let title = _t("Cannot reach homeserver"); let body = _t("Ensure you have a stable internet connection, or get in touch with the server admin"); if (!AutoDiscoveryUtils.isLivelinessError(err)) { @@ -153,11 +163,9 @@ export default class AutoDiscoveryUtils { /** * Validates a server configuration, using a homeserver domain name as input. * @param {string} serverName The homeserver domain name (eg: "matrix.org") to validate. - * @param {boolean} syntaxOnly If true, errors relating to liveliness of the servers will - * not be raised. * @returns {Promise} Resolves to the validated configuration. */ - static async validateServerName(serverName: string, syntaxOnly=false): ValidatedServerConfig { + static async validateServerName(serverName: string): ValidatedServerConfig { const result = await AutoDiscovery.findClientConfig(serverName); return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result); } @@ -186,7 +194,7 @@ export default class AutoDiscoveryUtils { const defaultConfig = SdkConfig.get()["validated_server_config"]; // Validate the identity server first because an invalid identity server causes - // and invalid homeserver, which may not be picked up correctly. + // an invalid homeserver, which may not be picked up correctly. // Note: In the cases where we rely on the default IS from the config (namely // lack of identity server provided by the discovery method), we intentionally do not @@ -197,20 +205,18 @@ export default class AutoDiscoveryUtils { preferredIdentityUrl = isResult["base_url"]; } else if (isResult && isResult.state !== AutoDiscovery.PROMPT) { console.error("Error determining preferred identity server URL:", isResult); - if (!syntaxOnly || !AutoDiscoveryUtils.isLivelinessError(isResult.error)) { + if (isResult.state === AutoDiscovery.FAIL_ERROR) { if (AutoDiscovery.ALL_ERRORS.indexOf(isResult.error) !== -1) { throw newTranslatableError(isResult.error); } throw newTranslatableError(_td("Unexpected error resolving identity server configuration")); } // else the error is not related to syntax - continue anyways. - // rewrite homeserver error if we don't care about problems - if (syntaxOnly) { - hsResult.error = AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER; + // rewrite homeserver error since we don't care about problems + hsResult.error = AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER; - // Also use the user's supplied identity server if provided - if (isResult["base_url"]) preferredIdentityUrl = isResult["base_url"]; - } + // Also use the user's supplied identity server if provided + if (isResult["base_url"]) preferredIdentityUrl = isResult["base_url"]; } if (hsResult.state !== AutoDiscovery.SUCCESS) { @@ -241,6 +247,7 @@ export default class AutoDiscoveryUtils { hsNameIsDifferent: url.hostname !== preferredHomeserverName, isUrl: preferredIdentityUrl, isDefault: false, + warning: hsResult.error, }); } } From 2c5565e5020edfe0306836fb37d49a8f410df2ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 18 Oct 2019 16:36:31 +0200 Subject: [PATCH 011/116] MatrixChat: Add some missing semicolons. --- src/components/structures/MatrixChat.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 51cf92da5f..402790df98 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -2130,13 +2130,13 @@ export default createReactClass({ checkpoint.roomId, checkpoint.token, 100, checkpoint.direction); } catch (e) { - console.log("Seshat: Error crawling events:", e) + console.log("Seshat: Error crawling events:", e); this.crawlerChekpoints.push(checkpoint); continue } if (res.chunk.length === 0) { - console.log("Seshat: Done with the checkpoint", checkpoint) + console.log("Seshat: Done with the checkpoint", checkpoint); // We got to the start/end of our timeline, lets just // delete our checkpoint and go back to sleep. await platform.removeCrawlerCheckpoint(checkpoint); @@ -2239,7 +2239,8 @@ export default createReactClass({ // Let us delete the checkpoint in that case, otherwise push // the new checkpoint to be used by the crawler. if (eventsAlreadyAdded === true && newCheckpoint.fullCrawl !== true) { - console.log("Seshat: Checkpoint had already all events added, stopping the crawl", checkpoint); + console.log("Seshat: Checkpoint had already all events", + "added, stopping the crawl", checkpoint); await platform.removeCrawlerCheckpoint(newCheckpoint); } else { this.crawlerChekpoints.push(newCheckpoint); From cfdcf45ac6eb019242b5d969ce8018fae195caec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 12 Nov 2019 13:29:07 +0100 Subject: [PATCH 012/116] MatrixChat: Move the event indexing logic into separate modules. --- src/EventIndexPeg.js | 74 +++++ src/EventIndexing.js | 404 ++++++++++++++++++++++++ src/Lifecycle.js | 2 + src/MatrixClientPeg.js | 4 +- src/components/structures/MatrixChat.js | 371 ++-------------------- 5 files changed, 499 insertions(+), 356 deletions(-) create mode 100644 src/EventIndexPeg.js create mode 100644 src/EventIndexing.js diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js new file mode 100644 index 0000000000..794450e4b7 --- /dev/null +++ b/src/EventIndexPeg.js @@ -0,0 +1,74 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* + * Holds the current Platform object used by the code to do anything + * specific to the platform we're running on (eg. web, electron) + * Platforms are provided by the app layer. + * This allows the app layer to set a Platform without necessarily + * having to have a MatrixChat object + */ + +import PlatformPeg from "./PlatformPeg"; +import EventIndex from "./EventIndexing"; +import MatrixClientPeg from "./MatrixClientPeg"; + +class EventIndexPeg { + constructor() { + this.index = null; + } + + /** + * Returns the current Event index object for the application. Can be null + * if the platform doesn't support event indexing. + */ + get() { + return this.index; + } + + /** Create a new EventIndex and initialize it if the platform supports it. + * Returns true if an EventIndex was successfully initialized, false + * otherwise. + */ + async init() { + const platform = PlatformPeg.get(); + if (!platform.supportsEventIndexing()) return false; + + let index = new EventIndex(); + + const userId = MatrixClientPeg.get().getUserId(); + // TODO log errors here and return false if it errors out. + await index.init(userId); + this.index = index; + + return true + } + + async stop() { + if (this.index == null) return; + index.stopCrawler(); + } + + async deleteEventIndex() { + if (this.index == null) return; + index.deleteEventIndex(); + } +} + +if (!global.mxEventIndexPeg) { + global.mxEventIndexPeg = new EventIndexPeg(); +} +module.exports = global.mxEventIndexPeg; diff --git a/src/EventIndexing.js b/src/EventIndexing.js new file mode 100644 index 0000000000..21ee8f3da6 --- /dev/null +++ b/src/EventIndexing.js @@ -0,0 +1,404 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import PlatformPeg from "./PlatformPeg"; +import MatrixClientPeg from "./MatrixClientPeg"; + +/** + * Event indexing class that wraps the platform specific event indexing. + */ +export default class EventIndexer { + constructor() { + this.crawlerChekpoints = []; + // The time that the crawler will wait between /rooms/{room_id}/messages + // requests + this._crawler_timeout = 3000; + this._crawlerRef = null; + this.liveEventsForIndex = new Set(); + } + + async init(userId) { + const platform = PlatformPeg.get(); + if (!platform.supportsEventIndexing()) return false; + platform.initEventIndex(userId); + } + + async onSync(state, prevState, data) { + const platform = PlatformPeg.get(); + if (!platform.supportsEventIndexing()) return; + + if (prevState === null && state === "PREPARED") { + // Load our stored checkpoints, if any. + this.crawlerChekpoints = await platform.loadCheckpoints(); + console.log("Seshat: Loaded checkpoints", + this.crawlerChekpoints); + return; + } + + if (prevState === "PREPARED" && state === "SYNCING") { + const addInitialCheckpoints = async () => { + const client = MatrixClientPeg.get(); + const rooms = client.getRooms(); + + const isRoomEncrypted = (room) => { + return client.isRoomEncrypted(room.roomId); + }; + + // We only care to crawl the encrypted rooms, non-encrytped + // rooms can use the search provided by the Homeserver. + const encryptedRooms = rooms.filter(isRoomEncrypted); + + console.log("Seshat: Adding initial crawler checkpoints"); + + // Gather the prev_batch tokens and create checkpoints for + // our message crawler. + await Promise.all(encryptedRooms.map(async (room) => { + const timeline = room.getLiveTimeline(); + const token = timeline.getPaginationToken("b"); + + console.log("Seshat: Got token for indexer", + room.roomId, token); + + const backCheckpoint = { + roomId: room.roomId, + token: token, + direction: "b", + }; + + const forwardCheckpoint = { + roomId: room.roomId, + token: token, + direction: "f", + }; + + await platform.addCrawlerCheckpoint(backCheckpoint); + await platform.addCrawlerCheckpoint(forwardCheckpoint); + this.crawlerChekpoints.push(backCheckpoint); + this.crawlerChekpoints.push(forwardCheckpoint); + })); + }; + + // If our indexer is empty we're most likely running Riot the + // first time with indexing support or running it with an + // initial sync. Add checkpoints to crawl our encrypted rooms. + const eventIndexWasEmpty = await platform.isEventIndexEmpty(); + if (eventIndexWasEmpty) await addInitialCheckpoints(); + + // Start our crawler. + this.startCrawler(); + return; + } + + if (prevState === "SYNCING" && state === "SYNCING") { + // A sync was done, presumably we queued up some live events, + // commit them now. + console.log("Seshat: Committing events"); + await platform.commitLiveEvents(); + return; + } + } + + async onRoomTimeline(ev, room, toStartOfTimeline, removed, data) { + const platform = PlatformPeg.get(); + if (!platform.supportsEventIndexing()) return; + + // We only index encrypted rooms locally. + if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; + + // If it isn't a live event or if it's redacted there's nothing to + // do. + if (toStartOfTimeline || !data || !data.liveEvent + || ev.isRedacted()) { + return; + } + + // If the event is not yet decrypted mark it for the + // Event.decrypted callback. + if (ev.isBeingDecrypted()) { + const eventId = ev.getId(); + this.liveEventsForIndex.add(eventId); + } else { + // If the event is decrypted or is unencrypted add it to the + // index now. + await this.addLiveEventToIndex(ev); + } + } + + async onEventDecrypted(ev, err) { + const platform = PlatformPeg.get(); + if (!platform.supportsEventIndexing()) return; + + const eventId = ev.getId(); + + // If the event isn't in our live event set, ignore it. + if (!this.liveEventsForIndex.delete(eventId)) return; + if (err) return; + await this.addLiveEventToIndex(ev); + } + + async addLiveEventToIndex(ev) { + const platform = PlatformPeg.get(); + if (!platform.supportsEventIndexing()) return; + + if (["m.room.message", "m.room.name", "m.room.topic"] + .indexOf(ev.getType()) == -1) { + return; + } + + const e = ev.toJSON().decrypted; + const profile = { + displayname: ev.sender.rawDisplayName, + avatar_url: ev.sender.getMxcAvatarUrl(), + }; + + platform.addEventToIndex(e, profile); + } + + async crawlerFunc(handle) { + // TODO either put this in a better place or find a library provided + // method that does this. + const sleep = async (ms) => { + return new Promise(resolve => setTimeout(resolve, ms)); + }; + + let cancelled = false; + + console.log("Seshat: Started crawler function"); + + const client = MatrixClientPeg.get(); + const platform = PlatformPeg.get(); + + handle.cancel = () => { + cancelled = true; + }; + + while (!cancelled) { + // This is a low priority task and we don't want to spam our + // Homeserver with /messages requests so we set a hefty timeout + // here. + await sleep(this._crawler_timeout); + + console.log("Seshat: Running the crawler loop."); + + if (cancelled) { + console.log("Seshat: Cancelling the crawler."); + break; + } + + const checkpoint = this.crawlerChekpoints.shift(); + + /// There is no checkpoint available currently, one may appear if + // a sync with limited room timelines happens, so go back to sleep. + if (checkpoint === undefined) { + continue; + } + + console.log("Seshat: crawling using checkpoint", checkpoint); + + // We have a checkpoint, let us fetch some messages, again, very + // conservatively to not bother our Homeserver too much. + const eventMapper = client.getEventMapper(); + // TODO we need to ensure to use member lazy loading with this + // request so we get the correct profiles. + let res; + + try { + res = await client._createMessagesRequest( + checkpoint.roomId, checkpoint.token, 100, + checkpoint.direction); + } catch (e) { + console.log("Seshat: Error crawling events:", e); + this.crawlerChekpoints.push(checkpoint); + continue + } + + if (res.chunk.length === 0) { + console.log("Seshat: Done with the checkpoint", checkpoint); + // We got to the start/end of our timeline, lets just + // delete our checkpoint and go back to sleep. + await platform.removeCrawlerCheckpoint(checkpoint); + continue; + } + + // Convert the plain JSON events into Matrix events so they get + // decrypted if necessary. + const matrixEvents = res.chunk.map(eventMapper); + let stateEvents = []; + if (res.state !== undefined) { + stateEvents = res.state.map(eventMapper); + } + + const profiles = {}; + + stateEvents.forEach(ev => { + if (ev.event.content && + ev.event.content.membership === "join") { + profiles[ev.event.sender] = { + displayname: ev.event.content.displayname, + avatar_url: ev.event.content.avatar_url, + }; + } + }); + + const decryptionPromises = []; + + matrixEvents.forEach(ev => { + if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) { + // TODO the decryption promise is a private property, this + // should either be made public or we should convert the + // event that gets fired when decryption is done into a + // promise using the once event emitter method: + // https://nodejs.org/api/events.html#events_events_once_emitter_name + decryptionPromises.push(ev._decryptionPromise); + } + }); + + // Let us wait for all the events to get decrypted. + await Promise.all(decryptionPromises); + + // We filter out events for which decryption failed, are redacted + // or aren't of a type that we know how to index. + const isValidEvent = (value) => { + return ([ + "m.room.message", + "m.room.name", + "m.room.topic", + ].indexOf(value.getType()) >= 0 + && !value.isRedacted() && !value.isDecryptionFailure() + ); + // TODO do we need to check if the event has all the valid + // attributes? + }; + + // TODO if there ar no events at this point we're missing a lot + // decryption keys, do we wan't to retry this checkpoint at a later + // stage? + const filteredEvents = matrixEvents.filter(isValidEvent); + + // Let us convert the events back into a format that Seshat can + // consume. + const events = filteredEvents.map((ev) => { + const jsonEvent = ev.toJSON(); + + let e; + if (ev.isEncrypted()) e = jsonEvent.decrypted; + else e = jsonEvent; + + let profile = {}; + if (e.sender in profiles) profile = profiles[e.sender]; + const object = { + event: e, + profile: profile, + }; + return object; + }); + + // Create a new checkpoint so we can continue crawling the room for + // messages. + const newCheckpoint = { + roomId: checkpoint.roomId, + token: res.end, + fullCrawl: checkpoint.fullCrawl, + direction: checkpoint.direction, + }; + + console.log( + "Seshat: Crawled room", + client.getRoom(checkpoint.roomId).name, + "and fetched", events.length, "events.", + ); + + try { + const eventsAlreadyAdded = await platform.addHistoricEvents( + events, newCheckpoint, checkpoint); + // If all events were already indexed we assume that we catched + // up with our index and don't need to crawl the room further. + // Let us delete the checkpoint in that case, otherwise push + // the new checkpoint to be used by the crawler. + if (eventsAlreadyAdded === true && newCheckpoint.fullCrawl !== true) { + console.log("Seshat: Checkpoint had already all events", + "added, stopping the crawl", checkpoint); + await platform.removeCrawlerCheckpoint(newCheckpoint); + } else { + this.crawlerChekpoints.push(newCheckpoint); + } + } catch (e) { + console.log("Seshat: Error durring a crawl", e); + // An error occured, put the checkpoint back so we + // can retry. + this.crawlerChekpoints.push(checkpoint); + } + } + + console.log("Seshat: Stopping crawler function"); + } + + async addCheckpointForLimitedRoom(roomId) { + const platform = PlatformPeg.get(); + if (!platform.supportsEventIndexing()) return; + if (!MatrixClientPeg.get().isRoomEncrypted(roomId)) return; + + const client = MatrixClientPeg.get(); + const room = client.getRoom(roomId); + + if (room === null) return; + + const timeline = room.getLiveTimeline(); + const token = timeline.getPaginationToken("b"); + + const backwardsCheckpoint = { + roomId: room.roomId, + token: token, + fullCrawl: false, + direction: "b", + }; + + const forwardsCheckpoint = { + roomId: room.roomId, + token: token, + fullCrawl: false, + direction: "f", + }; + + console.log("Seshat: Added checkpoint because of a limited timeline", + backwardsCheckpoint, forwardsCheckpoint); + + await platform.addCrawlerCheckpoint(backwardsCheckpoint); + await platform.addCrawlerCheckpoint(forwardsCheckpoint); + + this.crawlerChekpoints.push(backwardsCheckpoint); + this.crawlerChekpoints.push(forwardsCheckpoint); + } + + async deleteEventIndex() { + if (platform.supportsEventIndexing()) { + console.log("Seshat: Deleting event index."); + this.crawlerRef.cancel(); + await platform.deleteEventIndex(); + } + } + + startCrawler() { + const crawlerHandle = {}; + this.crawlerFunc(crawlerHandle); + this.crawlerRef = crawlerHandle; + } + + stopCrawler() { + this._crawlerRef.cancel(); + this._crawlerRef = null; + } +} diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 7490c5d464..0b44f2ed84 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -20,6 +20,7 @@ import Promise from 'bluebird'; import Matrix from 'matrix-js-sdk'; import MatrixClientPeg from './MatrixClientPeg'; +import EventIndexPeg from './EventIndexPeg'; import createMatrixClient from './utils/createMatrixClient'; import Analytics from './Analytics'; import Notifier from './Notifier'; @@ -587,6 +588,7 @@ async function startMatrixClient(startSyncing=true) { if (startSyncing) { await MatrixClientPeg.start(); + await EventIndexPeg.init(); } else { console.warn("Caller requested only auxiliary services be started"); await MatrixClientPeg.assign(); diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 5c5ee6e4ec..6c5b465bb0 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -31,6 +31,7 @@ import MatrixClientBackedSettingsHandler from "./settings/handlers/MatrixClientB import * as StorageManager from './utils/StorageManager'; import IdentityAuthClient from './IdentityAuthClient'; import PlatformPeg from "./PlatformPeg"; +import EventIndexPeg from "./EventIndexPeg"; interface MatrixClientCreds { homeserverUrl: string, @@ -223,9 +224,6 @@ class MatrixClientPeg { this.matrixClient = createMatrixClient(opts); - const platform = PlatformPeg.get(); - if (platform.supportsEventIndexing()) platform.initEventIndex(creds.userId); - // we're going to add eventlisteners for each matrix event tile, so the // potential number of event listeners is quite high. this.matrixClient.setMaxListeners(500); diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 402790df98..d006247151 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -31,6 +31,7 @@ import Analytics from "../../Analytics"; import { DecryptionFailureTracker } from "../../DecryptionFailureTracker"; import MatrixClientPeg from "../../MatrixClientPeg"; import PlatformPeg from "../../PlatformPeg"; +import EventIndexPeg from "../../EventIndexPeg"; import SdkConfig from "../../SdkConfig"; import * as RoomListSorter from "../../RoomListSorter"; import dis from "../../dispatcher"; @@ -1224,12 +1225,6 @@ export default createReactClass({ _onLoggedOut: async function() { const platform = PlatformPeg.get(); - if (platform.supportsEventIndexing()) { - console.log("Seshat: Deleting event index."); - this.crawlerRef.cancel(); - await platform.deleteEventIndex(); - } - this.notifyNewScreen('login'); this.setStateForNewView({ view: VIEWS.LOGIN, @@ -1270,8 +1265,6 @@ export default createReactClass({ // to do the first sync this.firstSyncComplete = false; this.firstSyncPromise = Promise.defer(); - this.crawlerChekpoints = []; - this.liveEventsForIndex = new Set(); const cli = MatrixClientPeg.get(); const IncomingSasDialog = sdk.getComponent('views.dialogs.IncomingSasDialog'); @@ -1284,7 +1277,10 @@ export default createReactClass({ cli.setCanResetTimelineCallback(async function(roomId) { console.log("Request to reset timeline in room ", roomId, " viewing:", self.state.currentRoomId); // TODO is there a better place to plug this in - await self.addCheckpointForLimitedRoom(roomId); + const eventIndex = EventIndexPeg.get(); + if (eventIndex !== null) { + await eventIndex.addCheckpointForLimitedRoom(roomId); + } if (roomId !== self.state.currentRoomId) { // It is safe to remove events from rooms we are not viewing. @@ -1301,80 +1297,21 @@ export default createReactClass({ }); cli.on('sync', async (state, prevState, data) => { - const platform = PlatformPeg.get(); - if (!platform.supportsEventIndexing()) return; + const eventIndex = EventIndexPeg.get(); + if (eventIndex === null) return; + await eventIndex.onSync(state, prevState, data); + }); - if (prevState === null && state === "PREPARED") { - /// Load our stored checkpoints, if any. - self.crawlerChekpoints = await platform.loadCheckpoints(); - console.log("Seshat: Loaded checkpoints", - self.crawlerChekpoints); - return; - } + cli.on("Room.timeline", async (ev, room, toStartOfTimeline, removed, data) => { + const eventIndex = EventIndexPeg.get(); + if (eventIndex === null) return; + await eventIndex.onRoomTimeline(ev, room, toStartOfTimeline, removed, data); + }); - if (prevState === "PREPARED" && state === "SYNCING") { - const addInitialCheckpoints = async () => { - const client = MatrixClientPeg.get(); - const rooms = client.getRooms(); - - const isRoomEncrypted = (room) => { - return client.isRoomEncrypted(room.roomId); - }; - - // We only care to crawl the encrypted rooms, non-encrytped - // rooms can use the search provided by the Homeserver. - const encryptedRooms = rooms.filter(isRoomEncrypted); - - console.log("Seshat: Adding initial crawler checkpoints"); - - // Gather the prev_batch tokens and create checkpoints for - // our message crawler. - await Promise.all(encryptedRooms.map(async (room) => { - const timeline = room.getLiveTimeline(); - const token = timeline.getPaginationToken("b"); - - console.log("Seshat: Got token for indexer", - room.roomId, token); - - const backCheckpoint = { - roomId: room.roomId, - token: token, - direction: "b", - }; - - const forwardCheckpoint = { - roomId: room.roomId, - token: token, - direction: "f", - }; - - await platform.addCrawlerCheckpoint(backCheckpoint); - await platform.addCrawlerCheckpoint(forwardCheckpoint); - self.crawlerChekpoints.push(backCheckpoint); - self.crawlerChekpoints.push(forwardCheckpoint); - })); - }; - - // If our indexer is empty we're most likely running Riot the - // first time with indexing support or running it with an - // initial sync. Add checkpoints to crawl our encrypted rooms. - const eventIndexWasEmpty = await platform.isEventIndexEmpty(); - if (eventIndexWasEmpty) await addInitialCheckpoints(); - - // Start our crawler. - const crawlerHandle = {}; - self.crawlerFunc(crawlerHandle); - self.crawlerRef = crawlerHandle; - return; - } - - if (prevState === "SYNCING" && state === "SYNCING") { - // A sync was done, presumably we queued up some live events, - // commit them now. - console.log("Seshat: Committing events"); - await platform.commitLiveEvents(); - return; - } + cli.on("Event.decrypted", async (ev, err) => { + const eventIndex = EventIndexPeg.get(); + if (eventIndex === null) return; + await eventIndex.onEventDecrypted(ev, err); }); cli.on('sync', function(state, prevState, data) { @@ -1459,44 +1396,6 @@ export default createReactClass({ }, null, true); }); - cli.on("Room.timeline", async (ev, room, toStartOfTimeline, removed, data) => { - const platform = PlatformPeg.get(); - if (!platform.supportsEventIndexing()) return; - - // We only index encrypted rooms locally. - if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; - - // If it isn't a live event or if it's redacted there's nothing to - // do. - if (toStartOfTimeline || !data || !data.liveEvent - || ev.isRedacted()) { - return; - } - - // If the event is not yet decrypted mark it for the - // Event.decrypted callback. - if (ev.isBeingDecrypted()) { - const eventId = ev.getId(); - self.liveEventsForIndex.add(eventId); - } else { - // If the event is decrypted or is unencrypted add it to the - // index now. - await self.addLiveEventToIndex(ev); - } - }); - - cli.on("Event.decrypted", async (ev, err) => { - const platform = PlatformPeg.get(); - if (!platform.supportsEventIndexing()) return; - - const eventId = ev.getId(); - - // If the event isn't in our live event set, ignore it. - if (!self.liveEventsForIndex.delete(eventId)) return; - if (err) return; - await self.addLiveEventToIndex(ev); - }); - cli.on("accountData", function(ev) { if (ev.getType() === 'im.vector.web.settings') { if (ev.getContent() && ev.getContent().theme) { @@ -2058,238 +1957,4 @@ export default createReactClass({ {view} ; }, - - async addLiveEventToIndex(ev) { - const platform = PlatformPeg.get(); - if (!platform.supportsEventIndexing()) return; - - if (["m.room.message", "m.room.name", "m.room.topic"] - .indexOf(ev.getType()) == -1) { - return; - } - - const e = ev.toJSON().decrypted; - const profile = { - displayname: ev.sender.rawDisplayName, - avatar_url: ev.sender.getMxcAvatarUrl(), - }; - - platform.addEventToIndex(e, profile); - }, - - async crawlerFunc(handle) { - // TODO either put this in a better place or find a library provided - // method that does this. - const sleep = async (ms) => { - return new Promise(resolve => setTimeout(resolve, ms)); - }; - - let cancelled = false; - - console.log("Seshat: Started crawler function"); - - const client = MatrixClientPeg.get(); - const platform = PlatformPeg.get(); - - handle.cancel = () => { - cancelled = true; - }; - - while (!cancelled) { - // This is a low priority task and we don't want to spam our - // Homeserver with /messages requests so we set a hefty 3s timeout - // here. - await sleep(3000); - - console.log("Seshat: Running the crawler loop."); - - if (cancelled) { - console.log("Seshat: Cancelling the crawler."); - break; - } - - const checkpoint = this.crawlerChekpoints.shift(); - - /// There is no checkpoint available currently, one may appear if - // a sync with limited room timelines happens, so go back to sleep. - if (checkpoint === undefined) { - continue; - } - - console.log("Seshat: crawling using checkpoint", checkpoint); - - // We have a checkpoint, let us fetch some messages, again, very - // conservatively to not bother our Homeserver too much. - const eventMapper = client.getEventMapper(); - // TODO we need to ensure to use member lazy loading with this - // request so we get the correct profiles. - let res; - - try { - res = await client._createMessagesRequest( - checkpoint.roomId, checkpoint.token, 100, - checkpoint.direction); - } catch (e) { - console.log("Seshat: Error crawling events:", e); - this.crawlerChekpoints.push(checkpoint); - continue - } - - if (res.chunk.length === 0) { - console.log("Seshat: Done with the checkpoint", checkpoint); - // We got to the start/end of our timeline, lets just - // delete our checkpoint and go back to sleep. - await platform.removeCrawlerCheckpoint(checkpoint); - continue; - } - - // Convert the plain JSON events into Matrix events so they get - // decrypted if necessary. - const matrixEvents = res.chunk.map(eventMapper); - let stateEvents = []; - if (res.state !== undefined) { - stateEvents = res.state.map(eventMapper); - } - - const profiles = {}; - - stateEvents.forEach(ev => { - if (ev.event.content && - ev.event.content.membership === "join") { - profiles[ev.event.sender] = { - displayname: ev.event.content.displayname, - avatar_url: ev.event.content.avatar_url, - }; - } - }); - - const decryptionPromises = []; - - matrixEvents.forEach(ev => { - if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) { - // TODO the decryption promise is a private property, this - // should either be made public or we should convert the - // event that gets fired when decryption is done into a - // promise using the once event emitter method: - // https://nodejs.org/api/events.html#events_events_once_emitter_name - decryptionPromises.push(ev._decryptionPromise); - } - }); - - // Let us wait for all the events to get decrypted. - await Promise.all(decryptionPromises); - - // We filter out events for which decryption failed, are redacted - // or aren't of a type that we know how to index. - const isValidEvent = (value) => { - return ([ - "m.room.message", - "m.room.name", - "m.room.topic", - ].indexOf(value.getType()) >= 0 - && !value.isRedacted() && !value.isDecryptionFailure() - ); - // TODO do we need to check if the event has all the valid - // attributes? - }; - - // TODO if there ar no events at this point we're missing a lot - // decryption keys, do we wan't to retry this checkpoint at a later - // stage? - const filteredEvents = matrixEvents.filter(isValidEvent); - - // Let us convert the events back into a format that Seshat can - // consume. - const events = filteredEvents.map((ev) => { - const jsonEvent = ev.toJSON(); - - let e; - if (ev.isEncrypted()) e = jsonEvent.decrypted; - else e = jsonEvent; - - let profile = {}; - if (e.sender in profiles) profile = profiles[e.sender]; - const object = { - event: e, - profile: profile, - }; - return object; - }); - - // Create a new checkpoint so we can continue crawling the room for - // messages. - const newCheckpoint = { - roomId: checkpoint.roomId, - token: res.end, - fullCrawl: checkpoint.fullCrawl, - direction: checkpoint.direction, - }; - - console.log( - "Seshat: Crawled room", - client.getRoom(checkpoint.roomId).name, - "and fetched", events.length, "events.", - ); - - try { - const eventsAlreadyAdded = await platform.addHistoricEvents( - events, newCheckpoint, checkpoint); - // If all events were already indexed we assume that we catched - // up with our index and don't need to crawl the room further. - // Let us delete the checkpoint in that case, otherwise push - // the new checkpoint to be used by the crawler. - if (eventsAlreadyAdded === true && newCheckpoint.fullCrawl !== true) { - console.log("Seshat: Checkpoint had already all events", - "added, stopping the crawl", checkpoint); - await platform.removeCrawlerCheckpoint(newCheckpoint); - } else { - this.crawlerChekpoints.push(newCheckpoint); - } - } catch (e) { - console.log("Seshat: Error durring a crawl", e); - // An error occured, put the checkpoint back so we - // can retry. - this.crawlerChekpoints.push(checkpoint); - } - } - - console.log("Seshat: Stopping crawler function"); - }, - - async addCheckpointForLimitedRoom(roomId) { - const platform = PlatformPeg.get(); - if (!platform.supportsEventIndexing()) return; - if (!MatrixClientPeg.get().isRoomEncrypted(roomId)) return; - - const client = MatrixClientPeg.get(); - const room = client.getRoom(roomId); - - if (room === null) return; - - const timeline = room.getLiveTimeline(); - const token = timeline.getPaginationToken("b"); - - const backwardsCheckpoint = { - roomId: room.roomId, - token: token, - fullCrawl: false, - direction: "b", - }; - - const forwardsCheckpoint = { - roomId: room.roomId, - token: token, - fullCrawl: false, - direction: "f", - }; - - console.log("Seshat: Added checkpoint because of a limited timeline", - backwardsCheckpoint, forwardsCheckpoint); - - await platform.addCrawlerCheckpoint(backwardsCheckpoint); - await platform.addCrawlerCheckpoint(forwardsCheckpoint); - - this.crawlerChekpoints.push(backwardsCheckpoint); - this.crawlerChekpoints.push(forwardsCheckpoint); - }, }); From e296fd05c0048e95a98c8777209ecb2990d787f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 12 Nov 2019 15:39:26 +0100 Subject: [PATCH 013/116] RoomView: Move the search logic into a separate module. --- src/EventIndexing.js | 5 + src/Searching.js | 137 ++++++++++++++++++++++++++ src/components/structures/RoomView.js | 125 +---------------------- 3 files changed, 147 insertions(+), 120 deletions(-) create mode 100644 src/Searching.js diff --git a/src/EventIndexing.js b/src/EventIndexing.js index 21ee8f3da6..29f9c48842 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -401,4 +401,9 @@ export default class EventIndexer { this._crawlerRef.cancel(); this._crawlerRef = null; } + + async search(searchArgs) { + const platform = PlatformPeg.get(); + return platform.searchEventIndex(searchArgs) + } } diff --git a/src/Searching.js b/src/Searching.js new file mode 100644 index 0000000000..cd06d9bc67 --- /dev/null +++ b/src/Searching.js @@ -0,0 +1,137 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import EventIndexPeg from "./EventIndexPeg"; +import MatrixClientPeg from "./MatrixClientPeg"; + +function serverSideSearch(term, roomId = undefined) { + let filter; + if (roomId !== undefined) { + filter = { + // XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :( + rooms: [roomId], + }; + } + + let searchPromise = MatrixClientPeg.get().searchRoomEvents({ + filter: filter, + term: term, + }); + + return searchPromise; +} + +function eventIndexSearch(term, roomId = undefined) { + const combinedSearchFunc = async (searchTerm) => { + // Create two promises, one for the local search, one for the + // server-side search. + const client = MatrixClientPeg.get(); + const serverSidePromise = serverSideSearch(searchTerm); + const localPromise = localSearchFunc(searchTerm); + + // Wait for both promises to resolve. + await Promise.all([serverSidePromise, localPromise]); + + // Get both search results. + const localResult = await localPromise; + const serverSideResult = await serverSidePromise; + + // Combine the search results into one result. + const result = {}; + + // Our localResult and serverSideResult are both ordered by + // recency separetly, when we combine them the order might not + // be the right one so we need to sort them. + const compare = (a, b) => { + const aEvent = a.context.getEvent().event; + const bEvent = b.context.getEvent().event; + + if (aEvent.origin_server_ts > + bEvent.origin_server_ts) return -1; + if (aEvent.origin_server_ts < + bEvent.origin_server_ts) return 1; + return 0; + }; + + result.count = localResult.count + serverSideResult.count; + result.results = localResult.results.concat( + serverSideResult.results).sort(compare); + result.highlights = localResult.highlights.concat( + serverSideResult.highlights); + + return result; + }; + + const localSearchFunc = async (searchTerm, roomId = undefined) => { + const searchArgs = { + search_term: searchTerm, + before_limit: 1, + after_limit: 1, + order_by_recency: true, + }; + + if (roomId !== undefined) { + searchArgs.room_id = roomId; + } + + const eventIndex = EventIndexPeg.get(); + + const localResult = await eventIndex.search(searchArgs); + + const response = { + search_categories: { + room_events: localResult, + }, + }; + + const emptyResult = { + results: [], + highlights: [], + }; + + const result = MatrixClientPeg.get()._processRoomEventsSearch( + emptyResult, response); + + return result; + }; + + let searchPromise; + + if (roomId !== undefined) { + if (MatrixClientPeg.get().isRoomEncrypted(roomId)) { + // The search is for a single encrypted room, use our local + // search method. + searchPromise = localSearchFunc(term, roomId); + } else { + // The search is for a single non-encrypted room, use the + // server-side search. + searchPromise = serverSideSearch(term, roomId); + } + } else { + // Search across all rooms, combine a server side search and a + // local search. + searchPromise = combinedSearchFunc(term); + } + + return searchPromise +} + +export default function eventSearch(term, roomId = undefined) { + const eventIndex = EventIndexPeg.get(); + + if (eventIndex === null) return serverSideSearch(term, roomId); + else return eventIndexSearch(term, roomId); +} diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 1b44335f51..9fe54ad164 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -34,7 +34,6 @@ import { _t } from '../../languageHandler'; import {RoomPermalinkCreator} from '../../utils/permalinks/Permalinks'; import MatrixClientPeg from '../../MatrixClientPeg'; -import PlatformPeg from "../../PlatformPeg"; import ContentMessages from '../../ContentMessages'; import Modal from '../../Modal'; import sdk from '../../index'; @@ -44,6 +43,7 @@ import Tinter from '../../Tinter'; import rate_limited_func from '../../ratelimitedfunc'; import ObjectUtils from '../../ObjectUtils'; import * as Rooms from '../../Rooms'; +import eventSearch from '../../Searching'; import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard'; @@ -1130,127 +1130,12 @@ module.exports = createReactClass({ // todo: should cancel any previous search requests. this.searchId = new Date().getTime(); - let filter; - if (scope === "Room") { - filter = { - // XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :( - rooms: [ - this.state.room.roomId, - ], - }; - } + let roomId; + if (scope === "Room") roomId = this.state.room.roomId, debuglog("sending search request"); - const platform = PlatformPeg.get(); - - if (platform.supportsEventIndexing()) { - const combinedSearchFunc = async (searchTerm) => { - // Create two promises, one for the local search, one for the - // server-side search. - const client = MatrixClientPeg.get(); - const serverSidePromise = client.searchRoomEvents({ - term: searchTerm, - }); - const localPromise = localSearchFunc(searchTerm); - - // Wait for both promises to resolve. - await Promise.all([serverSidePromise, localPromise]); - - // Get both search results. - const localResult = await localPromise; - const serverSideResult = await serverSidePromise; - - // Combine the search results into one result. - const result = {}; - - // Our localResult and serverSideResult are both ordered by - // recency separetly, when we combine them the order might not - // be the right one so we need to sort them. - const compare = (a, b) => { - const aEvent = a.context.getEvent().event; - const bEvent = b.context.getEvent().event; - - if (aEvent.origin_server_ts > - bEvent.origin_server_ts) return -1; - if (aEvent.origin_server_ts < - bEvent.origin_server_ts) return 1; - return 0; - }; - - result.count = localResult.count + serverSideResult.count; - result.results = localResult.results.concat( - serverSideResult.results).sort(compare); - result.highlights = localResult.highlights.concat( - serverSideResult.highlights); - - return result; - }; - - const localSearchFunc = async (searchTerm, roomId = undefined) => { - const searchArgs = { - search_term: searchTerm, - before_limit: 1, - after_limit: 1, - order_by_recency: true, - }; - - if (roomId !== undefined) { - searchArgs.room_id = roomId; - } - - const localResult = await platform.searchEventIndex( - searchArgs); - - const response = { - search_categories: { - room_events: localResult, - }, - }; - - const emptyResult = { - results: [], - highlights: [], - }; - - // TODO is there a better way to convert our result into what - // is expected by the handler method. - const result = MatrixClientPeg.get()._processRoomEventsSearch( - emptyResult, response); - - return result; - }; - - let searchPromise; - - if (scope === "Room") { - const roomId = this.state.room.roomId; - - if (MatrixClientPeg.get().isRoomEncrypted(roomId)) { - // The search is for a single encrypted room, use our local - // search method. - searchPromise = localSearchFunc(term, roomId); - } else { - // The search is for a single non-encrypted room, use the - // server-side search. - searchPromise = MatrixClientPeg.get().searchRoomEvents({ - filter: filter, - term: term, - }); - } - } else { - // Search across all rooms, combine a server side search and a - // local search. - searchPromise = combinedSearchFunc(term); - } - - this._handleSearchResult(searchPromise).done(); - } else { - const searchPromise = MatrixClientPeg.get().searchRoomEvents({ - filter: filter, - term: term, - }); - this._handleSearchResult(searchPromise).done(); - } + const searchPromise = eventSearch(term, roomId); + this._handleSearchResult(searchPromise).done(); }, _handleSearchResult: function(searchPromise) { From d911055f5d8016ebd2f036d68a9c1ee3f7343af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 12 Nov 2019 15:39:54 +0100 Subject: [PATCH 014/116] MatrixChat: Move the indexing limited room logic to a different event. --- src/components/structures/MatrixChat.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index d006247151..0d3d5abd55 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1276,11 +1276,6 @@ export default createReactClass({ // https://github.com/vector-im/riot-web/issues/3307#issuecomment-282895568 cli.setCanResetTimelineCallback(async function(roomId) { console.log("Request to reset timeline in room ", roomId, " viewing:", self.state.currentRoomId); - // TODO is there a better place to plug this in - const eventIndex = EventIndexPeg.get(); - if (eventIndex !== null) { - await eventIndex.addCheckpointForLimitedRoom(roomId); - } if (roomId !== self.state.currentRoomId) { // It is safe to remove events from rooms we are not viewing. @@ -1314,6 +1309,13 @@ export default createReactClass({ await eventIndex.onEventDecrypted(ev, err); }); + cli.on("Room.timelineReset", async (room, timelineSet, resetAllTimelines) => { + const eventIndex = EventIndexPeg.get(); + if (eventIndex === null) return; + if (resetAllTimelines === true) return; + await eventIndex.addCheckpointForLimitedRoom(roomId); + }); + cli.on('sync', function(state, prevState, data) { // LifecycleStore and others cannot directly subscribe to matrix client for // events because flux only allows store state changes during flux dispatches. From ecbc47c5488bf60b5cc068b09d4a51672b9a5c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 12 Nov 2019 15:40:49 +0100 Subject: [PATCH 015/116] EventIndexing: Rename the stop method. --- src/EventIndexPeg.js | 9 +++++---- src/EventIndexing.js | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index 794450e4b7..86fb889c7a 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -57,13 +57,14 @@ class EventIndexPeg { return true } - async stop() { - if (this.index == null) return; - index.stopCrawler(); + stop() { + if (this.index === null) return; + index.stop(); + this.index = null; } async deleteEventIndex() { - if (this.index == null) return; + if (this.index === null) return; index.deleteEventIndex(); } } diff --git a/src/EventIndexing.js b/src/EventIndexing.js index 29f9c48842..92a3a5a1f8 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -34,6 +34,7 @@ export default class EventIndexer { const platform = PlatformPeg.get(); if (!platform.supportsEventIndexing()) return false; platform.initEventIndex(userId); + return true; } async onSync(state, prevState, data) { @@ -397,7 +398,7 @@ export default class EventIndexer { this.crawlerRef = crawlerHandle; } - stopCrawler() { + stop() { this._crawlerRef.cancel(); this._crawlerRef = null; } From d69eb78b661764e9241d14a0c08dff23906245c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 12 Nov 2019 15:41:14 +0100 Subject: [PATCH 016/116] EventIndexing: Add a missing platform getting. --- src/EventIndexing.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EventIndexing.js b/src/EventIndexing.js index 92a3a5a1f8..ebd2ffe983 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -385,6 +385,7 @@ export default class EventIndexer { } async deleteEventIndex() { + const platform = PlatformPeg.get(); if (platform.supportsEventIndexing()) { console.log("Seshat: Deleting event index."); this.crawlerRef.cancel(); From 3502454c615f1a7bc74588f3512661278604d2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 12 Nov 2019 15:58:38 +0100 Subject: [PATCH 017/116] LifeCycle: Stop the crawler and delete the index when whe log out. --- src/Lifecycle.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 0b44f2ed84..7360cd3231 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -613,6 +613,7 @@ export function onLoggedOut() { // so that React components unmount first. This avoids React soft crashes // that can occur when components try to use a null client. dis.dispatch({action: 'on_logged_out'}); + EventIndexPeg.deleteEventIndex().done(); stopMatrixClient(); _clearStorage().done(); } @@ -648,6 +649,7 @@ export function stopMatrixClient(unsetClient=true) { ActiveWidgetStore.stop(); IntegrationManagers.sharedInstance().stopWatching(); if (DMRoomMap.shared()) DMRoomMap.shared().stop(); + EventIndexPeg.stop(); const cli = MatrixClientPeg.get(); if (cli) { cli.stopClient(); From 87f9e0d5650b42383f85804623c2b40aaba50854 Mon Sep 17 00:00:00 2001 From: take100yen Date: Tue, 12 Nov 2019 11:28:20 +0000 Subject: [PATCH 018/116] Translated using Weblate (Japanese) Currently translated at 62.7% (1168 of 1864 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/ja/ --- src/i18n/strings/ja.json | 53 +++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/ja.json b/src/i18n/strings/ja.json index 5e3fecaed9..22b092c06b 100644 --- a/src/i18n/strings/ja.json +++ b/src/i18n/strings/ja.json @@ -17,12 +17,12 @@ "Hide read receipts": "既読を表示しない", "Invited": "招待中", "%(displayName)s is typing": "%(displayName)s 文字入力中", - "%(targetName)s joined the room.": "%(targetName)s 部屋に参加しました。", + "%(targetName)s joined the room.": "%(targetName)s が部屋に参加しました。", "Low priority": "低優先度", "Mute": "通知しない", "New password": "新しいパスワード", "Notifications": "通知", - "Cancel": "取消", + "Cancel": "キャンセル", "Create new room": "新しい部屋を作成", "Room directory": "公開部屋一覧", "Search": "検索", @@ -490,7 +490,7 @@ "Delete %(count)s devices|one": "端末を削除する", "Device ID": "端末ID", "Device Name": "端末名", - "Last seen": "最後のシーン", + "Last seen": "最後に表示した時刻", "Select devices": "端末の選択", "Failed to set display name": "表示名の設定に失敗しました", "Disable Notifications": "通知を無効にする", @@ -1162,8 +1162,8 @@ "No media permissions": "メディア権限がありません", "You may need to manually permit Riot to access your microphone/webcam": "自分のマイク/ウェブカメラにアクセスするために手動でRiotを許可する必要があるかもしれません", "Missing Media Permissions, click here to request.": "メディアアクセス権がありません。ここをクリックしてリクエストしてください。", - "No Audio Outputs detected": "オーディオ出力が検出されなかった", - "Default Device": "標準端末", + "No Audio Outputs detected": "オーディオ出力が検出されませんでした", + "Default Device": "既定のデバイス", "Audio Output": "音声出力", "VoIP": "VoIP", "Email": "Eメール", @@ -1380,5 +1380,46 @@ "Enable Community Filter Panel": "コミュニティーフィルターパネルを有効にする", "Show recently visited rooms above the room list": "最近訪問した部屋をリストの上位に表示する", "Low bandwidth mode": "低帯域通信モード", - "Trust & Devices": "信頼と端末" + "Trust & Devices": "信頼と端末", + "Public Name": "パブリック名", + "Upload profile picture": "プロフィール画像をアップロード", + "Upgrade to your own domain": "あなた自身のドメインにアップグレード", + "Phone numbers": "電話番号", + "Set a new account password...": "アカウントの新しいパスワードを設定...", + "Language and region": "言語と地域", + "Theme": "テーマ", + "General": "一般", + "Preferences": "環境設定", + "Security & Privacy": "セキュリティとプライバシー", + "A device's public name is visible to people you communicate with": "デバイスのパブリック名はあなたと会話するすべての人が閲覧できます", + "Room information": "部屋の情報", + "Internal room ID:": "内部 部屋ID:", + "Room version": "部屋のバージョン", + "Room version:": "部屋のバージョン:", + "Developer options": "開発者オプション", + "Room Addresses": "部屋のアドレス", + "Sounds": "音", + "Notification sound": "通知音", + "Reset": "リセット", + "Set a new custom sound": "カスタム音を設定", + "Browse": "参照", + "Roles & Permissions": "役割と権限", + "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "誰が履歴を読み取れるかに関する変更は、今後送信されるメッセージにのみ適用されます。既に存在する履歴の表示は変更されません。", + "Encryption": "暗号化", + "Once enabled, encryption cannot be disabled.": "もし有効化された場合、二度と無効化できません。", + "Encrypted": "暗号化", + "Email Address": "メールアドレス", + "Main address": "メインアドレス", + "Join": "参加", + "This room is private, and can only be joined by invitation.": "この部屋はプライベートです。招待によってのみ参加できます。", + "Create a private room": "プライベートな部屋を作成", + "Topic (optional)": "トピック (オプション)", + "Hide advanced": "高度な設定を非表示", + "Show advanced": "高度な設定を表示", + "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "他の Matrix ホームサーバーからの参加を禁止する (この設定はあとから変更できません!)", + "Room Settings - %(roomName)s": "部屋の設定 - %(roomName)s", + "Explore": "探索", + "Filter": "フィルター", + "Find a room… (e.g. %(exampleRoom)s)": "部屋を探す… (例: %(exampleRoom)s)", + "If you can't find the room you're looking for, ask for an invite or Create a new room.": "もしお探しの部屋が見つからない場合、招待してもらうか部屋を作成しましょう。" } From 5fa4bc7192d22cdb0c811f676b9479b3ff99ad6a Mon Sep 17 00:00:00 2001 From: shuji narazaki Date: Tue, 12 Nov 2019 11:52:45 +0000 Subject: [PATCH 019/116] Translated using Weblate (Japanese) Currently translated at 62.7% (1168 of 1864 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/ja/ --- src/i18n/strings/ja.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/ja.json b/src/i18n/strings/ja.json index 22b092c06b..54e2c29a21 100644 --- a/src/i18n/strings/ja.json +++ b/src/i18n/strings/ja.json @@ -389,8 +389,8 @@ "%(senderName)s kicked %(targetName)s.": "%(senderName)s は %(targetName)s を追放しました。", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s は %(targetName)s の招待を撤回しました。", "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s はトピックを \"%(topic)s\" に変更しました。", - "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s はルーム名を削除しました。", - "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s はルーム名を %(roomName)s に変更しました。", + "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s は部屋名を削除しました。", + "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s は部屋名を %(roomName)s に変更しました。", "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s はイメージを送信しました。", "%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|other": "%(senderName)s はこの部屋のアドレスとして %(addedAddresses)s を追加しました。", "%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|one": "%(senderName)s はこの部屋のアドレスとして %(addedAddresses)s を追加しました。", @@ -1421,5 +1421,6 @@ "Explore": "探索", "Filter": "フィルター", "Find a room… (e.g. %(exampleRoom)s)": "部屋を探す… (例: %(exampleRoom)s)", - "If you can't find the room you're looking for, ask for an invite or Create a new room.": "もしお探しの部屋が見つからない場合、招待してもらうか部屋を作成しましょう。" + "If you can't find the room you're looking for, ask for an invite or Create a new room.": "もしお探しの部屋が見つからない場合、招待してもらうか部屋を作成しましょう。", + "Enable room encryption": "部屋の暗号化を有効にする" } From 523e17838de98b9a0744b229db36741a21d22996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=ED=83=9C=EC=84=AD?= Date: Tue, 12 Nov 2019 12:15:45 +0000 Subject: [PATCH 020/116] Translated using Weblate (Korean) Currently translated at 100.0% (1864 of 1864 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/ko/ --- src/i18n/strings/ko.json | 47 +++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/i18n/strings/ko.json b/src/i18n/strings/ko.json index 54425657cf..3d01d5be67 100644 --- a/src/i18n/strings/ko.json +++ b/src/i18n/strings/ko.json @@ -128,8 +128,8 @@ "Deactivate Account": "계정 비활성화", "Deactivate my account": "계정 정지하기", "Decline": "거절", - "Decrypt %(text)s": "%(text)s 해독", - "Decryption error": "암호 해독 오류", + "Decrypt %(text)s": "%(text)s 복호화", + "Decryption error": "암호 복호화 오류", "Delete": "지우기", "Deops user with given id": "받은 ID로 사용자의 등급을 낮추기", "Device ID:": "기기 ID:", @@ -156,7 +156,7 @@ "End-to-end encryption is in beta and may not be reliable": "종단 간 암호화는 베타 테스트 중이며 신뢰하기 힘들 수 있습니다.", "Enter Code": "코드를 입력하세요", "Enter passphrase": "암호 입력", - "Error decrypting attachment": "첨부 파일 해독 중 오류", + "Error decrypting attachment": "첨부 파일 복호화 중 오류", "Error: Problem communicating with the given homeserver.": "오류: 지정한 홈서버와 통신에 문제가 있습니다.", "Event information": "이벤트 정보", "Existing Call": "기존 전화", @@ -394,7 +394,7 @@ "Unable to capture screen": "화면을 찍을 수 없음", "Unable to enable Notifications": "알림을 사용할 수 없음", "Unable to load device list": "기기 목록을 불러올 수 없음", - "Undecryptable": "해독할 수 없음", + "Undecryptable": "복호화할 수 없음", "Unencrypted room": "암호화하지 않은 방", "unencrypted": "암호화하지 않음", "Unencrypted message": "암호화하지 않은 메시지", @@ -549,10 +549,10 @@ "Unknown error": "알 수 없는 오류", "Incorrect password": "맞지 않는 비밀번호", "To continue, please enter your password.": "계속하려면, 비밀번호를 입력해주세요.", - "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "이 과정으로 암호화한 방에서 받은 메시지의 키를 로컬 파일로 내보낼 수 있습니다. 그런 다음 나중에 다른 Matrix 클라이언트에서 파일을 가져와서, 해당 클라이언트에서도 이 메시지를 해독할 수 있도록 할 수 있습니다.", - "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "내보낸 파일이 있으면 누구든 암호화한 메시지를 해독해서 읽을 수 있으므로, 보안에 신경을 써야 합니다. 이런 이유로 내보낸 파일을 암호화하도록 아래에 암호를 입력하는 것을 추천합니다. 같은 암호를 사용해야 데이터를 불러올 수 있을 것입니다.", - "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "이 과정으로 다른 Matrix 클라이언트에서 내보낸 암호화 키를 가져올 수 있습니다. 그런 다음 이전 클라이언트에서 해독할 수 있는 모든 메시지를 해독할 수 있습니다.", - "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "내보낸 파일이 암호로 보호되어 있습니다. 파일을 해독하려면, 여기에 암호를 입력해야 합니다.", + "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "이 과정으로 암호화한 방에서 받은 메시지의 키를 로컬 파일로 내보낼 수 있습니다. 그런 다음 나중에 다른 Matrix 클라이언트에서 파일을 가져와서, 해당 클라이언트에서도 이 메시지를 복호화할 수 있도록 할 수 있습니다.", + "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "내보낸 파일이 있으면 누구든 암호화한 메시지를 복호화해서 읽을 수 있으므로, 보안에 신경을 써야 합니다. 이런 이유로 내보낸 파일을 암호화하도록 아래에 암호를 입력하는 것을 추천합니다. 같은 암호를 사용해야 데이터를 불러올 수 있을 것입니다.", + "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "이 과정으로 다른 Matrix 클라이언트에서 내보낸 암호화 키를 가져올 수 있습니다. 그런 다음 이전 클라이언트에서 복호화할 수 있는 모든 메시지를 복호화할 수 있습니다.", + "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "내보낸 파일이 암호로 보호되어 있습니다. 파일을 복호화하려면, 여기에 암호를 입력해야 합니다.", "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "이 이벤트를 감추길(삭제하길) 원하세요? 방 이름을 삭제하거나 주제를 바꾸면, 다시 생길 수도 있습니다.", "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:": "이 기기를 신뢰할 수 있는 지 인증하려면, 다른 방법(예를 들자면 직접 만나거나 전화를 걸어서)으로 소유자 분에게 연락해, 사용자 설정에 있는 키가 아래 키와 같은지 물어보세요:", "Device name": "기기 이름", @@ -590,9 +590,9 @@ "Custom server": "사용자 지정 서버", "Home server URL": "홈 서버 URL", "Identity server URL": "ID 서버 URL", - "Error decrypting audio": "음성 해독 오류", - "Error decrypting image": "사진 해독 중 오류", - "Error decrypting video": "영상 해독 중 오류", + "Error decrypting audio": "음성 복호화 오류", + "Error decrypting image": "사진 복호화 중 오류", + "Error decrypting video": "영상 복호화 중 오류", "Add an Integration": "통합 추가", "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "%(integrationsUrl)s에서 쓸 수 있도록 계정을 인증하려고 다른 사이트로 이동하고 있습니다. 계속하겠습니까?", "Removed or unknown message type": "감췄거나 알 수 없는 메시지 유형", @@ -670,7 +670,7 @@ "Messages containing my display name": "내 표시 이름이 포함된 메시지", "Messages in one-to-one chats": "1:1 대화 메시지", "Unavailable": "이용할 수 없음", - "View Decrypted Source": "해독된 소스 보기", + "View Decrypted Source": "복호화된 소스 보기", "Send": "보내기", "remove %(name)s from the directory.": "목록에서 %(name)s 방을 제거했습니다.", "Notifications on the following keywords follow rules which can’t be displayed here:": "여기에 표시할 수 없는 규칙에 따르는 다음 키워드에 대한 알림:", @@ -779,7 +779,7 @@ "%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)s님이 아바타를 %(count)s번 바꿨습니다", "%(oneUser)schanged their avatar %(count)s times|one": "%(oneUser)s님이 아바타를 바꿨습니다", "This setting cannot be changed later!": "이 설정은 나중에 바꿀 수 없습니다!", - "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "이전 버전 Riot의 데이터가 감지됬습니다. 이 때문에 이전 버전에서 종단간 암호화가 작동하지 않을 수 있습니다. 이전 버전을 사용하면서 최근에 교환한 종단간 암호화 메시지를 이 버전에서는 해독할 수 없습니다. 이 버전에서 메시지를 교환할 수 없을 수도 있습니다. 문제가 발생하면 로그아웃한 후 다시 로그인하세요. 메시지 기록을 유지하려면 키를 내보낸 후 다시 가져오세요.", + "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "이전 버전 Riot의 데이터가 감지됬습니다. 이 때문에 이전 버전에서 종단간 암호화가 작동하지 않을 수 있습니다. 이전 버전을 사용하면서 최근에 교환한 종단간 암호화 메시지를 이 버전에서는 복호화할 수 없습니다. 이 버전에서 메시지를 교환할 수 없을 수도 있습니다. 문제가 발생하면 로그아웃한 후 다시 로그인하세요. 메시지 기록을 유지하려면 키를 내보낸 후 다시 가져오세요.", "Hide display name changes": "별명 변경 내역 숨기기", "This event could not be displayed": "이 이벤트를 표시할 수 없음", "Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "%(displayName)s(%(userName)s)님이 %(dateTime)s에 확인함", @@ -902,7 +902,7 @@ "%(senderName)s sent a video": "%(senderName)s님이 영상을 보냄", "%(senderName)s uploaded a file": "%(senderName)s님이 파일을 업로드함", "Key request sent.": "키 요청을 보냈습니다.", - "If your other devices do not have the key for this message you will not be able to decrypt them.": "당신의 다른 기기에 이 메시지를 읽기 위한 키가 없다면 메시지를 해독할 수 없습니다.", + "If your other devices do not have the key for this message you will not be able to decrypt them.": "당신의 다른 기기에 이 메시지를 읽기 위한 키가 없다면 메시지를 복호화할 수 없습니다.", "Encrypting": "암호화 중", "Encrypted, not sent": "암호화 됨, 보내지지 않음", "Disinvite this user?": "이 사용자에 대한 초대를 취소할까요?", @@ -1811,13 +1811,13 @@ "Deny": "거부", "Unable to load backup status": "백업 상태 불러올 수 없음", "Recovery Key Mismatch": "복구 키가 맞지 않음", - "Backup could not be decrypted with this key: please verify that you entered the correct recovery key.": "이 키로 백업을 해독할 수 없음: 맞는 복구 키를 입력해서 인증해주세요.", + "Backup could not be decrypted with this key: please verify that you entered the correct recovery key.": "이 키로 백업을 복호화할 수 없음: 맞는 복구 키를 입력해서 인증해주세요.", "Incorrect Recovery Passphrase": "맞지 않은 복구 암호", - "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.": "이 암호로 백업을 해독할 수 없음: 맞는 암호를 입력해서 입증해주세요.", + "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.": "이 암호로 백업을 복호화할 수 없음: 맞는 암호를 입력해서 입증해주세요.", "Unable to restore backup": "백업을 복구할 수 없음", "No backup found!": "백업을 찾을 수 없습니다!", "Backup Restored": "백업 복구됨", - "Failed to decrypt %(failedCount)s sessions!": "%(failedCount)s개의 세션 해독에 실패했습니다!", + "Failed to decrypt %(failedCount)s sessions!": "%(failedCount)s개의 세션 복호화에 실패했습니다!", "Restored %(sessionCount)s session keys": "%(sessionCount)s개의 세션 키 복구됨", "Enter Recovery Passphrase": "복구 암호 입력", "Warning: you should only set up key backup from a trusted computer.": "경고: 신뢰할 수 있는 컴퓨터에서만 키 백업을 설정해야 합니다.", @@ -2124,5 +2124,16 @@ "Show tray icon and minimize window to it on close": "닫을 때 창을 최소화하고 트레이 아이콘으로 표시하기", "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "이 작업에는 이메일 주소 또는 전화번호를 확인하기 위해 기본 ID 서버 에 접근해야 합니다. 하지만 서버가 서비스 약관을 갖고 있지 않습니다.", "Trust": "신뢰함", - "Message Actions": "메시지 동작" + "Message Actions": "메시지 동작", + "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", + "Send verification requests in direct message": "다이렉트 메시지에서 확인 요청 보내기", + "You verified %(name)s": "%(name)s님을 확인했습니다", + "You cancelled verifying %(name)s": "%(name)s님의 확인을 취소했습니다", + "%(name)s cancelled verifying": "%(name)s님이 확인을 취소했습니다", + "You accepted": "당신이 수락했습니다", + "%(name)s accepted": "%(name)s님이 수락했습니다", + "You cancelled": "당신이 취소했습니다", + "%(name)s cancelled": "%(name)s님이 취소했습니다", + "%(name)s wants to verify": "%(name)s님이 확인을 요청합니다", + "You sent a verification request": "확인 요청을 보냈습니다" } From 651098b2cabbb6c9211a571a9beff480f8b8b1bf Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Tue, 12 Nov 2019 17:46:17 +0000 Subject: [PATCH 021/116] Translated using Weblate (Albanian) Currently translated at 99.8% (1894 of 1898 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sq/ --- src/i18n/strings/sq.json | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index 1d80a90a2b..2efe4ddd68 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -2063,7 +2063,7 @@ "Discovery options will appear once you have added a phone number above.": "Mundësitë e zbulimit do të shfaqen sapo të keni shtuar më sipër një numër telefoni.", "Call failed due to misconfigured server": "Thirrja dështoi për shkak shërbyesi të keqformësuar", "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Që thirrjet të funksionojnë pa probleme, ju lutemi, kërkojini përgjegjësit të shërbyesit tuaj Home (%(homeserverDomain)s) të formësojë një shërbyes TURN.", - "Alternatively, you can try to use the public server at turn.matrix.org, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Ndryshe, mund të provoni të përdorni shërbyesin publik te turn.matrix.org, por kjo s’do të jetë edhe aq e qëndrueshme, dhe adresa juaj IP do t’i bëhet e njohur atij shërbyesi.Këtë mund ta bëni edhe që nga Rregullimet.", + "Alternatively, you can try to use the public server at turn.matrix.org, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Ndryshe, mund të provoni të përdorni shërbyesin publik te turn.matrix.org, por kjo s’do të jetë edhe aq e qëndrueshme, dhe adresa juaj IP do t’i bëhet e njohur atij shërbyesi. Këtë mund ta bëni edhe që nga Rregullimet.", "Try using turn.matrix.org": "Provo të përdorësh turn.matrix.org", "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Lejoni shërbyes rrugëzgjidhje asistimi thirrjesh turn.matrix.org kur shërbyesi juaj Home nuk ofron një të tillë (gjatë thirrjes, adresa juaj IP do t’i bëhet e ditur)", "ID": "ID", @@ -2246,5 +2246,39 @@ "Cancel search": "Anulo kërkimin", "No identity server is configured so you cannot add an email address in order to reset your password in the future.": "S’ka shërbyes identitetesh të formësuar, ndaj s’mund të shtoni një adresë email që të mund të ricaktoni fjalëkalimin tuaj në të ardhmen.", "Jump to first unread room.": "Hidhu te dhoma e parë e palexuar.", - "Jump to first invite.": "Hidhu te ftesa e parë." + "Jump to first invite.": "Hidhu te ftesa e parë.", + "Try out new ways to ignore people (experimental)": "Provoni rrugë të reja për shpërfillje personash (eksperimentale)", + "My Ban List": "Lista Ime e Dëbimeve", + "This is your list of users/servers you have blocked - don't leave the room!": "Kjo është lista juaj e përdoruesve/shërbyesve që keni bllokuar - mos dilni nga dhoma!", + "Ignored/Blocked": "Të shpërfillur/Të bllokuar", + "Error adding ignored user/server": "Gabim shtimi përdoruesi/shërbyesi të shpërfillur", + "Something went wrong. Please try again or view your console for hints.": "Diç shkoi ters. Ju lutemi, riprovoni ose, për ndonjë ide, shihni konsolën tuaj.", + "Error subscribing to list": "Gabim pajtimi te lista", + "Please verify the room ID or alias and try again.": "Ju lutemi, verifikoni ID-në ose aliasin e dhomës dhe riprovoni.", + "Error removing ignored user/server": "Gabim në heqje përdoruesi/shërbyes të shpërfillur", + "Error unsubscribing from list": "Gabim shpajtimi nga lista", + "Please try again or view your console for hints.": "Ju lutemi, riprovoni, ose shihni konsolën tuaj, për ndonjë ide.", + "None": "Asnjë", + "Ban list rules - %(roomName)s": "Rregulla liste dëbimesh - %(roomName)s", + "Server rules": "Rregulla shërbyesi", + "User rules": "Rregulla përdoruesi", + "You have not ignored anyone.": "S’keni shpërfillur ndonjë.", + "You are currently ignoring:": "Aktualisht shpërfillni:", + "You are not subscribed to any lists": "S’jeni pajtuar te ndonjë listë", + "Unsubscribe": "Shpajtohuni", + "View rules": "Shihni rregulla", + "You are currently subscribed to:": "Jeni i pajtuar te:", + "⚠ These settings are meant for advanced users.": "⚠ Këto rregullime janë menduar për përdorues të përparuar.", + "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Shtoni këtu përdorues dhe shërbyes që doni të shpërfillen. Që Riot të kërkojë për përputhje me çfarëdo shkronjash, përdorni yllthin. Për shembull, @bot:* do të shpërfillë krej përdoruesit që kanë emrin 'bot' në çfarëdo shërbyesi.", + "Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.": "Shpërfillja e personave kryhet përmes listash dëbimi, të cilat përmbajnë rregulla se cilët të dëbohen. Pajtimi te një listë dëbimesh do të thotë se përdoruesit/shërbyesit e bllokuar nga ajo listë do t’ju fshihen juve.", + "Personal ban list": "Listë personale dëbimesh", + "Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "Lista juaj personale e dëbimeve mban krejt përdoruesit/shërbyesit prej të cilëve ju personalisht s’dëshironi të shihni mesazhe. Pas shpërfilljes së përdoruesit/shërbyesit tuaj të parë, te lista juaj e dhomave do të shfaqet një dhomë e re e quajtur 'Lista Ime e Dëbimeve' - qëndroni në këtë dhomë që ta mbani listën e dëbimeve në fuqi.", + "Server or user ID to ignore": "Shërbyes ose ID përdoruesi për t’u shpërfillur", + "eg: @bot:* or example.org": "p.sh.: @bot:* ose example.org", + "Subscribed lists": "Lista me pajtim", + "Subscribing to a ban list will cause you to join it!": "Pajtimi te një listë dëbimesh do të shkaktojë pjesëmarrjen tuaj në të!", + "If this isn't what you want, please use a different tool to ignore users.": "Nëse kjo s’është ajo çka doni, ju lutemi, përdorni një tjetër mjet për të shpërfillur përdorues.", + "Room ID or alias of ban list": "ID dhome ose alias e listës së dëbimeve", + "Subscribe": "Pajtohuni", + "You have ignored this user, so their message is hidden. Show anyways.": "E keni shpërfillur këtë përdorues, ndaj mesazhi i tij është fshehur. Shfaqe, sido qoftë." } From 6b8d8d13f241d996bab6b2345db7e084a287adee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Gr=C3=B6nroos?= Date: Tue, 12 Nov 2019 20:28:10 +0000 Subject: [PATCH 022/116] Translated using Weblate (Finnish) Currently translated at 95.6% (1814 of 1898 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fi/ --- src/i18n/strings/fi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index 5331bdb5c8..a1163f564b 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -1533,7 +1533,7 @@ "Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Lisää ”¯\\_(ツ)_/¯” viestin alkuun", "User %(userId)s is already in the room": "Käyttäjä %(userId)s on jo huoneessa", "The user must be unbanned before they can be invited.": "Käyttäjän porttikielto täytyy poistaa ennen kutsumista.", - "Upgrade to your own domain": "Päivitä omaan verkkotunnukseesi", + "Upgrade to your own domain": "Päivitä omaan verkkotunnukseen", "Accept all %(invitedRooms)s invites": "Hyväksy kaikki %(invitedRooms)s kutsut", "Change room avatar": "Vaihda huoneen kuva", "Change room name": "Vaihda huoneen nimi", From a0ff1e9e9b547de4386ac38a6900db5b612be840 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Tue, 12 Nov 2019 20:08:29 +0000 Subject: [PATCH 023/116] Translated using Weblate (Hungarian) Currently translated at 100.0% (1898 of 1898 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 67af8a6a57..29747bb1b6 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2278,5 +2278,39 @@ "You cancelled": "Megszakítottad", "%(name)s cancelled": "%(name)s megszakította", "%(name)s wants to verify": "%(name)s ellenőrizni szeretné", - "You sent a verification request": "Ellenőrzési kérést küldtél" + "You sent a verification request": "Ellenőrzési kérést küldtél", + "Try out new ways to ignore people (experimental)": "Emberek figyelmen kívül hagyásához próbálj ki új utakat (kísérleti)", + "My Ban List": "Tiltólistám", + "This is your list of users/servers you have blocked - don't leave the room!": "Ez az általad tiltott felhasználók/szerverek listája - ne hagyd el ezt a szobát!", + "Ignored/Blocked": "Figyelmen kívül hagyott/Tiltott", + "Error adding ignored user/server": "Hiba a felhasználó/szerver hozzáadásánál a figyelmen kívül hagyandók listájához", + "Something went wrong. Please try again or view your console for hints.": "Valami nem sikerült. Kérjük próbáld újra vagy nézd meg a konzolt a hiba okának felderítéséhez.", + "Error subscribing to list": "A listára való feliratkozásnál hiba történt", + "Please verify the room ID or alias and try again.": "Kérünk ellenőrizd a szoba azonosítóját vagy alternatív nevét és próbáld újra.", + "Error removing ignored user/server": "Hiba a felhasználó/szerver törlésénél a figyelmen kívül hagyandók listájából", + "Error unsubscribing from list": "A listáról való leiratkozásnál hiba történt", + "Please try again or view your console for hints.": "Kérjük próbáld újra vagy nézd meg a konzolt a hiba okának felderítéséhez.", + "None": "Semmi", + "Ban list rules - %(roomName)s": "Tiltási lista szabályok - %(roomName)s", + "Server rules": "Szerver szabályok", + "User rules": "Felhasználói szabályok", + "You have not ignored anyone.": "Senkit nem hagysz figyelmen kívül.", + "You are currently ignoring:": "Jelenleg őket hagyod figyelmen kívül:", + "You are not subscribed to any lists": "Nem iratkoztál fel egyetlen listára sem", + "Unsubscribe": "Leiratkozás", + "View rules": "Szabályok megtekintése", + "You are currently subscribed to:": "Jelenleg ezekre iratkoztál fel:", + "⚠ These settings are meant for advanced users.": "⚠ Ezek a beállítások haladó felhasználók számára vannak.", + "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Adj hozzá olyan felhasználókat és szervereket akiket figyelmen kívül kívánsz hagyni. Használj csillagot ahol bármilyen karakter állhat. Például: @bot:* minden „bot” nevű felhasználót figyelmen kívül fog hagyni akármelyik szerverről.", + "Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.": "Emberek figyelmen kívül hagyása tiltólistán keresztül történik ami arról tartalmaz szabályokat, hogy kiket kell kitiltani. A feliratkozás a tiltólistára azt jelenti, hogy azok a felhasználók/szerverek amik tiltva vannak a lista által, azoknak az üzenetei rejtve maradnak számodra.", + "Personal ban list": "Személyes tiltó lista", + "Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "A személyes tiltólistád tartalmazza azokat a személyeket/szervereket akiktől nem szeretnél üzeneteket látni. Az első felhasználó/szerver figyelmen kívül hagyása után egy új szoba jelenik meg a szobák listájában „Tiltólistám” névvel - ahhoz, hogy a lista érvényben maradjon maradj a szobában.", + "Server or user ID to ignore": "Figyelmen kívül hagyandó szerver vagy felhasználói azonosító", + "eg: @bot:* or example.org": "pl.: @bot:* vagy example.org", + "Subscribed lists": "Feliratkozott listák", + "Subscribing to a ban list will cause you to join it!": "A feliratkozás egy tiltó listára azzal jár, hogy csatlakozol hozzá!", + "If this isn't what you want, please use a different tool to ignore users.": "Ha nem ez az amit szeretnél, kérlek használj más eszközt a felhasználók figyelmen kívül hagyásához.", + "Room ID or alias of ban list": "Tiltó lista szoba azonosítója vagy alternatív neve", + "Subscribe": "Feliratkozás", + "You have ignored this user, so their message is hidden. Show anyways.": "Ezt a felhasználót figyelmen kívül hagyod, így az üzenetei el lesznek rejtve. Mindenképpen megmutat." } From 008554463d0478e9b06f0da35dce1af83d08eb55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 09:52:59 +0100 Subject: [PATCH 024/116] Lifecycle: Move the event index deletion into the clear storage method. --- src/Lifecycle.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 7360cd3231..1e68bcc062 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -613,7 +613,6 @@ export function onLoggedOut() { // so that React components unmount first. This avoids React soft crashes // that can occur when components try to use a null client. dis.dispatch({action: 'on_logged_out'}); - EventIndexPeg.deleteEventIndex().done(); stopMatrixClient(); _clearStorage().done(); } @@ -633,7 +632,13 @@ function _clearStorage() { // we'll never make any requests, so can pass a bogus HS URL baseUrl: "", }); - return cli.clearStores(); + + const clear = async() => { + await EventIndexPeg.deleteEventIndex(); + await cli.clearStores(); + } + + return clear(); } /** From 1cc64f2426bc049257985b06855b9ba9dbcd0113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 10:10:35 +0100 Subject: [PATCH 025/116] Searching: Move the small helper functions out of the eventIndexSearch function. --- src/Searching.js | 146 +++++++++++++++++++++++------------------------ 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/src/Searching.js b/src/Searching.js index cd06d9bc67..cff5742b04 100644 --- a/src/Searching.js +++ b/src/Searching.js @@ -34,80 +34,80 @@ function serverSideSearch(term, roomId = undefined) { return searchPromise; } +async function combinedSearchFunc(searchTerm) { + // Create two promises, one for the local search, one for the + // server-side search. + const client = MatrixClientPeg.get(); + const serverSidePromise = serverSideSearch(searchTerm); + const localPromise = localSearchFunc(searchTerm); + + // Wait for both promises to resolve. + await Promise.all([serverSidePromise, localPromise]); + + // Get both search results. + const localResult = await localPromise; + const serverSideResult = await serverSidePromise; + + // Combine the search results into one result. + const result = {}; + + // Our localResult and serverSideResult are both ordered by + // recency separetly, when we combine them the order might not + // be the right one so we need to sort them. + const compare = (a, b) => { + const aEvent = a.context.getEvent().event; + const bEvent = b.context.getEvent().event; + + if (aEvent.origin_server_ts > + bEvent.origin_server_ts) return -1; + if (aEvent.origin_server_ts < + bEvent.origin_server_ts) return 1; + return 0; + }; + + result.count = localResult.count + serverSideResult.count; + result.results = localResult.results.concat( + serverSideResult.results).sort(compare); + result.highlights = localResult.highlights.concat( + serverSideResult.highlights); + + return result; +} + +async function localSearchFunc(searchTerm, roomId = undefined) { + const searchArgs = { + search_term: searchTerm, + before_limit: 1, + after_limit: 1, + order_by_recency: true, + }; + + if (roomId !== undefined) { + searchArgs.room_id = roomId; + } + + const eventIndex = EventIndexPeg.get(); + + const localResult = await eventIndex.search(searchArgs); + + const response = { + search_categories: { + room_events: localResult, + }, + }; + + const emptyResult = { + results: [], + highlights: [], + }; + + const result = MatrixClientPeg.get()._processRoomEventsSearch( + emptyResult, response); + + return result; +} + function eventIndexSearch(term, roomId = undefined) { - const combinedSearchFunc = async (searchTerm) => { - // Create two promises, one for the local search, one for the - // server-side search. - const client = MatrixClientPeg.get(); - const serverSidePromise = serverSideSearch(searchTerm); - const localPromise = localSearchFunc(searchTerm); - - // Wait for both promises to resolve. - await Promise.all([serverSidePromise, localPromise]); - - // Get both search results. - const localResult = await localPromise; - const serverSideResult = await serverSidePromise; - - // Combine the search results into one result. - const result = {}; - - // Our localResult and serverSideResult are both ordered by - // recency separetly, when we combine them the order might not - // be the right one so we need to sort them. - const compare = (a, b) => { - const aEvent = a.context.getEvent().event; - const bEvent = b.context.getEvent().event; - - if (aEvent.origin_server_ts > - bEvent.origin_server_ts) return -1; - if (aEvent.origin_server_ts < - bEvent.origin_server_ts) return 1; - return 0; - }; - - result.count = localResult.count + serverSideResult.count; - result.results = localResult.results.concat( - serverSideResult.results).sort(compare); - result.highlights = localResult.highlights.concat( - serverSideResult.highlights); - - return result; - }; - - const localSearchFunc = async (searchTerm, roomId = undefined) => { - const searchArgs = { - search_term: searchTerm, - before_limit: 1, - after_limit: 1, - order_by_recency: true, - }; - - if (roomId !== undefined) { - searchArgs.room_id = roomId; - } - - const eventIndex = EventIndexPeg.get(); - - const localResult = await eventIndex.search(searchArgs); - - const response = { - search_categories: { - room_events: localResult, - }, - }; - - const emptyResult = { - results: [], - highlights: [], - }; - - const result = MatrixClientPeg.get()._processRoomEventsSearch( - emptyResult, response); - - return result; - }; - let searchPromise; if (roomId !== undefined) { From 1df28c75262e113ea0111a6cc0dccb74a512e93d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 10:30:38 +0100 Subject: [PATCH 026/116] Fix some lint errors. --- src/EventIndexPeg.js | 12 +++++++----- src/Lifecycle.js | 4 ++-- src/MatrixClientPeg.js | 2 -- src/Searching.js | 5 ++--- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index 86fb889c7a..15d34ea230 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -32,7 +32,9 @@ class EventIndexPeg { } /** - * Returns the current Event index object for the application. Can be null + * Get the current event index. + * + * @Returns The EventIndex object for the application. Can be null * if the platform doesn't support event indexing. */ get() { @@ -47,25 +49,25 @@ class EventIndexPeg { const platform = PlatformPeg.get(); if (!platform.supportsEventIndexing()) return false; - let index = new EventIndex(); + const index = new EventIndex(); const userId = MatrixClientPeg.get().getUserId(); // TODO log errors here and return false if it errors out. await index.init(userId); this.index = index; - return true + return true; } stop() { if (this.index === null) return; - index.stop(); + this.index.stop(); this.index = null; } async deleteEventIndex() { if (this.index === null) return; - index.deleteEventIndex(); + this.index.deleteEventIndex(); } } diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 1e68bcc062..aa900c81a1 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -633,10 +633,10 @@ function _clearStorage() { baseUrl: "", }); - const clear = async() => { + const clear = async () => { await EventIndexPeg.deleteEventIndex(); await cli.clearStores(); - } + }; return clear(); } diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 6c5b465bb0..bebb254afc 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -30,8 +30,6 @@ import {verificationMethods} from 'matrix-js-sdk/lib/crypto'; import MatrixClientBackedSettingsHandler from "./settings/handlers/MatrixClientBackedSettingsHandler"; import * as StorageManager from './utils/StorageManager'; import IdentityAuthClient from './IdentityAuthClient'; -import PlatformPeg from "./PlatformPeg"; -import EventIndexPeg from "./EventIndexPeg"; interface MatrixClientCreds { homeserverUrl: string, diff --git a/src/Searching.js b/src/Searching.js index cff5742b04..84e73b91f4 100644 --- a/src/Searching.js +++ b/src/Searching.js @@ -26,7 +26,7 @@ function serverSideSearch(term, roomId = undefined) { }; } - let searchPromise = MatrixClientPeg.get().searchRoomEvents({ + const searchPromise = MatrixClientPeg.get().searchRoomEvents({ filter: filter, term: term, }); @@ -37,7 +37,6 @@ function serverSideSearch(term, roomId = undefined) { async function combinedSearchFunc(searchTerm) { // Create two promises, one for the local search, one for the // server-side search. - const client = MatrixClientPeg.get(); const serverSidePromise = serverSideSearch(searchTerm); const localPromise = localSearchFunc(searchTerm); @@ -126,7 +125,7 @@ function eventIndexSearch(term, roomId = undefined) { searchPromise = combinedSearchFunc(term); } - return searchPromise + return searchPromise; } export default function eventSearch(term, roomId = undefined) { From 54b352f69cd1e9d82fff759c6838af1affca4f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 10:37:20 +0100 Subject: [PATCH 027/116] MatrixChat: Fix the limited timeline checkpoint adding. --- src/EventIndexing.js | 9 ++------- src/components/structures/MatrixChat.js | 4 +--- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/EventIndexing.js b/src/EventIndexing.js index ebd2ffe983..bf3f50690f 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -347,15 +347,10 @@ export default class EventIndexer { console.log("Seshat: Stopping crawler function"); } - async addCheckpointForLimitedRoom(roomId) { + async addCheckpointForLimitedRoom(room) { const platform = PlatformPeg.get(); if (!platform.supportsEventIndexing()) return; - if (!MatrixClientPeg.get().isRoomEncrypted(roomId)) return; - - const client = MatrixClientPeg.get(); - const room = client.getRoom(roomId); - - if (room === null) return; + if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; const timeline = room.getLiveTimeline(); const token = timeline.getPaginationToken("b"); diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 0d3d5abd55..ccc8b5e1d6 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1223,8 +1223,6 @@ export default createReactClass({ * Called when the session is logged out */ _onLoggedOut: async function() { - const platform = PlatformPeg.get(); - this.notifyNewScreen('login'); this.setStateForNewView({ view: VIEWS.LOGIN, @@ -1313,7 +1311,7 @@ export default createReactClass({ const eventIndex = EventIndexPeg.get(); if (eventIndex === null) return; if (resetAllTimelines === true) return; - await eventIndex.addCheckpointForLimitedRoom(roomId); + await eventIndex.addCheckpointForLimitedRoom(room); }); cli.on('sync', function(state, prevState, data) { From 80b28004e15821bd127bee3121baabd1cf6226a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 11:02:54 +0100 Subject: [PATCH 028/116] Searching: Define the room id in the const object. --- src/Searching.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Searching.js b/src/Searching.js index 84e73b91f4..ee46a66fb8 100644 --- a/src/Searching.js +++ b/src/Searching.js @@ -79,6 +79,7 @@ async function localSearchFunc(searchTerm, roomId = undefined) { before_limit: 1, after_limit: 1, order_by_recency: true, + room_id: undefined, }; if (roomId !== undefined) { From f453fea24acf110d0b297d8374234a8c873bec80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 12:25:16 +0100 Subject: [PATCH 029/116] BasePlatform: Move the event indexing methods into a separate class. --- src/BaseEventIndexManager.js | 208 +++++++++++++++++++++++++++++++++++ src/BasePlatform.js | 41 +------ src/EventIndexPeg.js | 6 +- src/EventIndexing.js | 62 +++++------ 4 files changed, 246 insertions(+), 71 deletions(-) create mode 100644 src/BaseEventIndexManager.js diff --git a/src/BaseEventIndexManager.js b/src/BaseEventIndexManager.js new file mode 100644 index 0000000000..cd7a735e8d --- /dev/null +++ b/src/BaseEventIndexManager.js @@ -0,0 +1,208 @@ +// @flow + +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export interface MatrixEvent { + type: string; + sender: string; + content: {}; + event_id: string; + origin_server_ts: number; + unsigned: ?{}; + room_id: string; +} + +export interface MatrixProfile { + avatar_url: string; + displayname: string; +} + +export interface CrawlerCheckpoint { + roomId: string; + token: string; + fullCrawl: boolean; + direction: string; +} + +export interface ResultContext { + events_before: [MatrixEvent]; + events_after: [MatrixEvent]; + profile_info: Map; +} + +export interface ResultsElement { + rank: number; + result: MatrixEvent; + context: ResultContext; +} + +export interface SearchResult { + count: number; + results: [ResultsElement]; + highlights: [string]; +} + +export interface SearchArgs { + search_term: string; + before_limit: number; + after_limit: number; + order_by_recency: boolean; + room_id: ?string; +} + +export interface HistoricEvent { + event: MatrixEvent; + profile: MatrixProfile; +} + +/** + * Base class for classes that provide platform-specific event indexing. + * + * Instances of this class are provided by the application. + */ +export default class BaseEventIndexManager { + /** + * Initialize the event index for the given user. + * + * @param {string} userId The unique identifier of the logged in user that + * owns the index. + * + * @return {Promise} A promise that will resolve when the event index is + * initialized. + */ + async initEventIndex(userId: string): Promise<> { + throw new Error("Unimplemented"); + } + + /** + * Queue up an event to be added to the index. + * + * @param {MatrixEvent} ev The event that should be added to the index. + * @param {MatrixProfile} profile The profile of the event sender at the + * time of the event receival. + * + * @return {Promise} A promise that will resolve when the was queued up for + * addition. + */ + async addEventToIndex(ev: MatrixEvent, profile: MatrixProfile): Promise<> { + throw new Error("Unimplemented"); + } + + /** + * Check if our event index is empty. + * + * @return {Promise} A promise that will resolve to true if the + * event index is empty, false otherwise. + */ + indexIsEmpty(): Promise { + throw new Error("Unimplemented"); + } + + /** + * Commit the previously queued up events to the index. + * + * @return {Promise} A promise that will resolve once the queued up events + * were added to the index. + */ + async commitLiveEvents(): Promise<> { + throw new Error("Unimplemented"); + } + + /** + * Search the event index using the given term for matching events. + * + * @param {SearchArgs} searchArgs The search configuration sets what should + * be searched for and what should be contained in the search result. + * + * @return {Promise<[SearchResult]>} A promise that will resolve to an array + * of search results once the search is done. + */ + async searchEventIndex(searchArgs: SearchArgs): Promise { + throw new Error("Unimplemented"); + } + + /** + * Add events from the room history to the event index. + * + * This is used to add a batch of events to the index. + * + * @param {[HistoricEvent]} events The list of events and profiles that + * should be added to the event index. + * @param {[CrawlerCheckpoint]} checkpoint A new crawler checkpoint that + * should be stored in the index which should be used to continue crawling + * the room. + * @param {[CrawlerCheckpoint]} oldCheckpoint The checkpoint that was used + * to fetch the current batch of events. This checkpoint will be removed + * from the index. + * + * @return {Promise} A promise that will resolve to true if all the events + * were already added to the index, false otherwise. + */ + async addHistoricEvents( + events: [HistoricEvent], + checkpoint: CrawlerCheckpoint | null = null, + oldCheckpoint: CrawlerCheckpoint | null = null, + ): Promise { + throw new Error("Unimplemented"); + } + + /** + * Add a new crawler checkpoint to the index. + * + * @param {CrawlerCheckpoint} checkpoint The checkpoint that should be added + * to the index. + * + * @return {Promise} A promise that will resolve once the checkpoint has + * been stored. + */ + async addCrawlerCheckpoint(checkpoint: CrawlerCheckpoint): Promise<> { + throw new Error("Unimplemented"); + } + + /** + * Add a new crawler checkpoint to the index. + * + * @param {CrawlerCheckpoint} checkpoint The checkpoint that should be + * removed from the index. + * + * @return {Promise} A promise that will resolve once the checkpoint has + * been removed. + */ + async removeCrawlerCheckpoint(checkpoint: CrawlerCheckpoint): Promise<> { + throw new Error("Unimplemented"); + } + + /** + * Load the stored checkpoints from the index. + * + * @return {Promise<[CrawlerCheckpoint]>} A promise that will resolve to an + * array of crawler checkpoints once they have been loaded from the index. + */ + async loadCheckpoints(): Promise<[CrawlerCheckpoint]> { + throw new Error("Unimplemented"); + } + + /** + * Delete our current event index. + * + * @return {Promise} A promise that will resolve once the event index has + * been deleted. + */ + async deleteEventIndex(): Promise<> { + throw new Error("Unimplemented"); + } +} diff --git a/src/BasePlatform.js b/src/BasePlatform.js index 7f5df822e4..582ac24cb0 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -19,6 +19,7 @@ limitations under the License. */ import dis from './dispatcher'; +import BaseEventIndexManager from './BaseEventIndexManager'; /** * Base class for classes that provide platform-specific functionality @@ -152,43 +153,7 @@ export default class BasePlatform { throw new Error("Unimplemented"); } - supportsEventIndexing(): boolean { - return false; - } - - async initEventIndex(userId: string): boolean { - throw new Error("Unimplemented"); - } - - async addEventToIndex(ev: {}, profile: {}): void { - throw new Error("Unimplemented"); - } - - indexIsEmpty(): Promise { - throw new Error("Unimplemented"); - } - - async commitLiveEvents(): void { - throw new Error("Unimplemented"); - } - - async searchEventIndex(term: string): Promise<{}> { - throw new Error("Unimplemented"); - } - - async addHistoricEvents(events: [], checkpoint: {} = null, oldCheckpoint: {} = null): Promise { - throw new Error("Unimplemented"); - } - - async addCrawlerCheckpoint(checkpoint: {}): Promise<> { - throw new Error("Unimplemented"); - } - - async removeCrawlerCheckpoint(checkpoint: {}): Promise<> { - throw new Error("Unimplemented"); - } - - async deleteEventIndex(): Promise<> { - throw new Error("Unimplemented"); + getEventIndexingManager(): BaseEventIndexManager | null { + return null; } } diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index 15d34ea230..bec3f075b6 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -46,9 +46,11 @@ class EventIndexPeg { * otherwise. */ async init() { - const platform = PlatformPeg.get(); - if (!platform.supportsEventIndexing()) return false; + const indexManager = PlatformPeg.get().getEventIndexingManager(); + console.log("Initializing event index, got {}", indexManager); + if (indexManager === null) return false; + console.log("Seshat: Creatingnew EventIndex object", indexManager); const index = new EventIndex(); const userId = MatrixClientPeg.get().getUserId(); diff --git a/src/EventIndexing.js b/src/EventIndexing.js index bf3f50690f..60482b76b5 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -31,19 +31,19 @@ export default class EventIndexer { } async init(userId) { - const platform = PlatformPeg.get(); - if (!platform.supportsEventIndexing()) return false; - platform.initEventIndex(userId); + const indexManager = PlatformPeg.get().getEventIndexingManager(); + if (indexManager === null) return false; + indexManager.initEventIndex(userId); return true; } async onSync(state, prevState, data) { - const platform = PlatformPeg.get(); - if (!platform.supportsEventIndexing()) return; + const indexManager = PlatformPeg.get().getEventIndexingManager(); + if (indexManager === null) return; if (prevState === null && state === "PREPARED") { // Load our stored checkpoints, if any. - this.crawlerChekpoints = await platform.loadCheckpoints(); + this.crawlerChekpoints = await indexManager.loadCheckpoints(); console.log("Seshat: Loaded checkpoints", this.crawlerChekpoints); return; @@ -85,8 +85,8 @@ export default class EventIndexer { direction: "f", }; - await platform.addCrawlerCheckpoint(backCheckpoint); - await platform.addCrawlerCheckpoint(forwardCheckpoint); + await indexManager.addCrawlerCheckpoint(backCheckpoint); + await indexManager.addCrawlerCheckpoint(forwardCheckpoint); this.crawlerChekpoints.push(backCheckpoint); this.crawlerChekpoints.push(forwardCheckpoint); })); @@ -95,7 +95,7 @@ export default class EventIndexer { // If our indexer is empty we're most likely running Riot the // first time with indexing support or running it with an // initial sync. Add checkpoints to crawl our encrypted rooms. - const eventIndexWasEmpty = await platform.isEventIndexEmpty(); + const eventIndexWasEmpty = await indexManager.isEventIndexEmpty(); if (eventIndexWasEmpty) await addInitialCheckpoints(); // Start our crawler. @@ -107,14 +107,14 @@ export default class EventIndexer { // A sync was done, presumably we queued up some live events, // commit them now. console.log("Seshat: Committing events"); - await platform.commitLiveEvents(); + await indexManager.commitLiveEvents(); return; } } async onRoomTimeline(ev, room, toStartOfTimeline, removed, data) { - const platform = PlatformPeg.get(); - if (!platform.supportsEventIndexing()) return; + const indexManager = PlatformPeg.get().getEventIndexingManager(); + if (indexManager === null) return; // We only index encrypted rooms locally. if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; @@ -139,8 +139,8 @@ export default class EventIndexer { } async onEventDecrypted(ev, err) { - const platform = PlatformPeg.get(); - if (!platform.supportsEventIndexing()) return; + const indexManager = PlatformPeg.get().getEventIndexingManager(); + if (indexManager === null) return; const eventId = ev.getId(); @@ -151,8 +151,8 @@ export default class EventIndexer { } async addLiveEventToIndex(ev) { - const platform = PlatformPeg.get(); - if (!platform.supportsEventIndexing()) return; + const indexManager = PlatformPeg.get().getEventIndexingManager(); + if (indexManager === null) return; if (["m.room.message", "m.room.name", "m.room.topic"] .indexOf(ev.getType()) == -1) { @@ -165,7 +165,7 @@ export default class EventIndexer { avatar_url: ev.sender.getMxcAvatarUrl(), }; - platform.addEventToIndex(e, profile); + indexManager.addEventToIndex(e, profile); } async crawlerFunc(handle) { @@ -180,7 +180,7 @@ export default class EventIndexer { console.log("Seshat: Started crawler function"); const client = MatrixClientPeg.get(); - const platform = PlatformPeg.get(); + const indexManager = PlatformPeg.get().getEventIndexingManager(); handle.cancel = () => { cancelled = true; @@ -223,14 +223,14 @@ export default class EventIndexer { } catch (e) { console.log("Seshat: Error crawling events:", e); this.crawlerChekpoints.push(checkpoint); - continue + continue; } if (res.chunk.length === 0) { console.log("Seshat: Done with the checkpoint", checkpoint); // We got to the start/end of our timeline, lets just // delete our checkpoint and go back to sleep. - await platform.removeCrawlerCheckpoint(checkpoint); + await indexManager.removeCrawlerCheckpoint(checkpoint); continue; } @@ -323,7 +323,7 @@ export default class EventIndexer { ); try { - const eventsAlreadyAdded = await platform.addHistoricEvents( + const eventsAlreadyAdded = await indexManager.addHistoricEvents( events, newCheckpoint, checkpoint); // If all events were already indexed we assume that we catched // up with our index and don't need to crawl the room further. @@ -332,7 +332,7 @@ export default class EventIndexer { if (eventsAlreadyAdded === true && newCheckpoint.fullCrawl !== true) { console.log("Seshat: Checkpoint had already all events", "added, stopping the crawl", checkpoint); - await platform.removeCrawlerCheckpoint(newCheckpoint); + await indexManager.removeCrawlerCheckpoint(newCheckpoint); } else { this.crawlerChekpoints.push(newCheckpoint); } @@ -348,8 +348,8 @@ export default class EventIndexer { } async addCheckpointForLimitedRoom(room) { - const platform = PlatformPeg.get(); - if (!platform.supportsEventIndexing()) return; + const indexManager = PlatformPeg.get().getEventIndexingManager(); + if (indexManager === null) return; if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; const timeline = room.getLiveTimeline(); @@ -372,19 +372,19 @@ export default class EventIndexer { console.log("Seshat: Added checkpoint because of a limited timeline", backwardsCheckpoint, forwardsCheckpoint); - await platform.addCrawlerCheckpoint(backwardsCheckpoint); - await platform.addCrawlerCheckpoint(forwardsCheckpoint); + await indexManager.addCrawlerCheckpoint(backwardsCheckpoint); + await indexManager.addCrawlerCheckpoint(forwardsCheckpoint); this.crawlerChekpoints.push(backwardsCheckpoint); this.crawlerChekpoints.push(forwardsCheckpoint); } async deleteEventIndex() { - const platform = PlatformPeg.get(); - if (platform.supportsEventIndexing()) { + const indexManager = PlatformPeg.get().getEventIndexingManager(); + if (indexManager !== null) { console.log("Seshat: Deleting event index."); this.crawlerRef.cancel(); - await platform.deleteEventIndex(); + await indexManager.deleteEventIndex(); } } @@ -400,7 +400,7 @@ export default class EventIndexer { } async search(searchArgs) { - const platform = PlatformPeg.get(); - return platform.searchEventIndex(searchArgs) + const indexManager = PlatformPeg.get().getEventIndexingManager(); + return indexManager.searchEventIndex(searchArgs); } } From 1316e04776b90ec7cc3d7770822b400795de171b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 15:23:08 +0100 Subject: [PATCH 030/116] EventIndexing: Check if there is a room when resetting the timeline. --- src/EventIndexing.js | 13 ++----------- src/components/structures/MatrixChat.js | 4 ++-- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/EventIndexing.js b/src/EventIndexing.js index 60482b76b5..4817df4b32 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -347,7 +347,7 @@ export default class EventIndexer { console.log("Seshat: Stopping crawler function"); } - async addCheckpointForLimitedRoom(room) { + async onLimitedTimeline(room) { const indexManager = PlatformPeg.get().getEventIndexingManager(); if (indexManager === null) return; if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; @@ -362,21 +362,12 @@ export default class EventIndexer { direction: "b", }; - const forwardsCheckpoint = { - roomId: room.roomId, - token: token, - fullCrawl: false, - direction: "f", - }; - console.log("Seshat: Added checkpoint because of a limited timeline", - backwardsCheckpoint, forwardsCheckpoint); + backwardsCheckpoint); await indexManager.addCrawlerCheckpoint(backwardsCheckpoint); - await indexManager.addCrawlerCheckpoint(forwardsCheckpoint); this.crawlerChekpoints.push(backwardsCheckpoint); - this.crawlerChekpoints.push(forwardsCheckpoint); } async deleteEventIndex() { diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index ccc8b5e1d6..f78bb5c168 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1310,8 +1310,8 @@ export default createReactClass({ cli.on("Room.timelineReset", async (room, timelineSet, resetAllTimelines) => { const eventIndex = EventIndexPeg.get(); if (eventIndex === null) return; - if (resetAllTimelines === true) return; - await eventIndex.addCheckpointForLimitedRoom(room); + if (room === null) return; + await eventIndex.onLimitedTimeline(room); }); cli.on('sync', function(state, prevState, data) { From ab7f34b45a66748fde1ee361faa7f31bc86db0e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 15:26:27 +0100 Subject: [PATCH 031/116] EventIndexing: Don't mention Seshat in the logs. --- src/EventIndexing.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/EventIndexing.js b/src/EventIndexing.js index 4817df4b32..f67d4c9eb3 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -44,7 +44,7 @@ export default class EventIndexer { if (prevState === null && state === "PREPARED") { // Load our stored checkpoints, if any. this.crawlerChekpoints = await indexManager.loadCheckpoints(); - console.log("Seshat: Loaded checkpoints", + console.log("EventIndex: Loaded checkpoints", this.crawlerChekpoints); return; } @@ -62,7 +62,7 @@ export default class EventIndexer { // rooms can use the search provided by the Homeserver. const encryptedRooms = rooms.filter(isRoomEncrypted); - console.log("Seshat: Adding initial crawler checkpoints"); + console.log("EventIndex: Adding initial crawler checkpoints"); // Gather the prev_batch tokens and create checkpoints for // our message crawler. @@ -70,7 +70,7 @@ export default class EventIndexer { const timeline = room.getLiveTimeline(); const token = timeline.getPaginationToken("b"); - console.log("Seshat: Got token for indexer", + console.log("EventIndex: Got token for indexer", room.roomId, token); const backCheckpoint = { @@ -106,7 +106,7 @@ export default class EventIndexer { if (prevState === "SYNCING" && state === "SYNCING") { // A sync was done, presumably we queued up some live events, // commit them now. - console.log("Seshat: Committing events"); + console.log("EventIndex: Committing events"); await indexManager.commitLiveEvents(); return; } @@ -177,7 +177,7 @@ export default class EventIndexer { let cancelled = false; - console.log("Seshat: Started crawler function"); + console.log("EventIndex: Started crawler function"); const client = MatrixClientPeg.get(); const indexManager = PlatformPeg.get().getEventIndexingManager(); @@ -192,10 +192,10 @@ export default class EventIndexer { // here. await sleep(this._crawler_timeout); - console.log("Seshat: Running the crawler loop."); + console.log("EventIndex: Running the crawler loop."); if (cancelled) { - console.log("Seshat: Cancelling the crawler."); + console.log("EventIndex: Cancelling the crawler."); break; } @@ -207,7 +207,7 @@ export default class EventIndexer { continue; } - console.log("Seshat: crawling using checkpoint", checkpoint); + console.log("EventIndex: crawling using checkpoint", checkpoint); // We have a checkpoint, let us fetch some messages, again, very // conservatively to not bother our Homeserver too much. @@ -221,13 +221,13 @@ export default class EventIndexer { checkpoint.roomId, checkpoint.token, 100, checkpoint.direction); } catch (e) { - console.log("Seshat: Error crawling events:", e); + console.log("EventIndex: Error crawling events:", e); this.crawlerChekpoints.push(checkpoint); continue; } if (res.chunk.length === 0) { - console.log("Seshat: Done with the checkpoint", checkpoint); + console.log("EventIndex: Done with the checkpoint", checkpoint); // We got to the start/end of our timeline, lets just // delete our checkpoint and go back to sleep. await indexManager.removeCrawlerCheckpoint(checkpoint); @@ -289,7 +289,7 @@ export default class EventIndexer { // stage? const filteredEvents = matrixEvents.filter(isValidEvent); - // Let us convert the events back into a format that Seshat can + // Let us convert the events back into a format that EventIndex can // consume. const events = filteredEvents.map((ev) => { const jsonEvent = ev.toJSON(); @@ -317,7 +317,7 @@ export default class EventIndexer { }; console.log( - "Seshat: Crawled room", + "EventIndex: Crawled room", client.getRoom(checkpoint.roomId).name, "and fetched", events.length, "events.", ); @@ -330,21 +330,21 @@ export default class EventIndexer { // Let us delete the checkpoint in that case, otherwise push // the new checkpoint to be used by the crawler. if (eventsAlreadyAdded === true && newCheckpoint.fullCrawl !== true) { - console.log("Seshat: Checkpoint had already all events", + console.log("EventIndex: Checkpoint had already all events", "added, stopping the crawl", checkpoint); await indexManager.removeCrawlerCheckpoint(newCheckpoint); } else { this.crawlerChekpoints.push(newCheckpoint); } } catch (e) { - console.log("Seshat: Error durring a crawl", e); + console.log("EventIndex: Error durring a crawl", e); // An error occured, put the checkpoint back so we // can retry. this.crawlerChekpoints.push(checkpoint); } } - console.log("Seshat: Stopping crawler function"); + console.log("EventIndex: Stopping crawler function"); } async onLimitedTimeline(room) { @@ -362,7 +362,7 @@ export default class EventIndexer { direction: "b", }; - console.log("Seshat: Added checkpoint because of a limited timeline", + console.log("EventIndex: Added checkpoint because of a limited timeline", backwardsCheckpoint); await indexManager.addCrawlerCheckpoint(backwardsCheckpoint); @@ -373,7 +373,7 @@ export default class EventIndexer { async deleteEventIndex() { const indexManager = PlatformPeg.get().getEventIndexingManager(); if (indexManager !== null) { - console.log("Seshat: Deleting event index."); + console.log("EventIndex: Deleting event index."); this.crawlerRef.cancel(); await indexManager.deleteEventIndex(); } From c33f5ba0ca8292116e1623a9d0c932aac62479a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 15:39:06 +0100 Subject: [PATCH 032/116] BaseEventIndexManager: Add a method to perform runtime checks for indexing support. --- src/BaseEventIndexManager.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/BaseEventIndexManager.js b/src/BaseEventIndexManager.js index cd7a735e8d..a74eac658a 100644 --- a/src/BaseEventIndexManager.js +++ b/src/BaseEventIndexManager.js @@ -75,6 +75,19 @@ export interface HistoricEvent { * Instances of this class are provided by the application. */ export default class BaseEventIndexManager { + /** + * Does our EventIndexManager support event indexing. + * + * If an EventIndexManager imlpementor has runtime dependencies that + * optionally enable event indexing they may override this method to perform + * the necessary runtime checks here. + * + * @return {Promise} A promise that will resolve to true if event indexing + * is supported, false otherwise. + */ + async supportsEventIndexing(): Promise { + return true; + } /** * Initialize the event index for the given user. * From bf558b46c3cfc9ee7b19dbe7a92ac79ed118e498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 15:39:39 +0100 Subject: [PATCH 033/116] EventIndexPeg: Clean up the event index initialization. --- src/EventIndexPeg.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index bec3f075b6..3ce88339eb 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -47,15 +47,25 @@ class EventIndexPeg { */ async init() { const indexManager = PlatformPeg.get().getEventIndexingManager(); - console.log("Initializing event index, got {}", indexManager); if (indexManager === null) return false; - console.log("Seshat: Creatingnew EventIndex object", indexManager); - const index = new EventIndex(); + if (await indexManager.supportsEventIndexing() !== true) { + console.log("EventIndex: Platform doesn't support event indexing,", + "not initializing."); + return false; + } + const index = new EventIndex(); const userId = MatrixClientPeg.get().getUserId(); - // TODO log errors here and return false if it errors out. - await index.init(userId); + + try { + await index.init(userId); + } catch (e) { + console.log("EventIndex: Error initializing the event index", e); + } + + console.log("EventIndex: Successfully initialized the event index"); + this.index = index; return true; From c26df9d9efc836b1a6b5d660edd702448a22b3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 15:57:12 +0100 Subject: [PATCH 034/116] EventIndexing: Fix a typo. --- src/EventIndexing.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/EventIndexing.js b/src/EventIndexing.js index f67d4c9eb3..af77979040 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -22,7 +22,7 @@ import MatrixClientPeg from "./MatrixClientPeg"; */ export default class EventIndexer { constructor() { - this.crawlerChekpoints = []; + this.crawlerCheckpoints = []; // The time that the crawler will wait between /rooms/{room_id}/messages // requests this._crawler_timeout = 3000; @@ -43,9 +43,9 @@ export default class EventIndexer { if (prevState === null && state === "PREPARED") { // Load our stored checkpoints, if any. - this.crawlerChekpoints = await indexManager.loadCheckpoints(); + this.crawlerCheckpoints = await indexManager.loadCheckpoints(); console.log("EventIndex: Loaded checkpoints", - this.crawlerChekpoints); + this.crawlerCheckpoints); return; } @@ -87,8 +87,8 @@ export default class EventIndexer { await indexManager.addCrawlerCheckpoint(backCheckpoint); await indexManager.addCrawlerCheckpoint(forwardCheckpoint); - this.crawlerChekpoints.push(backCheckpoint); - this.crawlerChekpoints.push(forwardCheckpoint); + this.crawlerCheckpoints.push(backCheckpoint); + this.crawlerCheckpoints.push(forwardCheckpoint); })); }; @@ -199,7 +199,7 @@ export default class EventIndexer { break; } - const checkpoint = this.crawlerChekpoints.shift(); + const checkpoint = this.crawlerCheckpoints.shift(); /// There is no checkpoint available currently, one may appear if // a sync with limited room timelines happens, so go back to sleep. @@ -222,7 +222,7 @@ export default class EventIndexer { checkpoint.direction); } catch (e) { console.log("EventIndex: Error crawling events:", e); - this.crawlerChekpoints.push(checkpoint); + this.crawlerCheckpoints.push(checkpoint); continue; } @@ -334,13 +334,13 @@ export default class EventIndexer { "added, stopping the crawl", checkpoint); await indexManager.removeCrawlerCheckpoint(newCheckpoint); } else { - this.crawlerChekpoints.push(newCheckpoint); + this.crawlerCheckpoints.push(newCheckpoint); } } catch (e) { console.log("EventIndex: Error durring a crawl", e); // An error occured, put the checkpoint back so we // can retry. - this.crawlerChekpoints.push(checkpoint); + this.crawlerCheckpoints.push(checkpoint); } } @@ -367,7 +367,7 @@ export default class EventIndexer { await indexManager.addCrawlerCheckpoint(backwardsCheckpoint); - this.crawlerChekpoints.push(backwardsCheckpoint); + this.crawlerCheckpoints.push(backwardsCheckpoint); } async deleteEventIndex() { From f2f8a82876a4dc46701ee71b79fa47bf697f7002 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 13 Nov 2019 03:31:51 +0000 Subject: [PATCH 035/116] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (1898 of 1898 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 36 ++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 185026aad5..a4898bd328 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2284,5 +2284,39 @@ "You cancelled": "您已取消", "%(name)s cancelled": "%(name)s 已取消", "%(name)s wants to verify": "%(name)s 想要驗證", - "You sent a verification request": "您已傳送了驗證請求" + "You sent a verification request": "您已傳送了驗證請求", + "Try out new ways to ignore people (experimental)": "試用新的方式來忽略人們(實驗性)", + "My Ban List": "我的封鎖清單", + "This is your list of users/servers you have blocked - don't leave the room!": "這是您已封鎖的的使用者/伺服器清單,不要離開聊天室!", + "Ignored/Blocked": "已忽略/已封鎖", + "Error adding ignored user/server": "新增要忽略的使用者/伺服器錯誤", + "Something went wrong. Please try again or view your console for hints.": "有東西出問題了。請重試或檢視您的主控臺以取得更多資訊。", + "Error subscribing to list": "訂閱清單發生錯誤", + "Please verify the room ID or alias and try again.": "請驗證聊天室 ID 或別名並再試一次。", + "Error removing ignored user/server": "移除要忽略的使用者/伺服器發生錯誤", + "Error unsubscribing from list": "從清單取消訂閱時發生錯誤", + "Please try again or view your console for hints.": "請重試或檢視您的主控臺以取得更多資訊。", + "None": "無", + "Ban list rules - %(roomName)s": "封鎖清單規則 - %(roomName)s", + "Server rules": "伺服器規則", + "User rules": "使用者規則", + "You have not ignored anyone.": "您尚未忽略任何人。", + "You are currently ignoring:": "您目前正在忽略:", + "You are not subscribed to any lists": "您尚未訂閱任何清單", + "Unsubscribe": "取消訂閱", + "View rules": "檢視規則", + "You are currently subscribed to:": "您目前已訂閱:", + "⚠ These settings are meant for advanced users.": "⚠ 這些設定適用於進階使用者。", + "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "在此新增您想要忽略的使用者與伺服器。使用星號以讓 Riot 核對所有字元。舉例來說,@bot:* 將會忽略在任何伺服器上,所有有 'bot' 名稱的使用者。", + "Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.": "忽略人們已透過封鎖清單完成,其中包含了誰要被封鎖的規則。訂閱封鎖清單代表被此清單封鎖的使用者/伺服器會對您隱藏。", + "Personal ban list": "個人封鎖清單", + "Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "您的個人封鎖清單包含了您個人不想要看到的所有使用者/伺服器。在忽略您的第一個使用者/伺服器後,您的聊天室清單中會出現新的聊天室,其名為「我的封鎖清單」,留在這個聊天室裡面以讓封鎖清單生效。", + "Server or user ID to ignore": "要忽略的伺服器或使用者 ID", + "eg: @bot:* or example.org": "例子:@bot:* 或 example.org", + "Subscribed lists": "已訂閱的清單", + "Subscribing to a ban list will cause you to join it!": "訂閱封鎖清單會讓您加入它!", + "If this isn't what you want, please use a different tool to ignore users.": "如果這不是您想要的,請使用不同的工具來忽略使用者。", + "Room ID or alias of ban list": "聊天室 ID 或封鎖清單的別名", + "Subscribe": "訂閱", + "You have ignored this user, so their message is hidden. Show anyways.": "您已經忽略了這個使用者,所以他們的訊息會隱藏。無論如何都顯示。" } From 95b8e83cd3f97cf6ce3f2ff144bc505518e155c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Wed, 13 Nov 2019 10:31:31 +0000 Subject: [PATCH 036/116] Translated using Weblate (French) Currently translated at 100.0% (1898 of 1898 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 40840c8d58..f4e889d955 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2291,5 +2291,39 @@ "You cancelled": "Vous avez annulé", "%(name)s cancelled": "%(name)s a annulé", "%(name)s wants to verify": "%(name)s veut vérifier", - "You sent a verification request": "Vous avez envoyé une demande de vérification" + "You sent a verification request": "Vous avez envoyé une demande de vérification", + "Try out new ways to ignore people (experimental)": "Essayez de nouvelles façons d’ignorer les gens (expérimental)", + "My Ban List": "Ma liste de bannissement", + "This is your list of users/servers you have blocked - don't leave the room!": "C’est la liste des utilisateurs/serveurs que vous avez bloqués − ne quittez pas le salon !", + "Ignored/Blocked": "Ignoré/bloqué", + "Error adding ignored user/server": "Erreur lors de l’ajout de l’utilisateur/du serveur ignoré", + "Something went wrong. Please try again or view your console for hints.": "Une erreur est survenue. Réessayez ou consultez votre console pour des indices.", + "Error subscribing to list": "Erreur lors de l’inscription à la liste", + "Please verify the room ID or alias and try again.": "Vérifiez l’identifiant ou l’alias du salon et réessayez.", + "Error removing ignored user/server": "Erreur lors de la suppression de l’utilisateur/du serveur ignoré", + "Error unsubscribing from list": "Erreur lors de la désinscription de la liste", + "Please try again or view your console for hints.": "Réessayez ou consultez votre console pour des indices.", + "None": "Aucun", + "Ban list rules - %(roomName)s": "Règles de la liste de bannissement − %(roomName)s", + "Server rules": "Règles de serveur", + "User rules": "Règles d’utilisateur", + "You have not ignored anyone.": "Vous n’avez ignoré personne.", + "You are currently ignoring:": "Vous ignorez actuellement :", + "You are not subscribed to any lists": "Vous n’êtes inscrit(e) à aucune liste", + "Unsubscribe": "Se désinscrire", + "View rules": "Voir les règles", + "You are currently subscribed to:": "Vous êtes actuellement inscrit(e) à :", + "⚠ These settings are meant for advanced users.": "⚠ Ces paramètres sont prévus pour les utilisateurs avancés.", + "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Ajoutez les utilisateurs et les serveurs que vous voulez ignorer ici. Utilisez des astérisques pour remplacer n’importe quel caractère. Par exemple, @bot:* ignorerait tous les utilisateurs qui ont le nom « bot » sur n’importe quel serveur.", + "Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.": "Ignorer les gens est possible grâce à des listes de bannissement qui contiennent des règles sur les personnes à bannir. L’inscription à une liste de bannissement signifie que les utilisateurs/serveurs bloqués par cette liste seront cachés pour vous.", + "Personal ban list": "Liste de bannissement personnelle", + "Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "Votre liste de bannissement personnelle contient tous les utilisateurs/serveurs dont vous ne voulez pas voir les messages personnellement. Quand vous aurez ignoré votre premier utilisateur/serveur, un nouveau salon nommé « Ma liste de bannissement » apparaîtra dans la liste de vos salons − restez dans ce salon pour que la liste de bannissement soit effective.", + "Server or user ID to ignore": "Serveur ou identifiant d’utilisateur à ignorer", + "eg: @bot:* or example.org": "par ex. : @bot:* ou exemple.org", + "Subscribed lists": "Listes souscrites", + "Subscribing to a ban list will cause you to join it!": "En vous inscrivant à une liste de bannissement, vous la rejoindrez !", + "If this isn't what you want, please use a different tool to ignore users.": "Si ce n’est pas ce que vous voulez, utilisez un autre outil pour ignorer les utilisateurs.", + "Room ID or alias of ban list": "Identifiant ou alias du salon de la liste de bannissement", + "Subscribe": "S’inscrire", + "You have ignored this user, so their message is hidden. Show anyways.": "Vous avez ignoré cet utilisateur, donc ses messages sont cachés. Les montrer quand même." } From f0696dcc3f7922a007bf9b3f2b547de9d6b528d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=ED=83=9C=EC=84=AD?= Date: Wed, 13 Nov 2019 01:12:50 +0000 Subject: [PATCH 037/116] Translated using Weblate (Korean) Currently translated at 98.4% (1868 of 1898 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/ko/ --- src/i18n/strings/ko.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/ko.json b/src/i18n/strings/ko.json index 3d01d5be67..01ef0e8ae1 100644 --- a/src/i18n/strings/ko.json +++ b/src/i18n/strings/ko.json @@ -2135,5 +2135,9 @@ "You cancelled": "당신이 취소했습니다", "%(name)s cancelled": "%(name)s님이 취소했습니다", "%(name)s wants to verify": "%(name)s님이 확인을 요청합니다", - "You sent a verification request": "확인 요청을 보냈습니다" + "You sent a verification request": "확인 요청을 보냈습니다", + "Try out new ways to ignore people (experimental)": "새 방식으로 사람들을 무시하기 (실험)", + "My Ban List": "차단 목록", + "This is your list of users/servers you have blocked - don't leave the room!": "차단한 사용자/서버 목록입니다 - 방을 떠나지 마세요!", + "Ignored/Blocked": "무시됨/차단됨" } From cc2ee53824b955e513def52cf4a08118d853e646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 16:21:26 +0100 Subject: [PATCH 038/116] EventIndex: Add some more docs and fix some lint issues. --- src/BaseEventIndexManager.js | 2 +- src/BasePlatform.js | 6 ++++++ src/EventIndexPeg.js | 20 ++++++++++++++++---- src/components/structures/RoomView.js | 2 +- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/BaseEventIndexManager.js b/src/BaseEventIndexManager.js index a74eac658a..48a96c4d88 100644 --- a/src/BaseEventIndexManager.js +++ b/src/BaseEventIndexManager.js @@ -168,7 +168,7 @@ export default class BaseEventIndexManager { async addHistoricEvents( events: [HistoricEvent], checkpoint: CrawlerCheckpoint | null = null, - oldCheckpoint: CrawlerCheckpoint | null = null, + oldCheckpoint: CrawlerCheckpoint | null = null, ): Promise { throw new Error("Unimplemented"); } diff --git a/src/BasePlatform.js b/src/BasePlatform.js index 582ac24cb0..f6301fd173 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -153,6 +153,12 @@ export default class BasePlatform { throw new Error("Unimplemented"); } + /** + * Get our platform specific EventIndexManager. + * + * @return {BaseEventIndexManager} The EventIndex manager for our platform, + * can be null if the platform doesn't support event indexing. + */ getEventIndexingManager(): BaseEventIndexManager | null { return null; } diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index 3ce88339eb..1b380e273f 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -34,16 +34,16 @@ class EventIndexPeg { /** * Get the current event index. * - * @Returns The EventIndex object for the application. Can be null - * if the platform doesn't support event indexing. + * @return {EventIndex} The current event index. */ get() { return this.index; } /** Create a new EventIndex and initialize it if the platform supports it. - * Returns true if an EventIndex was successfully initialized, false - * otherwise. + * + * @return {Promise} A promise that will resolve to true if an + * EventIndex was successfully initialized, false otherwise. */ async init() { const indexManager = PlatformPeg.get().getEventIndexingManager(); @@ -71,15 +71,27 @@ class EventIndexPeg { return true; } + /** + * Stop our event indexer. + */ stop() { if (this.index === null) return; this.index.stop(); this.index = null; } + /** + * Delete our event indexer. + * + * After a call to this the init() method will need to be called again. + * + * @return {Promise} A promise that will resolve once the event index is + * deleted. + */ async deleteEventIndex() { if (this.index === null) return; this.index.deleteEventIndex(); + this.index = null; } } diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 9fe54ad164..6dee60bec7 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1131,7 +1131,7 @@ module.exports = createReactClass({ this.searchId = new Date().getTime(); let roomId; - if (scope === "Room") roomId = this.state.room.roomId, + if (scope === "Room") roomId = this.state.room.roomId; debuglog("sending search request"); const searchPromise = eventSearch(term, roomId); From 368a77ec3ef318f7e0e55832bf97877b8575f737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 16:35:04 +0100 Subject: [PATCH 039/116] EventIndexing: Fix a style issue. --- src/EventIndexing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EventIndexing.js b/src/EventIndexing.js index af77979040..5830106e84 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -25,7 +25,7 @@ export default class EventIndexer { this.crawlerCheckpoints = []; // The time that the crawler will wait between /rooms/{room_id}/messages // requests - this._crawler_timeout = 3000; + this._crawlerTimeout = 3000; this._crawlerRef = null; this.liveEventsForIndex = new Set(); } From d4b31cb7e037301b0786372d3ae643c96b2b48e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 16:35:26 +0100 Subject: [PATCH 040/116] EventIndexing: Move the max events per crawl constant into the class. --- src/EventIndexing.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/EventIndexing.js b/src/EventIndexing.js index 5830106e84..77c4022480 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -26,6 +26,9 @@ export default class EventIndexer { // The time that the crawler will wait between /rooms/{room_id}/messages // requests this._crawlerTimeout = 3000; + // The maximum number of events our crawler should fetch in a single + // crawl. + this._eventsPerCrawl = 100; this._crawlerRef = null; this.liveEventsForIndex = new Set(); } @@ -218,7 +221,7 @@ export default class EventIndexer { try { res = await client._createMessagesRequest( - checkpoint.roomId, checkpoint.token, 100, + checkpoint.roomId, checkpoint.token, this._eventsPerCrawl, checkpoint.direction); } catch (e) { console.log("EventIndex: Error crawling events:", e); From 9b32ec10b43cc274df28d938610fbf8c4b53479b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 13 Nov 2019 16:47:21 +0100 Subject: [PATCH 041/116] EventIndexing: Use the correct timeout value. --- src/EventIndexing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EventIndexing.js b/src/EventIndexing.js index 77c4022480..67bd894c67 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -193,7 +193,7 @@ export default class EventIndexer { // This is a low priority task and we don't want to spam our // Homeserver with /messages requests so we set a hefty timeout // here. - await sleep(this._crawler_timeout); + await sleep(this._crawlerTimeout); console.log("EventIndex: Running the crawler loop."); From 28d2e658a4d184d7f51d2423cc0cde5c6ad41986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 14 Nov 2019 14:13:49 +0100 Subject: [PATCH 042/116] EventIndexing: Don't scope the event index per user. --- src/BaseEventIndexManager.js | 5 +---- src/EventIndexPeg.js | 3 +-- src/EventIndexing.js | 4 ++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/BaseEventIndexManager.js b/src/BaseEventIndexManager.js index 48a96c4d88..073bdbec81 100644 --- a/src/BaseEventIndexManager.js +++ b/src/BaseEventIndexManager.js @@ -91,13 +91,10 @@ export default class BaseEventIndexManager { /** * Initialize the event index for the given user. * - * @param {string} userId The unique identifier of the logged in user that - * owns the index. - * * @return {Promise} A promise that will resolve when the event index is * initialized. */ - async initEventIndex(userId: string): Promise<> { + async initEventIndex(): Promise<> { throw new Error("Unimplemented"); } diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index 1b380e273f..ff1b2099f2 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -56,10 +56,9 @@ class EventIndexPeg { } const index = new EventIndex(); - const userId = MatrixClientPeg.get().getUserId(); try { - await index.init(userId); + await index.init(); } catch (e) { console.log("EventIndex: Error initializing the event index", e); } diff --git a/src/EventIndexing.js b/src/EventIndexing.js index 67bd894c67..1fc9197082 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -33,10 +33,10 @@ export default class EventIndexer { this.liveEventsForIndex = new Set(); } - async init(userId) { + async init() { const indexManager = PlatformPeg.get().getEventIndexingManager(); if (indexManager === null) return false; - indexManager.initEventIndex(userId); + indexManager.initEventIndex(); return true; } From 51a65f388bc3339e7378636c7c75808b63061e3d Mon Sep 17 00:00:00 2001 From: random Date: Thu, 14 Nov 2019 14:15:42 +0000 Subject: [PATCH 043/116] Translated using Weblate (Italian) Currently translated at 99.9% (1896 of 1898 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 48 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index 6262315012..8c7edbadd8 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2224,5 +2224,51 @@ "%(count)s unread messages including mentions.|one": "1 citazione non letta.", "%(count)s unread messages.|one": "1 messaggio non letto.", "Unread messages.": "Messaggi non letti.", - "Show tray icon and minimize window to it on close": "Mostra icona in tray e usala alla chiusura della finestra" + "Show tray icon and minimize window to it on close": "Mostra icona in tray e usala alla chiusura della finestra", + "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Questa azione richiede l'accesso al server di identità predefinito per verificare un indirizzo email o numero di telefono, ma il server non ha termini di servizio.", + "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", + "Try out new ways to ignore people (experimental)": "Prova nuovi metodi per ignorare persone (sperimentale)", + "Send verification requests in direct message": "Invia richieste di verifica in un messaggio diretto", + "My Ban List": "Mia lista ban", + "This is your list of users/servers you have blocked - don't leave the room!": "Questa è la lista degli utenti/server che hai bloccato - non lasciare la stanza!", + "Error adding ignored user/server": "Errore di aggiunta utente/server ignorato", + "Something went wrong. Please try again or view your console for hints.": "Qualcosa è andato storto. Riprova o controlla la console per suggerimenti.", + "Error subscribing to list": "Errore di iscrizione alla lista", + "Please verify the room ID or alias and try again.": "Verifica l'ID della stanza o l'alias e riprova.", + "Error removing ignored user/server": "Errore di rimozione utente/server ignorato", + "Error unsubscribing from list": "Errore di disiscrizione dalla lista", + "Please try again or view your console for hints.": "Riprova o controlla la console per suggerimenti.", + "None": "Nessuno", + "Ban list rules - %(roomName)s": "Regole lista banditi - %(roomName)s", + "Server rules": "Regole server", + "User rules": "Regole utente", + "You have not ignored anyone.": "Non hai ignorato nessuno.", + "You are currently ignoring:": "Attualmente stai ignorando:", + "You are not subscribed to any lists": "Non sei iscritto ad alcuna lista", + "Unsubscribe": "Disiscriviti", + "View rules": "Vedi regole", + "You are currently subscribed to:": "Attualmente sei iscritto a:", + "⚠ These settings are meant for advanced users.": "⚠ Queste opzioni sono pensate per utenti esperti.", + "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Aggiungi qui gli utenti e i server che vuoi ignorare. Usa l'asterisco perchè Riot consideri qualsiasi carattere. Ad esempio, @bot:* ignorerà tutti gli utenti che hanno il nome 'bot' su qualsiasi server.", + "Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.": "Si possono ignorare persone attraverso liste di ban contenenti regole per chi bandire. Iscriversi ad una lista di ban significa che gli utenti/server bloccati da quella lista ti verranno nascosti.", + "Personal ban list": "Lista di ban personale", + "Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "La tua lista personale di ban contiene tutti gli utenti/server da cui non vuoi vedere messaggi. Dopo aver ignorato il tuo primo utente/server, apparirà una nuova stanza nel tuo elenco stanze chiamata 'Mia lista ban' - resta in questa stanza per mantenere effettiva la lista ban.", + "Server or user ID to ignore": "Server o ID utente da ignorare", + "eg: @bot:* or example.org": "es: @bot:* o esempio.org", + "Subscribed lists": "Liste sottoscritte", + "Subscribing to a ban list will cause you to join it!": "Iscriversi ad una lista di ban implica di unirsi ad essa!", + "If this isn't what you want, please use a different tool to ignore users.": "Se non è ciò che vuoi, usa uno strumento diverso per ignorare utenti.", + "Room ID or alias of ban list": "ID stanza o alias della lista di ban", + "Subscribe": "Iscriviti", + "Message Actions": "Azioni messaggio", + "You have ignored this user, so their message is hidden. Show anyways.": "Hai ignorato questo utente, perciò il suo messaggio è nascosto. Mostra comunque.", + "You verified %(name)s": "Hai verificato %(name)s", + "You cancelled verifying %(name)s": "Hai annullato la verifica di %(name)s", + "%(name)s cancelled verifying": "%(name)s ha annullato la verifica", + "You accepted": "Hai accettato", + "%(name)s accepted": "%(name)s ha accettato", + "You cancelled": "Hai annullato", + "%(name)s cancelled": "%(name)s ha annullato", + "%(name)s wants to verify": "%(name)s vuole verificare", + "You sent a verification request": "Hai inviato una richiesta di verifica" } From 154fb7ecacc795facc748adf1e9be3a8dd05efde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=ED=83=9C=EC=84=AD?= Date: Thu, 14 Nov 2019 03:08:03 +0000 Subject: [PATCH 044/116] Translated using Weblate (Korean) Currently translated at 99.4% (1887 of 1898 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/ko/ --- src/i18n/strings/ko.json | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/ko.json b/src/i18n/strings/ko.json index 01ef0e8ae1..fe8c929acd 100644 --- a/src/i18n/strings/ko.json +++ b/src/i18n/strings/ko.json @@ -2139,5 +2139,24 @@ "Try out new ways to ignore people (experimental)": "새 방식으로 사람들을 무시하기 (실험)", "My Ban List": "차단 목록", "This is your list of users/servers you have blocked - don't leave the room!": "차단한 사용자/서버 목록입니다 - 방을 떠나지 마세요!", - "Ignored/Blocked": "무시됨/차단됨" + "Ignored/Blocked": "무시됨/차단됨", + "Error adding ignored user/server": "무시한 사용자/서버 추가 중 오류", + "Something went wrong. Please try again or view your console for hints.": "무언가 잘못되었습니다. 다시 시도하거나 콘솔을 통해 원인을 알아봐주세요.", + "Error subscribing to list": "목록으로 구독하는 중 오류", + "Please verify the room ID or alias and try again.": "방 ID나 별칭을 확인한 후 다시 시도해주세요.", + "Error removing ignored user/server": "무시한 사용자/서버를 지우는 중 오류", + "Error unsubscribing from list": "목록에서 구독 해제 중 오류", + "Please try again or view your console for hints.": "다시 시도하거나 콘솔을 통해 원인을 알아봐주세요.", + "None": "없음", + "Ban list rules - %(roomName)s": "차단 목록 규칙 - %(roomName)s", + "Server rules": "서버 규칙", + "User rules": "사용자 규칙", + "You have not ignored anyone.": "아무도 무시하고 있지 않습니다.", + "You are currently ignoring:": "현재 무시하고 있음:", + "You are not subscribed to any lists": "어느 목록에도 구독하고 있지 않습니다", + "Unsubscribe": "구독 해제", + "View rules": "규칙 보기", + "You are currently subscribed to:": "현재 구독 중임:", + "⚠ These settings are meant for advanced users.": "⚠ 이 설정은 고급 사용자를 위한 것입니다.", + "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "무시하고 싶은 사용자와 서버를 여기에 추가하세요. 별표(*)를 사용해서 Riot이 이름과 문자를 맞춰볼 수 있습니다. 예를 들어, @bot:*이라면 모든 서버에서 'bot'이라는 문자를 가진 이름의 모든 사용자를 무시합니다." } From bcb7ec081453b15fc2ea7640b5d1ace3e3e5e1f5 Mon Sep 17 00:00:00 2001 From: andriusign Date: Wed, 13 Nov 2019 20:45:07 +0000 Subject: [PATCH 045/116] Translated using Weblate (Lithuanian) Currently translated at 49.2% (934 of 1898 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index 2aeb207387..5f3a76caa0 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -1117,5 +1117,6 @@ "Custom user status messages": "Pasirinktinės vartotojo būsenos žinutės", "Group & filter rooms by custom tags (refresh to apply changes)": "Grupuoti ir filtruoti kambarius pagal pasirinktines žymas (atnaujinkite, kad pritaikytumėte pakeitimus)", "Render simple counters in room header": "Užkrauti paprastus skaitiklius kambario antraštėje", - "Multiple integration managers": "Daugialypiai integracijų valdikliai" + "Multiple integration managers": "Daugialypiai integracijų valdikliai", + "%(name)s (%(userId)s)": "%(name)s (%(userId)s)" } From 448c9a82908b9e1504ed28a66dd1a68cb9daf9c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 14 Nov 2019 16:01:14 +0100 Subject: [PATCH 046/116] EventIndexPeg: Add a missing return statement. --- src/EventIndexPeg.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index ff1b2099f2..a4ab1815c9 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -61,6 +61,7 @@ class EventIndexPeg { await index.init(); } catch (e) { console.log("EventIndex: Error initializing the event index", e); + return false; } console.log("EventIndex: Successfully initialized the event index"); From 7516f2724aeb34f13ae379f7d5c2124beca1b5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 14 Nov 2019 16:13:22 +0100 Subject: [PATCH 047/116] EventIndexing: Rework the index initialization and deletion. --- src/BaseEventIndexManager.js | 10 +++++++++ src/EventIndexPeg.js | 43 ++++++++++++++++++++++++------------ src/EventIndexing.js | 40 +++++++++++++++++++-------------- src/Lifecycle.js | 1 + 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/BaseEventIndexManager.js b/src/BaseEventIndexManager.js index 073bdbec81..4e52344e76 100644 --- a/src/BaseEventIndexManager.js +++ b/src/BaseEventIndexManager.js @@ -206,6 +206,16 @@ export default class BaseEventIndexManager { throw new Error("Unimplemented"); } + /** + * close our event index. + * + * @return {Promise} A promise that will resolve once the event index has + * been closed. + */ + async closeEventIndex(): Promise<> { + throw new Error("Unimplemented"); + } + /** * Delete our current event index. * diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index a4ab1815c9..dc25b11cf7 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -31,15 +31,6 @@ class EventIndexPeg { this.index = null; } - /** - * Get the current event index. - * - * @return {EventIndex} The current event index. - */ - get() { - return this.index; - } - /** Create a new EventIndex and initialize it if the platform supports it. * * @return {Promise} A promise that will resolve to true if an @@ -72,11 +63,30 @@ class EventIndexPeg { } /** - * Stop our event indexer. + * Get the current event index. + * + * @return {EventIndex} The current event index. */ + get() { + return this.index; + } + stop() { if (this.index === null) return; - this.index.stop(); + this.index.stopCrawler(); + } + + /** + * Unset our event store + * + * After a call to this the init() method will need to be called again. + * + * @return {Promise} A promise that will resolve once the event index is + * closed. + */ + async unset() { + if (this.index === null) return; + this.index.close(); this.index = null; } @@ -89,9 +99,14 @@ class EventIndexPeg { * deleted. */ async deleteEventIndex() { - if (this.index === null) return; - this.index.deleteEventIndex(); - this.index = null; + const indexManager = PlatformPeg.get().getEventIndexingManager(); + + if (indexManager !== null) { + this.stop(); + console.log("EventIndex: Deleting event index."); + await indexManager.deleteEventIndex(); + this.index = null; + } } } diff --git a/src/EventIndexing.js b/src/EventIndexing.js index 1fc9197082..37167cf600 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -35,9 +35,7 @@ export default class EventIndexer { async init() { const indexManager = PlatformPeg.get().getEventIndexingManager(); - if (indexManager === null) return false; - indexManager.initEventIndex(); - return true; + return indexManager.initEventIndex(); } async onSync(state, prevState, data) { @@ -198,7 +196,6 @@ export default class EventIndexer { console.log("EventIndex: Running the crawler loop."); if (cancelled) { - console.log("EventIndex: Cancelling the crawler."); break; } @@ -373,26 +370,35 @@ export default class EventIndexer { this.crawlerCheckpoints.push(backwardsCheckpoint); } + startCrawler() { + if (this._crawlerRef !== null) return; + + const crawlerHandle = {}; + this.crawlerFunc(crawlerHandle); + this._crawlerRef = crawlerHandle; + } + + stopCrawler() { + if (this._crawlerRef === null) return; + + this._crawlerRef.cancel(); + this._crawlerRef = null; + } + + async close() { + const indexManager = PlatformPeg.get().getEventIndexingManager(); + this.stopCrawler(); + return indexManager.closeEventIndex(); + } + async deleteEventIndex() { const indexManager = PlatformPeg.get().getEventIndexingManager(); if (indexManager !== null) { - console.log("EventIndex: Deleting event index."); - this.crawlerRef.cancel(); + this.stopCrawler(); await indexManager.deleteEventIndex(); } } - startCrawler() { - const crawlerHandle = {}; - this.crawlerFunc(crawlerHandle); - this.crawlerRef = crawlerHandle; - } - - stop() { - this._crawlerRef.cancel(); - this._crawlerRef = null; - } - async search(searchArgs) { const indexManager = PlatformPeg.get().getEventIndexingManager(); return indexManager.searchEventIndex(searchArgs); diff --git a/src/Lifecycle.js b/src/Lifecycle.js index aa900c81a1..1d38934ade 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -662,6 +662,7 @@ export function stopMatrixClient(unsetClient=true) { if (unsetClient) { MatrixClientPeg.unset(); + EventIndexPeg.unset().done(); } } } From d82d4246e92800588c77ed74f3e4f957a554ffbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 14 Nov 2019 16:17:50 +0100 Subject: [PATCH 048/116] BaseEventIndexManager: Remove a return from a docstring. --- src/BaseEventIndexManager.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/BaseEventIndexManager.js b/src/BaseEventIndexManager.js index 4e52344e76..fe59cee673 100644 --- a/src/BaseEventIndexManager.js +++ b/src/BaseEventIndexManager.js @@ -114,9 +114,6 @@ export default class BaseEventIndexManager { /** * Check if our event index is empty. - * - * @return {Promise} A promise that will resolve to true if the - * event index is empty, false otherwise. */ indexIsEmpty(): Promise { throw new Error("Unimplemented"); From eb0b0a400f72d8ada1e9018192eff00c42dcf250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 14 Nov 2019 16:18:36 +0100 Subject: [PATCH 049/116] EventIndexPeg: Remove the now unused import of MatrixClientPeg. --- src/EventIndexPeg.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index dc25b11cf7..da5c5425e4 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -24,7 +24,6 @@ limitations under the License. import PlatformPeg from "./PlatformPeg"; import EventIndex from "./EventIndexing"; -import MatrixClientPeg from "./MatrixClientPeg"; class EventIndexPeg { constructor() { From 5f31efadb68c84a2d54599a4324e919e71d17d63 Mon Sep 17 00:00:00 2001 From: Victor Grousset Date: Fri, 15 Nov 2019 09:33:19 +0000 Subject: [PATCH 050/116] Translated using Weblate (Esperanto) Currently translated at 98.1% (1862 of 1898 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index bbed6773b5..b160c5390b 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -1995,7 +1995,7 @@ "Preview": "Antaŭrigardo", "View": "Rigardo", "Find a room…": "Trovi ĉambron…", - "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Se vi ne povas travi la serĉatan ĉambron, petu inviton aŭ kreu novan ĉambron.", + "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Se vi ne povas trovi la serĉatan ĉambron, petu inviton aŭ kreu novan ĉambron.", "Explore rooms": "Esplori ĉambrojn", "Add Email Address": "Aldoni retpoŝtadreson", "Add Phone Number": "Aldoni telefonnumeron", From 21dc9b9f25dfe3d21ed0970e8eef2a91c439ec5f Mon Sep 17 00:00:00 2001 From: Tuomas Hietala Date: Fri, 15 Nov 2019 11:47:01 +0000 Subject: [PATCH 051/116] Translated using Weblate (Finnish) Currently translated at 96.4% (1830 of 1898 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fi/ --- src/i18n/strings/fi.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index a1163f564b..80fbb9b138 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -2121,5 +2121,21 @@ "Emoji Autocomplete": "Emojien automaattinen täydennys", "Notification Autocomplete": "Ilmoitusten automaattinen täydennys", "Room Autocomplete": "Huoneiden automaattinen täydennys", - "User Autocomplete": "Käyttäjien automaattinen täydennys" + "User Autocomplete": "Käyttäjien automaattinen täydennys", + "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Tämä toiminto vaatii oletusidentiteettipalvelimen käyttämistä sähköpostiosoitteen tai puhelinnumeron validointiin, mutta palvelimella ei ole käyttöehtoja.", + "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", + "My Ban List": "Tekemäni estot", + "This is your list of users/servers you have blocked - don't leave the room!": "Tämä on luettelo käyttäjistä ja palvelimista, jotka olet estänyt - älä poistu huoneesta!", + "Something went wrong. Please try again or view your console for hints.": "Jotain meni vikaan. Yritä uudelleen tai katso vihjeitä konsolista.", + "Please verify the room ID or alias and try again.": "Varmista huoneen tunnus tai alias ja yritä uudelleen.", + "Please try again or view your console for hints.": "Yritä uudelleen tai katso vihjeitä konsolista.", + "⚠ These settings are meant for advanced users.": "⚠ Nämä asetukset on tarkoitettu edistyneille käyttäjille.", + "eg: @bot:* or example.org": "esim. @bot:* tai esimerkki.org", + "Show tray icon and minimize window to it on close": "Näytä ilmaisinalueen kuvake ja pienennä ikkuna siihen suljettaessa", + "Your email address hasn't been verified yet": "Sähköpostiosoitettasi ei ole vielä varmistettu", + "Verify the link in your inbox": "Varmista sähköpostiisi saapunut linkki", + "%(count)s unread messages including mentions.|one": "Yksi lukematon maininta.", + "%(count)s unread messages.|one": "Yksi lukematon viesti.", + "Unread messages.": "Lukemattomat viestit.", + "Message Actions": "Viestitoiminnot" } From 9a92f3d38eb237ab808c8887391ad6f65dd94811 Mon Sep 17 00:00:00 2001 From: Volodymyr Kostyrko Date: Thu, 14 Nov 2019 20:50:50 +0000 Subject: [PATCH 052/116] Translated using Weblate (Ukrainian) Currently translated at 29.0% (551 of 1898 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/uk/ --- src/i18n/strings/uk.json | 73 +++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/src/i18n/strings/uk.json b/src/i18n/strings/uk.json index 7b1c9e1126..4c03a7019d 100644 --- a/src/i18n/strings/uk.json +++ b/src/i18n/strings/uk.json @@ -23,8 +23,8 @@ "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "Текстове повідомлення було надіслано +%(msisdn)s. Введіть, будь ласка, код підтвердження з цього повідомлення", "Accept": "Прийняти", "Account": "Обліковка", - "%(targetName)s accepted an invitation.": "%(targetName)s прийняв/ла запрошення.", - "%(targetName)s accepted the invitation for %(displayName)s.": "Запрошення від %(displayName)s прийнято %(targetName)s.", + "%(targetName)s accepted an invitation.": "%(targetName)s приймає запрошення.", + "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s приймає запрошення від %(displayName)s.", "Access Token:": "Токен доступу:", "Active call (%(roomName)s)": "Активний виклик (%(roomName)s)", "Add": "Додати", @@ -71,7 +71,7 @@ "%(senderName)s banned %(targetName)s.": "%(senderName)s заблокував/ла %(targetName)s.", "Ban": "Заблокувати", "Banned users": "Заблоковані користувачі", - "Bans user with given id": "Блокує користувача з вказаним ID", + "Bans user with given id": "Блокує користувача з вказаним ідентифікатором", "Blacklisted": "В чорному списку", "Bulk Options": "Групові параметри", "Call Timeout": "Час очікування виклика", @@ -389,15 +389,15 @@ "Changes colour scheme of current room": "Змінює кольорову схему кімнати", "Sets the room topic": "Встановлює тему кімнати", "Invites user with given id to current room": "Запрошує користувача з вказаним ідентифікатором до кімнати", - "Joins room with given alias": "Приєднується до кімнати з поданим ідентифікатором", + "Joins room with given alias": "Приєднується до кімнати під іншим псевдонімом", "Leave room": "Покинути кімнату", - "Unrecognised room alias:": "Кімнату не знайдено:", - "Kicks user with given id": "Викинути з кімнати користувача з вказаним ідентифікатором", + "Unrecognised room alias:": "Не розпізнано псевдонім кімнати:", + "Kicks user with given id": "Вилучити з кімнати користувача з вказаним ідентифікатором", "Unbans user with given id": "Розблоковує користувача з вказаним ідентифікатором", - "Ignores a user, hiding their messages from you": "Ігнорувати користувача (приховує повідомлення від них)", + "Ignores a user, hiding their messages from you": "Ігнорує користувача, приховуючи повідомлення від них", "Ignored user": "Користувача ігноровано", "You are now ignoring %(userId)s": "Ви ігноруєте %(userId)s", - "Stops ignoring a user, showing their messages going forward": "Припинити ігнорувати користувача (показує їхні повідомлення від цього моменту)", + "Stops ignoring a user, showing their messages going forward": "Припиняє ігнорувати користувача, від цього моменту показуючи їхні повідомлення", "Unignored user": "Припинено ігнорування користувача", "You are no longer ignoring %(userId)s": "Ви більше не ігноруєте %(userId)s", "Define the power level of a user": "Вказати рівень прав користувача", @@ -407,9 +407,9 @@ "Unknown (user, device) pair:": "Невідома комбінація користувача і пристрою:", "Device already verified!": "Пристрій вже перевірено!", "WARNING: Device already verified, but keys do NOT MATCH!": "УВАГА: Пристрій уже перевірено, але ключі НЕ ЗБІГАЮТЬСЯ!", - "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "УВАГА: КЛЮЧ НЕ ПРОЙШОВ ПЕРЕВІРКУ! Підписний ключ %(userId)s на пристрої %(deviceId)s — це «%(fprint)s», і він не збігається з наданим ключем «%(fingerprint)s». Це може означати, що ваші повідомлення перехоплюють!", - "Verified key": "Ключ перевірено", - "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Підписний ключ, який ви вказали, збігається з підписним ключем, отриманим від пристрою %(deviceId)s користувача %(userId)s. Пристрій позначено як перевірений.", + "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "УВАГА: КЛЮЧ НЕ ПРОЙШОВ ПЕРЕВІРКУ! Ключ підпису %(userId)s на пристрої %(deviceId)s — це «%(fprint)s», і він не збігається з наданим ключем «%(fingerprint)s». Це може означати, що ваші повідомлення перехоплюють!", + "Verified key": "Перевірений ключ", + "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Ключ підпису, який ви вказали, збігається з ключем підпису, отриманим від пристрою %(deviceId)s користувача %(userId)s. Пристрій позначено як перевірений.", "Displays action": "Показує дію", "Unrecognised command:": "Невідома команда:", "Reason": "Причина", @@ -579,5 +579,54 @@ "A conference call could not be started because the integrations server is not available": "Конференц-дзвінок не можна розпочати оскільки інтеграційний сервер недоступний", "The file '%(fileName)s' failed to upload.": "Файл '%(fileName)s' не вийшло відвантажити.", "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Файл '%(fileName)s' перевищує ліміт розміру для відвантажень домашнього сервера", - "The server does not support the room version specified.": "Сервер не підтримує вказану версію кімнати." + "The server does not support the room version specified.": "Сервер не підтримує вказану версію кімнати.", + "Add Email Address": "Додати адресу е-пошти", + "Add Phone Number": "Додати номер телефону", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Чи використовуєте ви «хлібні крихти» (аватари на списком кімнат)", + "Call failed due to misconfigured server": "Виклик не вдався через неправильне налаштування сервера", + "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Запропонуйте адміністратору вашого домашнього серверу (%(homeserverDomain)s) налаштувати сервер TURN для надійної роботи викликів.", + "Alternatively, you can try to use the public server at turn.matrix.org, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Також ви можете спробувати використати публічний сервер turn.matrix.org, але це буде не настільки надійно, а також цей сервер матиме змогу бачити вашу IP-адресу. Ви можете керувати цим у налаштуваннях.", + "Try using turn.matrix.org": "Спробуйте використати turn.matrix.org", + "Replying With Files": "Відповісти файлами", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Зараз неможливо відповісти файлом. Хочете завантажити цей файл без відповіді?", + "Name or Matrix ID": "Імʼя або Matrix ID", + "Identity server has no terms of service": "Сервер ідентифікації не має умов надання послуг", + "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Щоб підтвердити адресу е-пошту або телефон ця дія потребує доступу до типового серверу ідентифікації , але сервер не має жодних умов надання послуг.", + "Only continue if you trust the owner of the server.": "Продовжуйте тільки якщо довіряєте власнику сервера.", + "Trust": "Довіра", + "Unable to load! Check your network connectivity and try again.": "Завантаження неможливе! Перевірте інтернет-зʼєднання та спробуйте ще.", + "Email, name or Matrix ID": "Е-пошта, імʼя або Matrix ID", + "Failed to start chat": "Не вдалося розпочати чат", + "Failed to invite users to the room:": "Не вдалося запросити користувачів до кімнати:", + "Messages": "Повідомлення", + "Actions": "Дії", + "Other": "Інше", + "Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Додає ¯\\_(ツ)_/¯ на початку текстового повідомлення", + "Sends a message as plain text, without interpreting it as markdown": "Надсилає повідомлення як чистий текст, не використовуючи markdown", + "Upgrades a room to a new version": "Покращує кімнату до нової версії", + "You do not have the required permissions to use this command.": "Вам бракує дозволу на використання цієї команди.", + "Room upgrade confirmation": "Підтвердження покращення кімнати", + "Upgrading a room can be destructive and isn't always necessary.": "Покращення кімнати може призвести до втрати даних та не є обовʼязковим.", + "Room upgrades are usually recommended when a room version is considered unstable. Unstable room versions might have bugs, missing features, or security vulnerabilities.": "Рекомендується покращувати кімнату, якщо поточна її версія вважається нестабільною. Нестабільні версії кімнат можуть мати вади, відсутні функції або вразливості безпеки.", + "Room upgrades usually only affect server-side processing of the room. If you're having problems with your Riot client, please file an issue with .": "Покращення кімнати загалом впливає лише на роботу з кімнатою на сервері. Якщо ви маєте проблему із вашим клієнтом Riot, надішліть свою проблему на .", + "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "Увага!: Покращення кімнати не перенесе автоматично усіх учасників до нової версії кімнати. Ми опублікуємо посилання на нову кімнату у старій версії кімнати, а учасники мають власноруч клацнути це посилання, щоб приєднатися до нової кімнати.", + "Please confirm that you'd like to go forward with upgrading this room from to .": "Підтвердьте, що ви згодні продовжити покращення цієї кімнати з до .", + "Upgrade": "Покращення", + "Changes your display nickname in the current room only": "Змінює ваше псевдо тільки для поточної кімнати", + "Changes the avatar of the current room": "Змінює аватар поточної кімнати", + "Changes your avatar in this current room only": "Змінює ваш аватар для поточної кімнати", + "Changes your avatar in all rooms": "Змінює ваш аватар для усіх кімнат", + "Gets or sets the room topic": "Показує чи встановлює тему кімнати", + "This room has no topic.": "Ця кімната не має теми.", + "Sets the room name": "Встановлює назву кімнати", + "Use an identity server": "Використовувати сервер ідентифікації", + "Use an identity server to invite by email. Manage in Settings.": "Використовувати ідентифікаційний сервер для запрошення через е-пошту. Керування у настройках.", + "Unbans user with given ID": "Розблоковує користувача з вказаним ідентифікатором", + "Adds a custom widget by URL to the room": "Додає власний віджет до кімнати за посиланням", + "Please supply a https:// or http:// widget URL": "Вкажіть посилання на віджет — https:// або http://", + "You cannot modify widgets in this room.": "Ви не можете змінювати віджети у цій кімнаті.", + "Forces the current outbound group session in an encrypted room to be discarded": "Примусово відкидає поточний вихідний груповий сеанс у шифрованій кімнаті", + "Sends the given message coloured as a rainbow": "Надсилає вказане повідомлення розфарбоване веселкою", + "Your Riot is misconfigured": "Ваш Riot налаштовано неправильно", + "Join the discussion": "Приєднатися до обговорення" } From 6b726a8e13786f6f10222cac3c1655f47c213354 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 15 Nov 2019 14:25:53 -0700 Subject: [PATCH 053/116] Implement the bulk of the new widget permission prompt design Part 1 of https://github.com/vector-im/riot-web/issues/11262 This is all the visual changes - the actual wiring of the UI to the right places is for another PR (though this PR still works independently). The help icon is known to be weird here - it's a bug in the svg we have. The tooltip also goes right instead of up because making the tooltip go up is not easy work for this PR - maybe a future one if we *really* want it to go up. --- res/css/_common.scss | 16 ++ res/css/views/rooms/_AppsDrawer.scss | 62 ++++--- .../views/elements/AppPermission.js | 152 +++++++++++------- src/components/views/elements/AppTile.js | 4 +- .../views/elements/TextWithTooltip.js | 4 +- src/i18n/strings/en_EN.json | 17 +- 6 files changed, 169 insertions(+), 86 deletions(-) diff --git a/res/css/_common.scss b/res/css/_common.scss index 70ab2457f1..5987275f7f 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -550,6 +550,22 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { color: $username-variant8-color; } +@define-mixin mx_Tooltip_dark { + box-shadow: none; + background-color: $tooltip-timeline-bg-color; + color: $tooltip-timeline-fg-color; + border: none; + border-radius: 3px; + padding: 6px 8px; +} + +// This is a workaround for our mixins not supporting child selectors +.mx_Tooltip_dark { + .mx_Tooltip_chevron::after { + border-right-color: $tooltip-timeline-bg-color; + } +} + @define-mixin mx_Settings_fullWidthField { margin-right: 100px; } diff --git a/res/css/views/rooms/_AppsDrawer.scss b/res/css/views/rooms/_AppsDrawer.scss index 9ca6954af7..6f5e3abade 100644 --- a/res/css/views/rooms/_AppsDrawer.scss +++ b/res/css/views/rooms/_AppsDrawer.scss @@ -294,49 +294,61 @@ form.mx_Custom_Widget_Form div { .mx_AppPermissionWarning { text-align: center; - background-color: $primary-bg-color; + background-color: $widget-menu-bar-bg-color; display: flex; height: 100%; flex-direction: column; justify-content: center; align-items: center; + font-size: 16px; } -.mx_AppPermissionWarningImage { - margin: 10px 0; +.mx_AppPermissionWarning_row { + margin-bottom: 12px; } -.mx_AppPermissionWarningImage img { - width: 100px; +.mx_AppPermissionWarning_smallText { + font-size: 12px; } -.mx_AppPermissionWarningText { - max-width: 90%; - margin: 10px auto 10px auto; - color: $primary-fg-color; +.mx_AppPermissionWarning_bolder { + font-weight: 600; } -.mx_AppPermissionWarningTextLabel { - font-weight: bold; - display: block; +.mx_AppPermissionWarning h4 { + margin: 0; + padding: 0; } -.mx_AppPermissionWarningTextURL { +.mx_AppPermissionWarning_helpIcon { + margin-top: 1px; + margin-right: 2px; + width: 10px; + height: 10px; display: inline-block; - max-width: 100%; - color: $accent-color; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; } -.mx_AppPermissionButton { - border: none; - padding: 5px 20px; - border-radius: 5px; - background-color: $button-bg-color; - color: $button-fg-color; - cursor: pointer; +.mx_AppPermissionWarning_helpIcon::before { + display: inline-block; + background-color: $accent-color; + mask-repeat: no-repeat; + mask-size: 12px; + width: 12px; + height: 12px; + mask-position: center; + content: ''; + vertical-align: middle; + mask-image: url('$(res)/img/feather-customised/help-circle.svg'); +} + +.mx_AppPermissionWarning_tooltip { + @mixin mx_Tooltip_dark; + + ul { + list-style-position: inside; + padding-left: 2px; + margin-left: 0; + } } .mx_AppLoading { diff --git a/src/components/views/elements/AppPermission.js b/src/components/views/elements/AppPermission.js index 1e019c0287..422427d4c4 100644 --- a/src/components/views/elements/AppPermission.js +++ b/src/components/views/elements/AppPermission.js @@ -19,79 +19,123 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import url from 'url'; +import sdk from '../../../index'; import { _t } from '../../../languageHandler'; import WidgetUtils from "../../../utils/WidgetUtils"; +import MatrixClientPeg from "../../../MatrixClientPeg"; export default class AppPermission extends React.Component { + static propTypes = { + url: PropTypes.string.isRequired, + creatorUserId: PropTypes.string.isRequired, + roomId: PropTypes.string.isRequired, + onPermissionGranted: PropTypes.func.isRequired, + }; + + static defaultProps = { + onPermissionGranted: () => {}, + }; + constructor(props) { super(props); - const curlBase = this.getCurlBase(); - this.state = { curlBase: curlBase}; + // The first step is to pick apart the widget so we can render information about it + const urlInfo = this.parseWidgetUrl(); + + // The second step is to find the user's profile so we can show it on the prompt + const room = MatrixClientPeg.get().getRoom(this.props.roomId); + let roomMember; + if (room) roomMember = room.getMember(this.props.creatorUserId); + + // Set all this into the initial state + this.state = { + ...urlInfo, + roomMember, + }; } - // Return string representation of content URL without query parameters - getCurlBase() { - const wurl = url.parse(this.props.url); - let curl; - let curlString; + parseWidgetUrl() { + const widgetUrl = url.parse(this.props.url); + const params = new URLSearchParams(widgetUrl.search); - const searchParams = new URLSearchParams(wurl.search); - - if (WidgetUtils.isScalarUrl(wurl) && searchParams && searchParams.get('url')) { - curl = url.parse(searchParams.get('url')); - if (curl) { - curl.search = curl.query = ""; - curlString = curl.format(); - } + // HACK: We're relying on the query params when we should be relying on the widget's `data`. + // This is a workaround for Scalar. + if (WidgetUtils.isScalarUrl(widgetUrl) && params && params.get('url')) { + const unwrappedUrl = url.parse(params.get('url')); + return { + widgetDomain: unwrappedUrl.host || unwrappedUrl.hostname, + isWrapped: true, + }; + } else { + return { + widgetDomain: widgetUrl.host || widgetUrl.hostname, + isWrapped: false, + }; } - if (!curl && wurl) { - wurl.search = wurl.query = ""; - curlString = wurl.format(); - } - return curlString; } render() { - let e2eWarningText; - if (this.props.isRoomEncrypted) { - e2eWarningText = - { _t('NOTE: Apps are not end-to-end encrypted') }; - } - const cookieWarning = - - { _t('Warning: This widget might use cookies.') } - ; + const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton"); + const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar"); + const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar"); + const TextWithTooltip = sdk.getComponent("views.elements.TextWithTooltip"); + + const displayName = this.state.roomMember ? this.state.roomMember.name : this.props.creatorUserId; + const userId = displayName === this.props.creatorUserId ? null : this.props.creatorUserId; + + const avatar = this.state.roomMember + ? + : ; + + const warningTooltipText = ( +
+ {_t("Any of the following data may be shared:")} +
    +
  • {_t("Your display name")}
  • +
  • {_t("Your avatar URL")}
  • +
  • {_t("Your user ID")}
  • +
  • {_t("Your theme")}
  • +
  • {_t("Riot URL")}
  • +
  • {_t("Room ID")}
  • +
  • {_t("Widget ID")}
  • +
+
+ ); + const warningTooltip = ( + + + + ); + + // Due to i18n limitations, we can't dedupe the code for variables in these two messages. + const warning = this.state.isWrapped + ? _t("Using this widget may share data with %(widgetDomain)s & your Integration Manager.", + {widgetDomain: this.state.widgetDomain}, {helpIcon: () => warningTooltip}) + : _t("Using this widget may share data with %(widgetDomain)s.", + {widgetDomain: this.state.widgetDomain}, {helpIcon: () => warningTooltip}); + return (
-
- {_t('Warning!')} +
+ {_t("Widget added by")}
-
- {_t('Do you want to load widget from URL:')} - {this.state.curlBase} - { e2eWarningText } - { cookieWarning } +
+ {avatar} +

{displayName}

+
{userId}
+
+
+ {warning} +
+
+ {_t("This widget may use cookies.")} +
+
+ + {_t("Continue")} +
-
); } } - -AppPermission.propTypes = { - isRoomEncrypted: PropTypes.bool, - url: PropTypes.string.isRequired, - onPermissionGranted: PropTypes.func.isRequired, -}; -AppPermission.defaultProps = { - isRoomEncrypted: false, - onPermissionGranted: function() {}, -}; diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 260b63dfd4..ffd9d73cca 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -569,11 +569,11 @@ export default class AppTile extends React.Component {
); if (!this.state.hasPermissionToLoad) { - const isRoomEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId); appTileBody = (
diff --git a/src/components/views/elements/TextWithTooltip.js b/src/components/views/elements/TextWithTooltip.js index 61c3a2125a..f6cef47117 100644 --- a/src/components/views/elements/TextWithTooltip.js +++ b/src/components/views/elements/TextWithTooltip.js @@ -21,7 +21,8 @@ import sdk from '../../../index'; export default class TextWithTooltip extends React.Component { static propTypes = { class: PropTypes.string, - tooltip: PropTypes.string.isRequired, + tooltipClass: PropTypes.string, + tooltip: PropTypes.node.isRequired, }; constructor() { @@ -49,6 +50,7 @@ export default class TextWithTooltip extends React.Component { ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 655c7030c4..37383b7e4e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1183,10 +1183,18 @@ "Quick Reactions": "Quick Reactions", "Cancel search": "Cancel search", "Unknown Address": "Unknown Address", - "NOTE: Apps are not end-to-end encrypted": "NOTE: Apps are not end-to-end encrypted", - "Warning: This widget might use cookies.": "Warning: This widget might use cookies.", - "Do you want to load widget from URL:": "Do you want to load widget from URL:", - "Allow": "Allow", + "Any of the following data may be shared:": "Any of the following data may be shared:", + "Your display name": "Your display name", + "Your avatar URL": "Your avatar URL", + "Your user ID": "Your user ID", + "Your theme": "Your theme", + "Riot URL": "Riot URL", + "Room ID": "Room ID", + "Widget ID": "Widget ID", + "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Using this widget may share data with %(widgetDomain)s & your Integration Manager.", + "Using this widget may share data with %(widgetDomain)s.": "Using this widget may share data with %(widgetDomain)s.", + "Widget added by": "Widget added by", + "This widget may use cookies.": "This widget may use cookies.", "Delete Widget": "Delete Widget", "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?", "Delete widget": "Delete widget", @@ -1494,6 +1502,7 @@ "A widget would like to verify your identity": "A widget would like to verify your identity", "A widget located at %(widgetUrl)s would like to verify your identity. By allowing this, the widget will be able to verify your user ID, but not perform actions as you.": "A widget located at %(widgetUrl)s would like to verify your identity. By allowing this, the widget will be able to verify your user ID, but not perform actions as you.", "Remember my selection for this widget": "Remember my selection for this widget", + "Allow": "Allow", "Deny": "Deny", "Unable to load backup status": "Unable to load backup status", "Recovery Key Mismatch": "Recovery Key Mismatch", From d56ae702873a8a655e0d8aff0f5327f60b15f204 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Sat, 16 Nov 2019 08:37:59 +0000 Subject: [PATCH 054/116] Translated using Weblate (Albanian) Currently translated at 99.8% (1904 of 1908 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sq/ --- src/i18n/strings/sq.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index 2efe4ddd68..2bf5732131 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -2280,5 +2280,17 @@ "If this isn't what you want, please use a different tool to ignore users.": "Nëse kjo s’është ajo çka doni, ju lutemi, përdorni një tjetër mjet për të shpërfillur përdorues.", "Room ID or alias of ban list": "ID dhome ose alias e listës së dëbimeve", "Subscribe": "Pajtohuni", - "You have ignored this user, so their message is hidden. Show anyways.": "E keni shpërfillur këtë përdorues, ndaj mesazhi i tij është fshehur. Shfaqe, sido qoftë." + "You have ignored this user, so their message is hidden. Show anyways.": "E keni shpërfillur këtë përdorues, ndaj mesazhi i tij është fshehur. Shfaqe, sido qoftë.", + "Custom (%(level)s)": "Vetjak (%(level)s)", + "Trusted": "E besuar", + "Not trusted": "Jo e besuar", + "Hide verified Sign-In's": "Fshihi Hyrjet e verifikuara", + "%(count)s verified Sign-In's|other": "%(count)s Hyrje të verifikuara", + "%(count)s verified Sign-In's|one": "1 Hyrje e verifikuar", + "Direct message": "Mesazh i Drejtpërdrejtë", + "Unverify user": "Hiqi verifikimin përdoruesit", + "%(role)s in %(roomName)s": "%(role)s në %(roomName)s", + "Messages in this room are end-to-end encrypted.": "Mesazhet në këtë dhomë janë të fshehtëzuara skaj-më-skaj.", + "Security": "Siguri", + "Verify": "Verifikoje" } From a9ef6bde6333e425c50cb4bf6ca60cd5b1050e36 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Sat, 16 Nov 2019 13:09:01 +0000 Subject: [PATCH 055/116] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (1908 of 1908 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index a4898bd328..5c6e69c864 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2318,5 +2318,17 @@ "If this isn't what you want, please use a different tool to ignore users.": "如果這不是您想要的,請使用不同的工具來忽略使用者。", "Room ID or alias of ban list": "聊天室 ID 或封鎖清單的別名", "Subscribe": "訂閱", - "You have ignored this user, so their message is hidden. Show anyways.": "您已經忽略了這個使用者,所以他們的訊息會隱藏。無論如何都顯示。" + "You have ignored this user, so their message is hidden. Show anyways.": "您已經忽略了這個使用者,所以他們的訊息會隱藏。無論如何都顯示。", + "Custom (%(level)s)": "自訂 (%(level)s)", + "Trusted": "已信任", + "Not trusted": "不信任", + "Hide verified Sign-In's": "隱藏已驗證的登入", + "%(count)s verified Sign-In's|other": "%(count)s 個已驗證的登入", + "%(count)s verified Sign-In's|one": "1 個已驗證的登入", + "Direct message": "直接訊息", + "Unverify user": "未驗證的使用者", + "%(role)s in %(roomName)s": "%(role)s 在 %(roomName)s", + "Messages in this room are end-to-end encrypted.": "在此聊天室中的訊息為端到端加密。", + "Security": "安全", + "Verify": "驗證" } From 78c36e593650ec6fac3b592d7aea5647603b04ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Fri, 15 Nov 2019 21:17:15 +0000 Subject: [PATCH 056/116] Translated using Weblate (French) Currently translated at 100.0% (1908 of 1908 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index f4e889d955..eef9438761 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2325,5 +2325,17 @@ "If this isn't what you want, please use a different tool to ignore users.": "Si ce n’est pas ce que vous voulez, utilisez un autre outil pour ignorer les utilisateurs.", "Room ID or alias of ban list": "Identifiant ou alias du salon de la liste de bannissement", "Subscribe": "S’inscrire", - "You have ignored this user, so their message is hidden. Show anyways.": "Vous avez ignoré cet utilisateur, donc ses messages sont cachés. Les montrer quand même." + "You have ignored this user, so their message is hidden. Show anyways.": "Vous avez ignoré cet utilisateur, donc ses messages sont cachés. Les montrer quand même.", + "Custom (%(level)s)": "Personnalisé (%(level)s)", + "Trusted": "Fiable", + "Not trusted": "Non vérifié", + "Hide verified Sign-In's": "Masquer les connexions vérifiées", + "%(count)s verified Sign-In's|other": "%(count)s connexions vérifiées", + "%(count)s verified Sign-In's|one": "1 connexion vérifiée", + "Direct message": "Message direct", + "Unverify user": "Ne plus marquer l’utilisateur comme vérifié", + "%(role)s in %(roomName)s": "%(role)s dans %(roomName)s", + "Messages in this room are end-to-end encrypted.": "Les messages dans ce salon sont chiffrés de bout en bout.", + "Security": "Sécurité", + "Verify": "Vérifier" } From 16a107a1634c9f3071810172f57c74528789d49b Mon Sep 17 00:00:00 2001 From: Szimszon Date: Sat, 16 Nov 2019 09:23:56 +0000 Subject: [PATCH 057/116] Translated using Weblate (Hungarian) Currently translated at 100.0% (1908 of 1908 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 29747bb1b6..3c049cc321 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2312,5 +2312,17 @@ "If this isn't what you want, please use a different tool to ignore users.": "Ha nem ez az amit szeretnél, kérlek használj más eszközt a felhasználók figyelmen kívül hagyásához.", "Room ID or alias of ban list": "Tiltó lista szoba azonosítója vagy alternatív neve", "Subscribe": "Feliratkozás", - "You have ignored this user, so their message is hidden. Show anyways.": "Ezt a felhasználót figyelmen kívül hagyod, így az üzenetei el lesznek rejtve. Mindenképpen megmutat." + "You have ignored this user, so their message is hidden. Show anyways.": "Ezt a felhasználót figyelmen kívül hagyod, így az üzenetei el lesznek rejtve. Mindenképpen megmutat.", + "Custom (%(level)s)": "Egyedi (%(level)s)", + "Trusted": "Megbízható", + "Not trusted": "Megbízhatatlan", + "Hide verified Sign-In's": "Ellenőrzött belépések elrejtése", + "%(count)s verified Sign-In's|other": "%(count)s ellenőrzött belépés", + "%(count)s verified Sign-In's|one": "1 ellenőrzött belépés", + "Direct message": "Közvetlen beszélgetés", + "Unverify user": "Ellenőrizetlen felhasználó", + "%(role)s in %(roomName)s": "%(role)s a szobában: %(roomName)s", + "Messages in this room are end-to-end encrypted.": "Az üzenetek a szobában végponttól végpontig titkosítottak.", + "Security": "Biztonság", + "Verify": "Ellenőriz" } From 20dd731ed017439929a4f8908e004bcd11a0e1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=ED=83=9C=EC=84=AD?= Date: Sat, 16 Nov 2019 04:00:39 +0000 Subject: [PATCH 058/116] Translated using Weblate (Korean) Currently translated at 100.0% (1908 of 1908 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/ko/ --- src/i18n/strings/ko.json | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/ko.json b/src/i18n/strings/ko.json index fe8c929acd..8d34fab025 100644 --- a/src/i18n/strings/ko.json +++ b/src/i18n/strings/ko.json @@ -2158,5 +2158,28 @@ "View rules": "규칙 보기", "You are currently subscribed to:": "현재 구독 중임:", "⚠ These settings are meant for advanced users.": "⚠ 이 설정은 고급 사용자를 위한 것입니다.", - "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "무시하고 싶은 사용자와 서버를 여기에 추가하세요. 별표(*)를 사용해서 Riot이 이름과 문자를 맞춰볼 수 있습니다. 예를 들어, @bot:*이라면 모든 서버에서 'bot'이라는 문자를 가진 이름의 모든 사용자를 무시합니다." + "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "무시하고 싶은 사용자와 서버를 여기에 추가하세요. 별표(*)를 사용해서 Riot이 이름과 문자를 맞춰볼 수 있습니다. 예를 들어, @bot:*이라면 모든 서버에서 'bot'이라는 문자를 가진 이름의 모든 사용자를 무시합니다.", + "Custom (%(level)s)": "맞춤 (%(level)s)", + "Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.": "차단당하는 사람은 규칙에 따라 차단 목록을 통해 무시됩니다. 차단 목록을 구독하면 그 목록에서 차단당한 사용자/서버를 당신으로부터 감추게됩니다.", + "Personal ban list": "개인 차단 목록", + "Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "개인 차단 목록은 개인적으로 보고 싶지 않은 메시지를 보낸 모든 사용자/서버를 담고 있습니다. 처음 사용자/서버를 무시했다면, 방 목록에 '나의 차단 목록'이라는 이름의 새 방이 나타납니다 - 차단 목록의 효력을 유지하려면 이 방을 그대로 두세요.", + "Server or user ID to ignore": "무시할 서버 또는 사용자 ID", + "eg: @bot:* or example.org": "예: @bot:* 또는 example.org", + "Subscribed lists": "구독 목록", + "Subscribing to a ban list will cause you to join it!": "차단 목록을 구독하면 차단 목록에 참여하게 됩니다!", + "If this isn't what you want, please use a different tool to ignore users.": "이것을 원한 것이 아니라면, 사용자를 무시하는 다른 도구를 사용해주세요.", + "Room ID or alias of ban list": "방 ID 또는 차단 목록의 별칭", + "Subscribe": "구독", + "Trusted": "신뢰함", + "Not trusted": "신뢰하지 않음", + "Hide verified Sign-In's": "확인 로그인 숨기기", + "%(count)s verified Sign-In's|other": "확인된 %(count)s개의 로그인", + "%(count)s verified Sign-In's|one": "확인된 1개의 로그인", + "Direct message": "다이렉트 메시지", + "Unverify user": "사용자 확인 취소", + "%(role)s in %(roomName)s": "%(roomName)s 방의 %(role)s", + "Messages in this room are end-to-end encrypted.": "이 방의 메시지는 종단간 암호화되었습니다.", + "Security": "보안", + "Verify": "확인", + "You have ignored this user, so their message is hidden. Show anyways.": "이 사용자를 무시했습니다. 사용자의 메시지는 숨겨집니다. 무시하고 보이기." } From 5d3a84db855bb8c3e89d8759b35c6feefa06eb47 Mon Sep 17 00:00:00 2001 From: Nobbele Date: Sun, 17 Nov 2019 11:58:39 +0000 Subject: [PATCH 059/116] Translated using Weblate (Swedish) Currently translated at 76.4% (1457 of 1908 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sv/ --- src/i18n/strings/sv.json | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/sv.json b/src/i18n/strings/sv.json index 6d611be464..a1c31faf57 100644 --- a/src/i18n/strings/sv.json +++ b/src/i18n/strings/sv.json @@ -757,7 +757,7 @@ "Device Name": "Enhetsnamn", "Select devices": "Välj enheter", "Disable Emoji suggestions while typing": "Inaktivera Emoji-förslag medan du skriver", - "Use compact timeline layout": "Använd kompakt chattlayout", + "Use compact timeline layout": "Använd kompakt tidslinjelayout", "Not a valid Riot keyfile": "Inte en giltig Riot-nyckelfil", "Authentication check failed: incorrect password?": "Autentiseringskontroll misslyckades: felaktigt lösenord?", "Always show encryption icons": "Visa alltid krypteringsikoner", @@ -1019,7 +1019,7 @@ "Add rooms to the community": "Lägg till rum i communityn", "Add to community": "Lägg till i community", "Failed to invite users to community": "Det gick inte att bjuda in användare till communityn", - "Mirror local video feed": "Spegelvänd lokal video", + "Mirror local video feed": "Speglad lokal-video", "Disable Community Filter Panel": "Inaktivera community-filterpanel", "Community Invites": "Community-inbjudningar", "Invalid community ID": "Ogiltigt community-ID", @@ -1353,9 +1353,9 @@ "Show read receipts sent by other users": "Visa läskvitton som skickats av andra användare", "Show avatars in user and room mentions": "Visa avatarer i användar- och rumsnämningar", "Enable big emoji in chat": "Aktivera stor emoji i chatt", - "Send typing notifications": "Skicka \"skriver\"-status", + "Send typing notifications": "Skicka \"skriver\"-statusar", "Enable Community Filter Panel": "Aktivera community-filterpanel", - "Allow Peer-to-Peer for 1:1 calls": "Tillåt enhet-till-enhet-kommunikation för direktsamtal (mellan två personer)", + "Allow Peer-to-Peer for 1:1 calls": "Tillåt peer-to-peer kommunikation för 1:1 samtal", "Messages containing my username": "Meddelanden som innehåller mitt användarnamn", "Messages containing @room": "Meddelanden som innehåller @room", "Encrypted messages in one-to-one chats": "Krypterade meddelanden i privata chattar", @@ -1564,7 +1564,7 @@ "Please supply a https:// or http:// widget URL": "Ange en widget-URL med https:// eller http://", "You cannot modify widgets in this room.": "Du kan inte ändra widgets i detta rum.", "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s återkallade inbjudan för %(targetDisplayName)s att gå med i rummet.", - "Show a reminder to enable Secure Message Recovery in encrypted rooms": "", + "Show a reminder to enable Secure Message Recovery in encrypted rooms": "Visa en påminnelse för att sätta på säker meddelande återhämtning i krypterade rum", "The other party cancelled the verification.": "Den andra parten avbröt verifieringen.", "Verified!": "Verifierad!", "You've successfully verified this user.": "Du har verifierat den här användaren.", @@ -1817,5 +1817,11 @@ "Add Phone Number": "Lägg till telefonnummer", "Identity server has no terms of service": "Identitetsserver har inga användarvillkor", "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Den här åtgärden kräver åtkomst till standardidentitetsservern för att validera en e-postadress eller telefonnummer, men servern har inga användarvillkor.", - "Trust": "Förtroende" + "Trust": "Förtroende", + "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", + "Use the new, consistent UserInfo panel for Room Members and Group Members": "Använd den nya, konsistenta UserInfo panelen för rum medlemmar och grupp medlemmar", + "Try out new ways to ignore people (experimental)": "Testa nya sätt att ignorera personer (experimentalt)", + "Send verification requests in direct message": "Skicka verifikations begäran i direkt meddelanden", + "Use the new, faster, composer for writing messages": "Använd den nya, snabbare kompositören för att skriva meddelanden", + "Show previews/thumbnails for images": "Visa förhandsvisning/tumnagel för bilder" } From 30d4dd36a7d2086123b52d95123cbd3e43122754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 10:05:11 +0100 Subject: [PATCH 060/116] BaseEventIndexManager: Remove the flow annotation. --- src/BaseEventIndexManager.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/BaseEventIndexManager.js b/src/BaseEventIndexManager.js index fe59cee673..c5a3273a45 100644 --- a/src/BaseEventIndexManager.js +++ b/src/BaseEventIndexManager.js @@ -1,5 +1,3 @@ -// @flow - /* Copyright 2019 New Vector Ltd From ab93745460501c13ec9f68fe118fbaa9f2c06480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 10:16:29 +0100 Subject: [PATCH 061/116] Fix the copyright headers from New Vector to The Matrix Foundation. --- src/BaseEventIndexManager.js | 2 +- src/EventIndexPeg.js | 2 +- src/EventIndexing.js | 2 +- src/Searching.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/BaseEventIndexManager.js b/src/BaseEventIndexManager.js index c5a3273a45..7cefb023d1 100644 --- a/src/BaseEventIndexManager.js +++ b/src/BaseEventIndexManager.js @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index da5c5425e4..54d9c40079 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/EventIndexing.js b/src/EventIndexing.js index 37167cf600..f2c3c5c433 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/Searching.js b/src/Searching.js index ee46a66fb8..cb641ec72a 100644 --- a/src/Searching.js +++ b/src/Searching.js @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 9fa8e8238a8cc1e406ed2e6ef471bfb452d707c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 10:18:09 +0100 Subject: [PATCH 062/116] BaseEventIndexManager: Fix a typo. --- src/BaseEventIndexManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BaseEventIndexManager.js b/src/BaseEventIndexManager.js index 7cefb023d1..61c556a0ff 100644 --- a/src/BaseEventIndexManager.js +++ b/src/BaseEventIndexManager.js @@ -76,7 +76,7 @@ export default class BaseEventIndexManager { /** * Does our EventIndexManager support event indexing. * - * If an EventIndexManager imlpementor has runtime dependencies that + * If an EventIndexManager implementor has runtime dependencies that * optionally enable event indexing they may override this method to perform * the necessary runtime checks here. * From 5149164010f1237e9e07e3a1a03b5cc0738e9b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 10:23:04 +0100 Subject: [PATCH 063/116] MatrixChat: Revert the unnecessary changes in the MatrixChat class. --- src/components/structures/MatrixChat.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index f78bb5c168..b45884e64f 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1222,7 +1222,7 @@ export default createReactClass({ /** * Called when the session is logged out */ - _onLoggedOut: async function() { + _onLoggedOut: function() { this.notifyNewScreen('login'); this.setStateForNewView({ view: VIEWS.LOGIN, @@ -1272,9 +1272,8 @@ export default createReactClass({ // particularly noticeable when there are lots of 'limited' /sync responses // such as when laptops unsleep. // https://github.com/vector-im/riot-web/issues/3307#issuecomment-282895568 - cli.setCanResetTimelineCallback(async function(roomId) { + cli.setCanResetTimelineCallback(function(roomId) { console.log("Request to reset timeline in room ", roomId, " viewing:", self.state.currentRoomId); - if (roomId !== self.state.currentRoomId) { // It is safe to remove events from rooms we are not viewing. return true; From 910c3ac08db4bbdf8097e998cb486e6cdfef1a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 10:26:17 +0100 Subject: [PATCH 064/116] BaseEventIndexManager: Fix some type annotations. --- src/BaseEventIndexManager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BaseEventIndexManager.js b/src/BaseEventIndexManager.js index 61c556a0ff..5e8ca668ad 100644 --- a/src/BaseEventIndexManager.js +++ b/src/BaseEventIndexManager.js @@ -159,8 +159,8 @@ export default class BaseEventIndexManager { */ async addHistoricEvents( events: [HistoricEvent], - checkpoint: CrawlerCheckpoint | null = null, - oldCheckpoint: CrawlerCheckpoint | null = null, + checkpoint: CrawlerCheckpoint | null, + oldCheckpoint: CrawlerCheckpoint | null, ): Promise { throw new Error("Unimplemented"); } From ddb536e94a69485360611458cf70341720a3f604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 10:27:10 +0100 Subject: [PATCH 065/116] EventIndexPeg: Move a docstring to the correct place. --- src/EventIndexPeg.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index 54d9c40079..4d0e518ab8 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -30,7 +30,8 @@ class EventIndexPeg { this.index = null; } - /** Create a new EventIndex and initialize it if the platform supports it. + /** + * Create a new EventIndex and initialize it if the platform supports it. * * @return {Promise} A promise that will resolve to true if an * EventIndex was successfully initialized, false otherwise. From 050e52ce461de709c01b1697379e6f8ad7fac18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 10:34:48 +0100 Subject: [PATCH 066/116] EventIndexPeg: Treat both cases of unavailable platform support the same. --- src/EventIndexPeg.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index 4d0e518ab8..f1841b3f2b 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -38,9 +38,7 @@ class EventIndexPeg { */ async init() { const indexManager = PlatformPeg.get().getEventIndexingManager(); - if (indexManager === null) return false; - - if (await indexManager.supportsEventIndexing() !== true) { + if (!indexManager || await indexManager.supportsEventIndexing() !== true) { console.log("EventIndex: Platform doesn't support event indexing,", "not initializing."); return false; From 3b06c684d23fd4e5cd012ccc197926b612fef63b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 10:35:57 +0100 Subject: [PATCH 067/116] EventIndexing: Don't capitalize homeserver. --- src/EventIndexing.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/EventIndexing.js b/src/EventIndexing.js index f2c3c5c433..05d5fd03da 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -59,8 +59,8 @@ export default class EventIndexer { return client.isRoomEncrypted(room.roomId); }; - // We only care to crawl the encrypted rooms, non-encrytped - // rooms can use the search provided by the Homeserver. + // We only care to crawl the encrypted rooms, non-encrypted. + // rooms can use the search provided by the homeserver. const encryptedRooms = rooms.filter(isRoomEncrypted); console.log("EventIndex: Adding initial crawler checkpoints"); @@ -189,7 +189,7 @@ export default class EventIndexer { while (!cancelled) { // This is a low priority task and we don't want to spam our - // Homeserver with /messages requests so we set a hefty timeout + // homeserver with /messages requests so we set a hefty timeout // here. await sleep(this._crawlerTimeout); @@ -210,7 +210,7 @@ export default class EventIndexer { console.log("EventIndex: crawling using checkpoint", checkpoint); // We have a checkpoint, let us fetch some messages, again, very - // conservatively to not bother our Homeserver too much. + // conservatively to not bother our homeserver too much. const eventMapper = client.getEventMapper(); // TODO we need to ensure to use member lazy loading with this // request so we get the correct profiles. From b4a6123295c896b49952302e4ced6ce166034419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 10:48:18 +0100 Subject: [PATCH 068/116] Searching: Move a comment to the correct place. --- src/Searching.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Searching.js b/src/Searching.js index cb641ec72a..4e6c8b9b4d 100644 --- a/src/Searching.js +++ b/src/Searching.js @@ -20,8 +20,9 @@ import MatrixClientPeg from "./MatrixClientPeg"; function serverSideSearch(term, roomId = undefined) { let filter; if (roomId !== undefined) { + // XXX: it's unintuitive that the filter for searching doesn't have + // the same shape as the v2 filter API :( filter = { - // XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :( rooms: [roomId], }; } From a4ad8151f8415bf76bd1fd16b64ee167cc1bec0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 10:55:02 +0100 Subject: [PATCH 069/116] Searching: Use the short form to build the search arguments object. --- src/Searching.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Searching.js b/src/Searching.js index 4e6c8b9b4d..eb7137e221 100644 --- a/src/Searching.js +++ b/src/Searching.js @@ -28,8 +28,8 @@ function serverSideSearch(term, roomId = undefined) { } const searchPromise = MatrixClientPeg.get().searchRoomEvents({ - filter: filter, - term: term, + filter, + term, }); return searchPromise; From 0e3a0008df387bb036867177bb92451702f3fff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 10:56:57 +0100 Subject: [PATCH 070/116] Searching: Remove the func suffix from our search functions. --- src/Searching.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Searching.js b/src/Searching.js index eb7137e221..601da56f86 100644 --- a/src/Searching.js +++ b/src/Searching.js @@ -35,11 +35,11 @@ function serverSideSearch(term, roomId = undefined) { return searchPromise; } -async function combinedSearchFunc(searchTerm) { +async function combinedSearch(searchTerm) { // Create two promises, one for the local search, one for the // server-side search. const serverSidePromise = serverSideSearch(searchTerm); - const localPromise = localSearchFunc(searchTerm); + const localPromise = localSearch(searchTerm); // Wait for both promises to resolve. await Promise.all([serverSidePromise, localPromise]); @@ -74,7 +74,7 @@ async function combinedSearchFunc(searchTerm) { return result; } -async function localSearchFunc(searchTerm, roomId = undefined) { +async function localSearch(searchTerm, roomId = undefined) { const searchArgs = { search_term: searchTerm, before_limit: 1, @@ -115,7 +115,7 @@ function eventIndexSearch(term, roomId = undefined) { if (MatrixClientPeg.get().isRoomEncrypted(roomId)) { // The search is for a single encrypted room, use our local // search method. - searchPromise = localSearchFunc(term, roomId); + searchPromise = localSearch(term, roomId); } else { // The search is for a single non-encrypted room, use the // server-side search. @@ -124,7 +124,7 @@ function eventIndexSearch(term, roomId = undefined) { } else { // Search across all rooms, combine a server side search and a // local search. - searchPromise = combinedSearchFunc(term); + searchPromise = combinedSearch(term); } return searchPromise; From 2bb331cdf0c635728d2a08f993e2cb186a89e381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 10:57:23 +0100 Subject: [PATCH 071/116] Searching: Fix a typo. --- src/Searching.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Searching.js b/src/Searching.js index 601da56f86..ca3e7f041f 100644 --- a/src/Searching.js +++ b/src/Searching.js @@ -52,7 +52,7 @@ async function combinedSearch(searchTerm) { const result = {}; // Our localResult and serverSideResult are both ordered by - // recency separetly, when we combine them the order might not + // recency separately, when we combine them the order might not // be the right one so we need to sort them. const compare = (a, b) => { const aEvent = a.context.getEvent().event; From 579cbef7b0ed38f298fb35ad82b3d73096f080f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 14:29:03 +0100 Subject: [PATCH 072/116] EventIndexPeg: Rewrite the module documentation. --- src/EventIndexPeg.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index f1841b3f2b..a289c9e629 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -15,11 +15,8 @@ limitations under the License. */ /* - * Holds the current Platform object used by the code to do anything - * specific to the platform we're running on (eg. web, electron) - * Platforms are provided by the app layer. - * This allows the app layer to set a Platform without necessarily - * having to have a MatrixChat object + * Object holding the global EventIndex object. Can only be initialized if the + * platform supports event indexing. */ import PlatformPeg from "./PlatformPeg"; From 45e7aab41e3767026aa1e207640850f935f2aacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 14:30:07 +0100 Subject: [PATCH 073/116] EventIndexing: Rename our EventIndexer class. --- src/EventIndexing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EventIndexing.js b/src/EventIndexing.js index 05d5fd03da..38d610bac7 100644 --- a/src/EventIndexing.js +++ b/src/EventIndexing.js @@ -20,7 +20,7 @@ import MatrixClientPeg from "./MatrixClientPeg"; /** * Event indexing class that wraps the platform specific event indexing. */ -export default class EventIndexer { +export default class EventIndex { constructor() { this.crawlerCheckpoints = []; // The time that the crawler will wait between /rooms/{room_id}/messages From b983eaa3f9321a245c0f2f63d16a153dc13c9b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 14:36:08 +0100 Subject: [PATCH 074/116] EventIndex: Rename the file to be consistent with the class. --- src/{EventIndexing.js => EventIndex.js} | 0 src/EventIndexPeg.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{EventIndexing.js => EventIndex.js} (100%) diff --git a/src/EventIndexing.js b/src/EventIndex.js similarity index 100% rename from src/EventIndexing.js rename to src/EventIndex.js diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index a289c9e629..7530dd1a99 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -20,7 +20,7 @@ limitations under the License. */ import PlatformPeg from "./PlatformPeg"; -import EventIndex from "./EventIndexing"; +import EventIndex from "./EventIndex"; class EventIndexPeg { constructor() { From c48ccf9761d1f481da21b661bf88cbebef04da0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 14:40:04 +0100 Subject: [PATCH 075/116] EventIndex: Remove some unnecessary checks if event indexing is supported. --- src/EventIndex.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/EventIndex.js b/src/EventIndex.js index 38d610bac7..e7aee6189e 100644 --- a/src/EventIndex.js +++ b/src/EventIndex.js @@ -40,7 +40,6 @@ export default class EventIndex { async onSync(state, prevState, data) { const indexManager = PlatformPeg.get().getEventIndexingManager(); - if (indexManager === null) return; if (prevState === null && state === "PREPARED") { // Load our stored checkpoints, if any. @@ -115,7 +114,6 @@ export default class EventIndex { async onRoomTimeline(ev, room, toStartOfTimeline, removed, data) { const indexManager = PlatformPeg.get().getEventIndexingManager(); - if (indexManager === null) return; // We only index encrypted rooms locally. if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; @@ -141,7 +139,6 @@ export default class EventIndex { async onEventDecrypted(ev, err) { const indexManager = PlatformPeg.get().getEventIndexingManager(); - if (indexManager === null) return; const eventId = ev.getId(); @@ -153,7 +150,6 @@ export default class EventIndex { async addLiveEventToIndex(ev) { const indexManager = PlatformPeg.get().getEventIndexingManager(); - if (indexManager === null) return; if (["m.room.message", "m.room.name", "m.room.topic"] .indexOf(ev.getType()) == -1) { @@ -349,7 +345,6 @@ export default class EventIndex { async onLimitedTimeline(room) { const indexManager = PlatformPeg.get().getEventIndexingManager(); - if (indexManager === null) return; if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; const timeline = room.getLiveTimeline(); From 8d7e7d0cc404c8d2df9d9602a5f526e3bb01924b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 14:40:38 +0100 Subject: [PATCH 076/116] EventIndex: Remove the unused deleteEventIndex method. We need to support the deletion of the event index even if it's not currently initialized, therefore the deletion ended up in the EventIndexPeg class. --- src/EventIndex.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/EventIndex.js b/src/EventIndex.js index e7aee6189e..6d8f265661 100644 --- a/src/EventIndex.js +++ b/src/EventIndex.js @@ -386,14 +386,6 @@ export default class EventIndex { return indexManager.closeEventIndex(); } - async deleteEventIndex() { - const indexManager = PlatformPeg.get().getEventIndexingManager(); - if (indexManager !== null) { - this.stopCrawler(); - await indexManager.deleteEventIndex(); - } - } - async search(searchArgs) { const indexManager = PlatformPeg.get().getEventIndexingManager(); return indexManager.searchEventIndex(searchArgs); From 4a6623bc00f9047256daaec382e3386b8f83741c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 15:04:22 +0100 Subject: [PATCH 077/116] EventIndex: Rework the crawler cancellation. --- src/EventIndex.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/EventIndex.js b/src/EventIndex.js index 6d8f265661..75e3cda4f2 100644 --- a/src/EventIndex.js +++ b/src/EventIndex.js @@ -29,7 +29,7 @@ export default class EventIndex { // The maximum number of events our crawler should fetch in a single // crawl. this._eventsPerCrawl = 100; - this._crawlerRef = null; + this._crawler = null; this.liveEventsForIndex = new Set(); } @@ -165,7 +165,7 @@ export default class EventIndex { indexManager.addEventToIndex(e, profile); } - async crawlerFunc(handle) { + async crawlerFunc() { // TODO either put this in a better place or find a library provided // method that does this. const sleep = async (ms) => { @@ -179,7 +179,9 @@ export default class EventIndex { const client = MatrixClientPeg.get(); const indexManager = PlatformPeg.get().getEventIndexingManager(); - handle.cancel = () => { + this._crawler = {}; + + this._crawler.cancel = () => { cancelled = true; }; @@ -340,6 +342,8 @@ export default class EventIndex { } } + this._crawler = null; + console.log("EventIndex: Stopping crawler function"); } @@ -366,18 +370,13 @@ export default class EventIndex { } startCrawler() { - if (this._crawlerRef !== null) return; - - const crawlerHandle = {}; - this.crawlerFunc(crawlerHandle); - this._crawlerRef = crawlerHandle; + if (this._crawler !== null) return; + this.crawlerFunc(); } stopCrawler() { - if (this._crawlerRef === null) return; - - this._crawlerRef.cancel(); - this._crawlerRef = null; + if (this._crawler === null) return; + this._crawler.cancel(); } async close() { From 21f00aaeb1c6c47f314c9aed1543b3ba41208811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 18 Nov 2019 15:04:44 +0100 Subject: [PATCH 078/116] EventIndex: Fix some spelling errors. --- src/EventIndex.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EventIndex.js b/src/EventIndex.js index 75e3cda4f2..c96fe25fc8 100644 --- a/src/EventIndex.js +++ b/src/EventIndex.js @@ -282,8 +282,8 @@ export default class EventIndex { // attributes? }; - // TODO if there ar no events at this point we're missing a lot - // decryption keys, do we wan't to retry this checkpoint at a later + // TODO if there are no events at this point we're missing a lot + // decryption keys, do we want to retry this checkpoint at a later // stage? const filteredEvents = matrixEvents.filter(isValidEvent); @@ -336,7 +336,7 @@ export default class EventIndex { } } catch (e) { console.log("EventIndex: Error durring a crawl", e); - // An error occured, put the checkpoint back so we + // An error occurred, put the checkpoint back so we // can retry. this.crawlerCheckpoints.push(checkpoint); } From 50cccd3212b384d3b26460544f2e8e54651f259f Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 15 Nov 2019 10:57:20 +0000 Subject: [PATCH 079/116] Add cross-signing feature flag Fixes https://github.com/vector-im/riot-web/issues/11407 --- src/MatrixClientPeg.js | 10 ++++++++++ src/i18n/strings/en_EN.json | 1 + src/settings/Settings.js | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index bebb254afc..ef0130ec15 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -220,6 +220,16 @@ class MatrixClientPeg { identityServer: new IdentityAuthClient(), }; + if (SettingsStore.isFeatureEnabled("feature_cross_signing")) { + // TODO: Cross-signing keys are temporarily in memory only. A + // separate task in the cross-signing project will build from here. + const keys = []; + opts.cryptoCallbacks = { + getCrossSigningKey: k => keys[k], + saveCrossSigningKeys: newKeys => Object.assign(keys, newKeys), + }; + } + this.matrixClient = createMatrixClient(opts); // we're going to add eventlisteners for each matrix event tile, so the diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index dc9773ad21..8469f62d5c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -342,6 +342,7 @@ "Use the new, consistent UserInfo panel for Room Members and Group Members": "Use the new, consistent UserInfo panel for Room Members and Group Members", "Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)", "Send verification requests in direct message": "Send verification requests in direct message", + "Enable cross-signing to verify per-user instead of per-device": "Enable cross-signing to verify per-user instead of per-device", "Use the new, faster, composer for writing messages": "Use the new, faster, composer for writing messages", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 973d389ba6..c63217775a 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -146,6 +146,12 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, + "feature_cross_signing": { + isFeature: true, + displayName: _td("Enable cross-signing to verify per-user instead of per-device"), + supportedLevels: LEVELS_FEATURE, + default: false, + }, "useCiderComposer": { displayName: _td("Use the new, faster, composer for writing messages"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, From b8c0a0fe7238675dc818d152e0e63720a0a04bf6 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 15 Nov 2019 11:31:19 +0000 Subject: [PATCH 080/116] Reload automatically when changing cross-signing flag --- src/settings/Settings.js | 5 +++-- ...LowBandwidthController.js => ReloadOnChangeController.js} | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) rename src/settings/controllers/{LowBandwidthController.js => ReloadOnChangeController.js} (91%) diff --git a/src/settings/Settings.js b/src/settings/Settings.js index c63217775a..89bca043bd 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -23,7 +23,7 @@ import { } from "./controllers/NotificationControllers"; import CustomStatusController from "./controllers/CustomStatusController"; import ThemeController from './controllers/ThemeController'; -import LowBandwidthController from "./controllers/LowBandwidthController"; +import ReloadOnChangeController from "./controllers/ReloadOnChangeController"; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config']; @@ -151,6 +151,7 @@ export const SETTINGS = { displayName: _td("Enable cross-signing to verify per-user instead of per-device"), supportedLevels: LEVELS_FEATURE, default: false, + controller: new ReloadOnChangeController(), }, "useCiderComposer": { displayName: _td("Use the new, faster, composer for writing messages"), @@ -433,7 +434,7 @@ export const SETTINGS = { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, displayName: _td('Low bandwidth mode'), default: false, - controller: new LowBandwidthController(), + controller: new ReloadOnChangeController(), }, "fallbackICEServerAllowed": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, diff --git a/src/settings/controllers/LowBandwidthController.js b/src/settings/controllers/ReloadOnChangeController.js similarity index 91% rename from src/settings/controllers/LowBandwidthController.js rename to src/settings/controllers/ReloadOnChangeController.js index c7796a425a..eadaee89ca 100644 --- a/src/settings/controllers/LowBandwidthController.js +++ b/src/settings/controllers/ReloadOnChangeController.js @@ -17,7 +17,7 @@ limitations under the License. import SettingController from "./SettingController"; import PlatformPeg from "../../PlatformPeg"; -export default class LowBandwidthController extends SettingController { +export default class ReloadOnChangeController extends SettingController { onChange(level, roomId, newValue) { PlatformPeg.get().reload(); } From 52e7d3505009732015f14be8743e58b4f650797f Mon Sep 17 00:00:00 2001 From: random Date: Mon, 18 Nov 2019 10:19:11 +0000 Subject: [PATCH 081/116] Translated using Weblate (Italian) Currently translated at 99.9% (1906 of 1908 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index 8c7edbadd8..10227d447a 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2270,5 +2270,17 @@ "You cancelled": "Hai annullato", "%(name)s cancelled": "%(name)s ha annullato", "%(name)s wants to verify": "%(name)s vuole verificare", - "You sent a verification request": "Hai inviato una richiesta di verifica" + "You sent a verification request": "Hai inviato una richiesta di verifica", + "Custom (%(level)s)": "Personalizzato (%(level)s)", + "Trusted": "Fidato", + "Not trusted": "Non fidato", + "Hide verified Sign-In's": "Nascondi accessi verificati", + "%(count)s verified Sign-In's|other": "%(count)s accessi verificati", + "%(count)s verified Sign-In's|one": "1 accesso verificato", + "Direct message": "Messaggio diretto", + "Unverify user": "Revoca verifica utente", + "%(role)s in %(roomName)s": "%(role)s in %(roomName)s", + "Messages in this room are end-to-end encrypted.": "I messaggi in questa stanza sono cifrati end-to-end.", + "Security": "Sicurezza", + "Verify": "Verifica" } From fd5e2398852b8b201c62fb7b0ac0d1b8f93b65e2 Mon Sep 17 00:00:00 2001 From: Walter Date: Mon, 18 Nov 2019 13:35:58 +0000 Subject: [PATCH 082/116] Translated using Weblate (Russian) Currently translated at 97.2% (1855 of 1908 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/ru/ --- src/i18n/strings/ru.json | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 01065a9e96..7806ea731b 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -2018,7 +2018,7 @@ "Create a private room": "Создать приватную комнату", "Topic (optional)": "Тема (опционально)", "Make this room public": "Сделать комнату публичной", - "Use the new, faster, composer for writing messages": "Используйте новый, более быстрый, редактор для написания сообщений.", + "Use the new, faster, composer for writing messages": "Используйте новый, более быстрый, редактор для написания сообщений", "Send read receipts for messages (requires compatible homeserver to disable)": "Отправлять подтверждения о прочтении сообщений (требуется отключение совместимого домашнего сервера)", "Show previews/thumbnails for images": "Показать превью / миниатюры для изображений", "Disconnect from the identity server and connect to instead?": "Отключиться от сервера идентификации и вместо этого подключиться к ?", @@ -2050,7 +2050,7 @@ "contact the administrators of identity server ": "связаться с администраторами сервера идентификации ", "wait and try again later": "Подождите и повторите попытку позже", "Error changing power level requirement": "Ошибка изменения требования к уровню прав", - "An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.": "Произошла ошибка при изменении требований к уровню прав комнаты. Убедитесь, что у вас достаточно прав и попробуйте снова.", + "An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.": "Произошла ошибка при изменении требований к уровню доступа комнаты. Убедитесь, что у вас достаточно прав и попробуйте снова.", "Error changing power level": "Ошибка изменения уровня прав", "An error occurred changing the user's power level. Ensure you have sufficient permissions and try again.": "Произошла ошибка при изменении уровня прав пользователя. Убедитесь, что у вас достаточно прав и попробуйте снова.", "Unable to revoke sharing for email address": "Не удается отменить общий доступ к адресу электронной почты", @@ -2165,5 +2165,14 @@ "%(count)s unread messages including mentions.|one": "1 непрочитанное упоминание.", "%(count)s unread messages.|one": "1 непрочитанное сообщение.", "Unread messages.": "Непрочитанные сообщения.", - "Message Actions": "Сообщение действий" + "Message Actions": "Сообщение действий", + "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Это действие требует по умолчанию доступа к серверу идентификации для подтверждения адреса электронной почты или номера телефона, но у сервера нет никакого пользовательского соглашения.", + "Custom (%(level)s)": "Пользовательский (%(level)s)", + "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", + "Try out new ways to ignore people (experimental)": "Попробуйте новые способы игнорировать людей (экспериментальные)", + "Send verification requests in direct message": "Отправить запросы на подтверждение в прямом сообщении", + "My Ban List": "Мой список запрещенных", + "Ignored/Blocked": "Игнорируемые/Заблокированные", + "Error adding ignored user/server": "Ошибка добавления игнорируемого пользователя/сервера", + "Error subscribing to list": "Ошибка при подписке на список" } From f9d1fed74ac0f787ebae41d9d856e47129d6d6f5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 18 Nov 2019 19:00:22 +0000 Subject: [PATCH 083/116] re-add missing case of codepath --- src/components/structures/TimelinePanel.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 3dd5ea761e..e8e23c2f76 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -1076,6 +1076,7 @@ const TimelinePanel = createReactClass({ if (timeline) { // This is a hot-path optimization by skipping a promise tick // by repeating a no-op sync branch in TimelineSet.getTimelineForEvent & MatrixClient.getEventTimeline + this._timelineWindow.load(eventId, INITIAL_SIZE); // in this branch this method will happen in sync time onLoaded(); } else { const prom = this._timelineWindow.load(eventId, INITIAL_SIZE); From 6f8129419b5e3548209599fd1f6eb7baa537fa05 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Mon, 18 Nov 2019 19:47:10 +0000 Subject: [PATCH 084/116] Translated using Weblate (Hungarian) Currently translated at 100.0% (1918 of 1918 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 3c049cc321..003af8240c 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2324,5 +2324,18 @@ "%(role)s in %(roomName)s": "%(role)s a szobában: %(roomName)s", "Messages in this room are end-to-end encrypted.": "Az üzenetek a szobában végponttól végpontig titkosítottak.", "Security": "Biztonság", - "Verify": "Ellenőriz" + "Verify": "Ellenőriz", + "Enable cross-signing to verify per-user instead of per-device": "Kereszt-aláírás engedélyezése eszköz alapú ellenőrzés helyett felhasználó alapú ellenőrzéshez", + "Any of the following data may be shared:": "Az alábbi adatok közül bármelyik megosztásra kerülhet:", + "Your display name": "Megjelenítési neved", + "Your avatar URL": "Profilképed URL-je", + "Your user ID": "Felhasználói azonosítód", + "Your theme": "Témád", + "Riot URL": "Riot URL", + "Room ID": "Szoba azonosító", + "Widget ID": "Kisalkalmazás azon.", + "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Ennek a kisalkalmazásnak a használata adatot oszthat meg %(widgetDomain)s domain-nel és az Integrációs Menedzserrel.", + "Using this widget may share data with %(widgetDomain)s.": "Ennek a kisalkalmazásnak a használata adatot oszthat meg %(widgetDomain)s domain-nel.", + "Widget added by": "A kisalkalmazást hozzáadta", + "This widget may use cookies.": "Ez a kisalkalmazás sütiket használhat." } From f5ec9eb8f470eb0d55c98a9fcc36a3dd6d7e3e47 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 18 Nov 2019 13:16:36 -0700 Subject: [PATCH 085/116] Ensure widgets always have a sender associated with them Fixes https://github.com/vector-im/riot-web/issues/11419 --- src/components/views/elements/PersistentApp.js | 2 +- src/components/views/rooms/AppsDrawer.js | 2 +- src/utils/WidgetUtils.js | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/PersistentApp.js b/src/components/views/elements/PersistentApp.js index d6931850be..391e7728f6 100644 --- a/src/components/views/elements/PersistentApp.js +++ b/src/components/views/elements/PersistentApp.js @@ -67,7 +67,7 @@ module.exports = createReactClass({ return ev.getStateKey() === ActiveWidgetStore.getPersistentWidgetId(); }); const app = WidgetUtils.makeAppConfig( - appEvent.getStateKey(), appEvent.getContent(), appEvent.sender, persistentWidgetInRoomId, + appEvent.getStateKey(), appEvent.getContent(), appEvent.getSender(), persistentWidgetInRoomId, ); const capWhitelist = WidgetUtils.getCapWhitelistForAppTypeInRoomId(app.type, persistentWidgetInRoomId); const AppTile = sdk.getComponent('elements.AppTile'); diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 2a0a7569fb..8e6319e315 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -107,7 +107,7 @@ module.exports = createReactClass({ this.props.room.roomId, WidgetUtils.getRoomWidgets(this.props.room), ); return widgets.map((ev) => { - return WidgetUtils.makeAppConfig(ev.getStateKey(), ev.getContent(), ev.sender); + return WidgetUtils.makeAppConfig(ev.getStateKey(), ev.getContent(), ev.getSender()); }); }, diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index 36907da5ab..eb26ff1484 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -400,7 +400,7 @@ export default class WidgetUtils { return client.setAccountData('m.widgets', userWidgets); } - static makeAppConfig(appId, app, sender, roomId) { + static makeAppConfig(appId, app, senderUserId, roomId) { const myUserId = MatrixClientPeg.get().credentials.userId; const user = MatrixClientPeg.get().getUser(myUserId); const params = { @@ -413,6 +413,11 @@ export default class WidgetUtils { '$theme': SettingsStore.getValue("theme"), }; + if (!senderUserId) { + throw new Error("Widgets must be created by someone - provide a senderUserId"); + } + app.creatorUserId = senderUserId; + app.id = appId; app.name = app.name || app.type; @@ -425,7 +430,6 @@ export default class WidgetUtils { } app.url = encodeUri(app.url, params); - app.creatorUserId = (sender && sender.userId) ? sender.userId : null; return app; } From 8d25952dbbacdcf139b46977199491c449235768 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 18 Nov 2019 14:17:31 -0700 Subject: [PATCH 086/116] Add a bit more safety around breadcrumbs Fixes https://github.com/vector-im/riot-web/issues/11420 --- src/settings/handlers/AccountSettingsHandler.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/settings/handlers/AccountSettingsHandler.js b/src/settings/handlers/AccountSettingsHandler.js index f738bf7971..9c39d98990 100644 --- a/src/settings/handlers/AccountSettingsHandler.js +++ b/src/settings/handlers/AccountSettingsHandler.js @@ -126,6 +126,7 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa if (!content || !content['recent_rooms']) { content = this._getSettings(BREADCRUMBS_LEGACY_EVENT_TYPE); } + if (!content) content = {}; // If we still don't have content, make some content['recent_rooms'] = newValue; return MatrixClientPeg.get().setAccountData(BREADCRUMBS_EVENT_TYPE, content); @@ -167,7 +168,7 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa // This seems fishy - try and get the event for the new rooms const newType = this._getSettings(BREADCRUMBS_EVENT_TYPE); if (newType) val = newType['recent_rooms']; - else val = event.getContent()['rooms']; + else val = event.getContent()['rooms'] || []; } else if (event.getType() === BREADCRUMBS_EVENT_TYPE) { val = event.getContent()['recent_rooms']; } else { From 2f89f284965951013e8716df89aae5b8b622349f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 18 Nov 2019 14:25:04 -0700 Subject: [PATCH 087/116] Remove extraneous paranoia The value is nullchecked later on. --- src/settings/handlers/AccountSettingsHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/handlers/AccountSettingsHandler.js b/src/settings/handlers/AccountSettingsHandler.js index 9c39d98990..7b05ad0c1b 100644 --- a/src/settings/handlers/AccountSettingsHandler.js +++ b/src/settings/handlers/AccountSettingsHandler.js @@ -168,7 +168,7 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa // This seems fishy - try and get the event for the new rooms const newType = this._getSettings(BREADCRUMBS_EVENT_TYPE); if (newType) val = newType['recent_rooms']; - else val = event.getContent()['rooms'] || []; + else val = event.getContent()['rooms']; } else if (event.getType() === BREADCRUMBS_EVENT_TYPE) { val = event.getContent()['recent_rooms']; } else { From d2a99183595df253cdf4d97eee9c494e890fc4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 19 Nov 2019 09:26:46 +0100 Subject: [PATCH 088/116] EventIndex: Remove some unused variables and some trailing whitespace. --- src/EventIndex.js | 4 ---- src/EventIndexPeg.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/EventIndex.js b/src/EventIndex.js index c96fe25fc8..7ed43ad31c 100644 --- a/src/EventIndex.js +++ b/src/EventIndex.js @@ -113,8 +113,6 @@ export default class EventIndex { } async onRoomTimeline(ev, room, toStartOfTimeline, removed, data) { - const indexManager = PlatformPeg.get().getEventIndexingManager(); - // We only index encrypted rooms locally. if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; @@ -138,8 +136,6 @@ export default class EventIndex { } async onEventDecrypted(ev, err) { - const indexManager = PlatformPeg.get().getEventIndexingManager(); - const eventId = ev.getId(); // If the event isn't in our live event set, ignore it. diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index 7530dd1a99..266b8f2d53 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -15,7 +15,7 @@ limitations under the License. */ /* - * Object holding the global EventIndex object. Can only be initialized if the + * Object holding the global EventIndex object. Can only be initialized if the * platform supports event indexing. */ From 92292003c8fcbe741fbe95e50bb54e5cee68fdb6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 19 Nov 2019 12:51:02 +0100 Subject: [PATCH 089/116] make shield on verification request scale correctly by not overriding `mask-size` using `mask` for `mask-image` --- res/css/views/messages/_MKeyVerificationRequest.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/messages/_MKeyVerificationRequest.scss b/res/css/views/messages/_MKeyVerificationRequest.scss index b4cde4e7ef..87a75dee82 100644 --- a/res/css/views/messages/_MKeyVerificationRequest.scss +++ b/res/css/views/messages/_MKeyVerificationRequest.scss @@ -25,7 +25,7 @@ limitations under the License. width: 12px; height: 16px; content: ""; - mask: url("$(res)/img/e2e/normal.svg"); + mask-image: url("$(res)/img/e2e/normal.svg"); mask-repeat: no-repeat; mask-size: 100%; margin-top: 4px; @@ -33,7 +33,7 @@ limitations under the License. } &.mx_KeyVerification_icon_verified::after { - mask: url("$(res)/img/e2e/verified.svg"); + mask-image: url("$(res)/img/e2e/verified.svg"); background-color: $accent-color; } From de15965c4a59495cc47e364833710410d0adfd19 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 19 Nov 2019 12:51:37 +0100 Subject: [PATCH 090/116] improve device list layout --- res/css/views/right_panel/_UserInfo.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/res/css/views/right_panel/_UserInfo.scss b/res/css/views/right_panel/_UserInfo.scss index c68f3ffd37..df7d0a5f87 100644 --- a/res/css/views/right_panel/_UserInfo.scss +++ b/res/css/views/right_panel/_UserInfo.scss @@ -195,6 +195,8 @@ limitations under the License. .mx_UserInfo_devices { .mx_UserInfo_device { display: flex; + margin: 8px 0; + &.mx_UserInfo_device_verified { .mx_UserInfo_device_trusted { @@ -210,6 +212,7 @@ limitations under the License. .mx_UserInfo_device_name { flex: 1; margin-right: 5px; + word-break: break-word; } } From 39939de04ffbc29c66e68789f0d70372afcbc72e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 19 Nov 2019 12:51:46 +0100 Subject: [PATCH 091/116] =?UTF-8?q?remove=20white=20background=20on=20!=20?= =?UTF-8?q?and=20=E2=9C=85=20so=20it=20looks=20better=20on=20dark=20theme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- res/css/views/rooms/_E2EIcon.scss | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/res/css/views/rooms/_E2EIcon.scss b/res/css/views/rooms/_E2EIcon.scss index bc11ac6e1c..1ee5008888 100644 --- a/res/css/views/rooms/_E2EIcon.scss +++ b/res/css/views/rooms/_E2EIcon.scss @@ -22,21 +22,6 @@ limitations under the License. display: block; } -.mx_E2EIcon_verified::before, .mx_E2EIcon_warning::before { - content: ""; - display: block; - /* the symbols in the shield icons are cut out to make it themeable with css masking. - if they appear on a different background than white, the symbol wouldn't be white though, so we - add a rectangle here below the masked element to shine through the symbol cut-out. - hardcoding white and not using a theme variable as this would probably be white for any theme. */ - background-color: white; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} - .mx_E2EIcon_verified::after, .mx_E2EIcon_warning::after { content: ""; display: block; @@ -49,23 +34,11 @@ limitations under the License. mask-size: contain; } -.mx_E2EIcon_verified::before { - /* white rectangle below checkmark of shield */ - margin: 25% 28% 38% 25%; -} - - .mx_E2EIcon_verified::after { mask-image: url('$(res)/img/e2e/verified.svg'); background-color: $accent-color; } - -.mx_E2EIcon_warning::before { - /* white rectangle below "!" of shield */ - margin: 18% 40% 25% 40%; -} - .mx_E2EIcon_warning::after { mask-image: url('$(res)/img/e2e/warning.svg'); background-color: $warning-color; From 5f7b0fef334763df3a83aac563bf533574006101 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 19 Nov 2019 12:55:28 +0100 Subject: [PATCH 092/116] scale (new) icons to fit available size fixes https://github.com/vector-im/riot-web/issues/11399 --- res/css/views/rooms/_MemberDeviceInfo.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/rooms/_MemberDeviceInfo.scss b/res/css/views/rooms/_MemberDeviceInfo.scss index 951d1945b1..e73e6c58f1 100644 --- a/res/css/views/rooms/_MemberDeviceInfo.scss +++ b/res/css/views/rooms/_MemberDeviceInfo.scss @@ -25,6 +25,7 @@ limitations under the License. width: 12px; height: 12px; mask-repeat: no-repeat; + mask-size: 100%; } .mx_MemberDeviceInfo_icon_blacklisted { mask-image: url('$(res)/img/e2e/blacklisted.svg'); From 6017473caf8e23e31666924a17fb5f6ce60af42d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 19 Nov 2019 10:46:18 +0100 Subject: [PATCH 093/116] EventIndex: Move the event listener registration into the EventIndex class. --- src/EventIndex.js | 41 +++++++++++++++++++++++-- src/EventIndexPeg.js | 3 +- src/components/structures/MatrixChat.js | 26 ---------------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/EventIndex.js b/src/EventIndex.js index 7ed43ad31c..b6784cd331 100644 --- a/src/EventIndex.js +++ b/src/EventIndex.js @@ -31,11 +31,45 @@ export default class EventIndex { this._eventsPerCrawl = 100; this._crawler = null; this.liveEventsForIndex = new Set(); + + this.boundOnSync = async (state, prevState, data) => { + await this.onSync(state, prevState, data); + }; + this.boundOnRoomTimeline = async ( ev, room, toStartOfTimeline, removed, + data) => { + await this.onRoomTimeline(ev, room, toStartOfTimeline, removed, data); + }; + this.boundOnEventDecrypted = async (ev, err) => { + await this.onEventDecrypted(ev, err); + }; + this.boundOnTimelineReset = async (room, timelineSet, + resetAllTimelines) => await this.onTimelineReset(room); } async init() { const indexManager = PlatformPeg.get().getEventIndexingManager(); - return indexManager.initEventIndex(); + await indexManager.initEventIndex(); + + this.registerListeners(); + } + + registerListeners() { + const client = MatrixClientPeg.get(); + + client.on('sync', this.boundOnSync); + client.on('Room.timeline', this.boundOnRoomTimeline); + client.on('Event.decrypted', this.boundOnEventDecrypted); + client.on('Room.timelineReset', this.boundOnTimelineReset); + } + + removeListeners() { + const client = MatrixClientPeg.get(); + if (client === null) return; + + client.removeListener('sync', this.boundOnSync); + client.removeListener('Room.timeline', this.boundOnRoomTimeline); + client.removeListener('Event.decrypted', this.boundOnEventDecrypted); + client.removeListener('Room.timelineReset', this.boundOnTimelineReset); } async onSync(state, prevState, data) { @@ -343,7 +377,9 @@ export default class EventIndex { console.log("EventIndex: Stopping crawler function"); } - async onLimitedTimeline(room) { + async onTimelineReset(room) { + if (room === null) return; + const indexManager = PlatformPeg.get().getEventIndexingManager(); if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; @@ -377,6 +413,7 @@ export default class EventIndex { async close() { const indexManager = PlatformPeg.get().getEventIndexingManager(); + this.removeListeners(); this.stopCrawler(); return indexManager.closeEventIndex(); } diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index 266b8f2d53..74b7968c70 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -97,10 +97,9 @@ class EventIndexPeg { const indexManager = PlatformPeg.get().getEventIndexingManager(); if (indexManager !== null) { - this.stop(); + this.unset(); console.log("EventIndex: Deleting event index."); await indexManager.deleteEventIndex(); - this.index = null; } } } diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index b45884e64f..da67416400 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -31,7 +31,6 @@ import Analytics from "../../Analytics"; import { DecryptionFailureTracker } from "../../DecryptionFailureTracker"; import MatrixClientPeg from "../../MatrixClientPeg"; import PlatformPeg from "../../PlatformPeg"; -import EventIndexPeg from "../../EventIndexPeg"; import SdkConfig from "../../SdkConfig"; import * as RoomListSorter from "../../RoomListSorter"; import dis from "../../dispatcher"; @@ -1288,31 +1287,6 @@ export default createReactClass({ return self._loggedInView.child.canResetTimelineInRoom(roomId); }); - cli.on('sync', async (state, prevState, data) => { - const eventIndex = EventIndexPeg.get(); - if (eventIndex === null) return; - await eventIndex.onSync(state, prevState, data); - }); - - cli.on("Room.timeline", async (ev, room, toStartOfTimeline, removed, data) => { - const eventIndex = EventIndexPeg.get(); - if (eventIndex === null) return; - await eventIndex.onRoomTimeline(ev, room, toStartOfTimeline, removed, data); - }); - - cli.on("Event.decrypted", async (ev, err) => { - const eventIndex = EventIndexPeg.get(); - if (eventIndex === null) return; - await eventIndex.onEventDecrypted(ev, err); - }); - - cli.on("Room.timelineReset", async (room, timelineSet, resetAllTimelines) => { - const eventIndex = EventIndexPeg.get(); - if (eventIndex === null) return; - if (room === null) return; - await eventIndex.onLimitedTimeline(room); - }); - cli.on('sync', function(state, prevState, data) { // LifecycleStore and others cannot directly subscribe to matrix client for // events because flux only allows store state changes during flux dispatches. From 979803797fb58bb421c1e1a98cf90f263ae3af91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 19 Nov 2019 11:05:37 +0100 Subject: [PATCH 094/116] Lifecycle: Make the clear storage method async. --- src/Lifecycle.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 1d38934ade..1b69ca6ade 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -607,20 +607,20 @@ async function startMatrixClient(startSyncing=true) { * Stops a running client and all related services, and clears persistent * storage. Used after a session has been logged out. */ -export function onLoggedOut() { +export async function onLoggedOut() { _isLoggingOut = false; // Ensure that we dispatch a view change **before** stopping the client so // so that React components unmount first. This avoids React soft crashes // that can occur when components try to use a null client. dis.dispatch({action: 'on_logged_out'}); stopMatrixClient(); - _clearStorage().done(); + await _clearStorage(); } /** * @returns {Promise} promise which resolves once the stores have been cleared */ -function _clearStorage() { +async function _clearStorage() { Analytics.logout(); if (window.localStorage) { @@ -633,12 +633,8 @@ function _clearStorage() { baseUrl: "", }); - const clear = async () => { - await EventIndexPeg.deleteEventIndex(); - await cli.clearStores(); - }; - - return clear(); + await EventIndexPeg.deleteEventIndex(); + await cli.clearStores(); } /** @@ -662,7 +658,7 @@ export function stopMatrixClient(unsetClient=true) { if (unsetClient) { MatrixClientPeg.unset(); - EventIndexPeg.unset().done(); + EventIndexPeg.unset(); } } } From f776bdcc8b4306557df2bda71bce6ec097694abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 19 Nov 2019 12:23:49 +0100 Subject: [PATCH 095/116] EventIndex: Hide the feature behind a labs flag. --- src/EventIndexPeg.js | 5 +++++ src/i18n/strings/en_EN.json | 3 ++- src/settings/Settings.js | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/EventIndexPeg.js b/src/EventIndexPeg.js index 74b7968c70..eb4caa2ca4 100644 --- a/src/EventIndexPeg.js +++ b/src/EventIndexPeg.js @@ -21,6 +21,7 @@ limitations under the License. import PlatformPeg from "./PlatformPeg"; import EventIndex from "./EventIndex"; +import SettingsStore from './settings/SettingsStore'; class EventIndexPeg { constructor() { @@ -34,6 +35,10 @@ class EventIndexPeg { * EventIndex was successfully initialized, false otherwise. */ async init() { + if (!SettingsStore.isFeatureEnabled("feature_event_indexing")) { + return false; + } + const indexManager = PlatformPeg.get().getEventIndexingManager(); if (!indexManager || await indexManager.supportsEventIndexing() !== true) { console.log("EventIndex: Platform doesn't support event indexing,", diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f524a22d4b..69c3f07f3f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1829,5 +1829,6 @@ "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", "Failed to set direct chat tag": "Failed to set direct chat tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", - "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" + "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room", + "Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)" } diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 3c33ae57fe..8abd845f0c 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -120,6 +120,12 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, + "feature_event_indexing": { + isFeature: true, + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + displayName: _td("Enable local event indexing and E2EE search (requires restart)"), + default: false, + }, "useCiderComposer": { displayName: _td("Use the new, faster, composer for writing messages"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, From e9df973c8273f7a0958a69e257cd2d9204ce8404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 19 Nov 2019 12:52:12 +0100 Subject: [PATCH 096/116] EventIndex: Move the event indexing files into a separate folder. --- src/BasePlatform.js | 2 +- src/Lifecycle.js | 2 +- src/Searching.js | 2 +- src/{ => indexing}/BaseEventIndexManager.js | 0 src/{ => indexing}/EventIndex.js | 4 ++-- src/{ => indexing}/EventIndexPeg.js | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) rename src/{ => indexing}/BaseEventIndexManager.js (100%) rename src/{ => indexing}/EventIndex.js (99%) rename src/{ => indexing}/EventIndexPeg.js (95%) diff --git a/src/BasePlatform.js b/src/BasePlatform.js index f6301fd173..14e34a1f40 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -19,7 +19,7 @@ limitations under the License. */ import dis from './dispatcher'; -import BaseEventIndexManager from './BaseEventIndexManager'; +import BaseEventIndexManager from './indexing/BaseEventIndexManager'; /** * Base class for classes that provide platform-specific functionality diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 1b69ca6ade..65fa0b29ce 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -20,7 +20,7 @@ import Promise from 'bluebird'; import Matrix from 'matrix-js-sdk'; import MatrixClientPeg from './MatrixClientPeg'; -import EventIndexPeg from './EventIndexPeg'; +import EventIndexPeg from './indexing/EventIndexPeg'; import createMatrixClient from './utils/createMatrixClient'; import Analytics from './Analytics'; import Notifier from './Notifier'; diff --git a/src/Searching.js b/src/Searching.js index ca3e7f041f..f8976c92e4 100644 --- a/src/Searching.js +++ b/src/Searching.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import EventIndexPeg from "./EventIndexPeg"; +import EventIndexPeg from "./indexing/EventIndexPeg"; import MatrixClientPeg from "./MatrixClientPeg"; function serverSideSearch(term, roomId = undefined) { diff --git a/src/BaseEventIndexManager.js b/src/indexing/BaseEventIndexManager.js similarity index 100% rename from src/BaseEventIndexManager.js rename to src/indexing/BaseEventIndexManager.js diff --git a/src/EventIndex.js b/src/indexing/EventIndex.js similarity index 99% rename from src/EventIndex.js rename to src/indexing/EventIndex.js index b6784cd331..df81667c6e 100644 --- a/src/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import PlatformPeg from "./PlatformPeg"; -import MatrixClientPeg from "./MatrixClientPeg"; +import PlatformPeg from "../PlatformPeg"; +import MatrixClientPeg from "../MatrixClientPeg"; /** * Event indexing class that wraps the platform specific event indexing. diff --git a/src/EventIndexPeg.js b/src/indexing/EventIndexPeg.js similarity index 95% rename from src/EventIndexPeg.js rename to src/indexing/EventIndexPeg.js index eb4caa2ca4..c0bdd74ff4 100644 --- a/src/EventIndexPeg.js +++ b/src/indexing/EventIndexPeg.js @@ -19,9 +19,9 @@ limitations under the License. * platform supports event indexing. */ -import PlatformPeg from "./PlatformPeg"; -import EventIndex from "./EventIndex"; -import SettingsStore from './settings/SettingsStore'; +import PlatformPeg from "../PlatformPeg"; +import EventIndex from "../indexing/EventIndex"; +import SettingsStore from '../settings/SettingsStore'; class EventIndexPeg { constructor() { From 43884923e839fc900ab51fd8aef5a7ed903c6372 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 19 Nov 2019 14:07:14 +0100 Subject: [PATCH 097/116] merge the feature_user_info_panel flag into feature_dm_verification --- src/components/structures/RightPanel.js | 4 ++-- src/i18n/strings/en_EN.json | 3 +-- src/settings/Settings.js | 9 ++------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js index 48d272f6c9..895f6ae57e 100644 --- a/src/components/structures/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -185,7 +185,7 @@ export default class RightPanel extends React.Component { } else if (this.state.phase === RightPanel.Phase.GroupRoomList) { panel = ; } else if (this.state.phase === RightPanel.Phase.RoomMemberInfo) { - if (SettingsStore.isFeatureEnabled("feature_user_info_panel")) { + if (SettingsStore.isFeatureEnabled("feature_dm_verification")) { const onClose = () => { dis.dispatch({ action: "view_user", @@ -204,7 +204,7 @@ export default class RightPanel extends React.Component { } else if (this.state.phase === RightPanel.Phase.Room3pidMemberInfo) { panel = ; } else if (this.state.phase === RightPanel.Phase.GroupMemberInfo) { - if (SettingsStore.isFeatureEnabled("feature_user_info_panel")) { + if (SettingsStore.isFeatureEnabled("feature_dm_verification")) { const onClose = () => { dis.dispatch({ action: "view_user", diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 9f13d133c4..473efdfb76 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -340,9 +340,8 @@ "Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)", "Render simple counters in room header": "Render simple counters in room header", "Multiple integration managers": "Multiple integration managers", - "Use the new, consistent UserInfo panel for Room Members and Group Members": "Use the new, consistent UserInfo panel for Room Members and Group Members", "Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)", - "Send verification requests in direct message": "Send verification requests in direct message", + "Send verification requests in direct message, including a new verification UX in the member panel.": "Send verification requests in direct message, including a new verification UX in the member panel.", "Enable cross-signing to verify per-user instead of per-device": "Enable cross-signing to verify per-user instead of per-device", "Use the new, faster, composer for writing messages": "Use the new, faster, composer for writing messages", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 89bca043bd..718a0daec3 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -120,12 +120,6 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, - "feature_user_info_panel": { - isFeature: true, - displayName: _td("Use the new, consistent UserInfo panel for Room Members and Group Members"), - supportedLevels: LEVELS_FEATURE, - default: false, - }, "feature_mjolnir": { isFeature: true, displayName: _td("Try out new ways to ignore people (experimental)"), @@ -142,7 +136,8 @@ export const SETTINGS = { }, "feature_dm_verification": { isFeature: true, - displayName: _td("Send verification requests in direct message"), + displayName: _td("Send verification requests in direct message," + + " including a new verification UX in the member panel."), supportedLevels: LEVELS_FEATURE, default: false, }, From 27d1e4fbbedb6ccf1bccdf17bd1cee74dee4c224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 19 Nov 2019 14:17:51 +0100 Subject: [PATCH 098/116] Fix the translations en_EN file by regenerating it. --- src/i18n/strings/en_EN.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 69c3f07f3f..6f116cbac2 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -334,6 +334,7 @@ "Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)", "Render simple counters in room header": "Render simple counters in room header", "Multiple integration managers": "Multiple integration managers", + "Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)", "Use the new, faster, composer for writing messages": "Use the new, faster, composer for writing messages", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", @@ -1829,6 +1830,5 @@ "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", "Failed to set direct chat tag": "Failed to set direct chat tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", - "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room", - "Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)" + "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" } From da2665f4a331d2c91ad07ff4e9ee59132cea3575 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Tue, 19 Nov 2019 06:47:53 +0000 Subject: [PATCH 099/116] Translated using Weblate (Albanian) Currently translated at 99.7% (1913 of 1918 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sq/ --- src/i18n/strings/sq.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index 2bf5732131..4d0ad6582b 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -2292,5 +2292,17 @@ "%(role)s in %(roomName)s": "%(role)s në %(roomName)s", "Messages in this room are end-to-end encrypted.": "Mesazhet në këtë dhomë janë të fshehtëzuara skaj-më-skaj.", "Security": "Siguri", - "Verify": "Verifikoje" + "Verify": "Verifikoje", + "Any of the following data may be shared:": "Mund të ndahen me të tjerët cilado prej të dhënave vijuese:", + "Your display name": "Emri juaj në ekran", + "Your avatar URL": "URL-ja e avatarit tuaj", + "Your user ID": "ID-ja juaj e përdoruesit", + "Your theme": "Tema juaj", + "Riot URL": "URL Riot-i", + "Room ID": "ID dhome", + "Widget ID": "ID widget-i", + "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Përdorimi i këtij widget-i mund të sjellë ndarje të dhënash me %(widgetDomain)s & Përgjegjësin tuaj të Integrimeve.", + "Using this widget may share data with %(widgetDomain)s.": "Përdorimi i këtij widget-i mund të sjellë ndarje të dhënash me %(widgetDomain)s.", + "Widget added by": "Widget i shtuar nga", + "This widget may use cookies.": "Ky widget mund të përdorë cookies." } From 0c0437ebf501c4da485460343dff6a6d25ad0540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Tue, 19 Nov 2019 08:04:08 +0000 Subject: [PATCH 100/116] Translated using Weblate (French) Currently translated at 100.0% (1918 of 1918 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index eef9438761..824da9d3ff 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2337,5 +2337,18 @@ "%(role)s in %(roomName)s": "%(role)s dans %(roomName)s", "Messages in this room are end-to-end encrypted.": "Les messages dans ce salon sont chiffrés de bout en bout.", "Security": "Sécurité", - "Verify": "Vérifier" + "Verify": "Vérifier", + "Enable cross-signing to verify per-user instead of per-device": "Activer la signature croisée pour vérifier par utilisateur et non par appareil", + "Any of the following data may be shared:": "Les données suivants peuvent être partagées :", + "Your display name": "Votre nom d’affichage", + "Your avatar URL": "L’URL de votre avatar", + "Your user ID": "Votre identifiant utilisateur", + "Your theme": "Votre thème", + "Riot URL": "URL de Riot", + "Room ID": "Identifiant du salon", + "Widget ID": "Identifiant du widget", + "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "L’utilisation de ce widget pourrait partager des données avec %(widgetDomain)s et votre gestionnaire d’intégrations.", + "Using this widget may share data with %(widgetDomain)s.": "L’utilisation de ce widget pourrait partager des données avec %(widgetDomain)s.", + "Widget added by": "Widget ajouté par", + "This widget may use cookies.": "Ce widget pourrait utiliser des cookies." } From dbee3a1215c2c3022ecc3ed87c9efeabf507dbae Mon Sep 17 00:00:00 2001 From: random Date: Tue, 19 Nov 2019 09:21:14 +0000 Subject: [PATCH 101/116] Translated using Weblate (Italian) Currently translated at 99.9% (1916 of 1918 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index 10227d447a..efab4595f6 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2282,5 +2282,18 @@ "%(role)s in %(roomName)s": "%(role)s in %(roomName)s", "Messages in this room are end-to-end encrypted.": "I messaggi in questa stanza sono cifrati end-to-end.", "Security": "Sicurezza", - "Verify": "Verifica" + "Verify": "Verifica", + "Enable cross-signing to verify per-user instead of per-device": "Attiva la firma incrociata per verificare per-utente invece che per-dispositivo", + "Any of the following data may be shared:": "Possono essere condivisi tutti i seguenti dati:", + "Your display name": "Il tuo nome visualizzato", + "Your avatar URL": "L'URL del tuo avatar", + "Your user ID": "Il tuo ID utente", + "Your theme": "Il tuo tema", + "Riot URL": "URL di Riot", + "Room ID": "ID stanza", + "Widget ID": "ID widget", + "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Usando questo widget i dati possono essere condivisi con %(widgetDomain)s e il tuo Gestore di Integrazione.", + "Using this widget may share data with %(widgetDomain)s.": "Usando questo widget i dati possono essere condivisi con %(widgetDomain)s.", + "Widget added by": "Widget aggiunto da", + "This widget may use cookies.": "Questo widget può usare cookie." } From afeab31ce6a3cd784606e4caa4624f30832dd3a8 Mon Sep 17 00:00:00 2001 From: fenuks Date: Tue, 19 Nov 2019 00:34:13 +0000 Subject: [PATCH 102/116] Translated using Weblate (Polish) Currently translated at 73.8% (1415 of 1918 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/pl/ --- src/i18n/strings/pl.json | 46 +++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/i18n/strings/pl.json b/src/i18n/strings/pl.json index 31f82bc2dd..4054c48f97 100644 --- a/src/i18n/strings/pl.json +++ b/src/i18n/strings/pl.json @@ -320,7 +320,7 @@ "Mobile phone number (optional)": "Numer telefonu komórkowego (opcjonalne)", "Moderator": "Moderator", "%(serverName)s Matrix ID": "%(serverName)s Matrix ID", - "Name": "Imię", + "Name": "Nazwa", "Never send encrypted messages to unverified devices from this device": "Nigdy nie wysyłaj zaszyfrowanych wiadomości do niezweryfikowanych urządzeń z tego urządzenia", "Never send encrypted messages to unverified devices in this room from this device": "Nigdy nie wysyłaj niezaszyfrowanych wiadomości do niezweryfikowanych urządzeń z tego urządzenia", "New address (e.g. #foo:%(localDomain)s)": "Nowy adres (np. #foo:%(localDomain)s)", @@ -972,7 +972,7 @@ "Disinvite this user?": "Anulować zaproszenie tego użytkownika?", "Unignore": "Przestań ignorować", "Jump to read receipt": "Przeskocz do potwierdzenia odczytu", - "Share Link to User": "Udostępnij link do użytkownika", + "Share Link to User": "Udostępnij odnośnik do użytkownika", "At this time it is not possible to reply with a file so this will be sent without being a reply.": "W tej chwili nie można odpowiedzieć plikiem, więc zostanie wysłany nie będąc odpowiedzią.", "Unable to reply": "Nie udało się odpowiedzieć", "At this time it is not possible to reply with an emote.": "W tej chwili nie można odpowiedzieć emotikoną.", @@ -1556,7 +1556,7 @@ "Order rooms in the room list by most important first instead of most recent": "Kolejkuj pokoje na liście pokojów od najważniejszych niż od najnowszych", "Show hidden events in timeline": "Pokaż ukryte wydarzenia na linii czasowej", "Low bandwidth mode": "Tryb wolnej przepustowości", - "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Powzól na awaryjny serwer wspomagania połączeń turn.matrix.org, gdy Twój serwer domowy takiego nie oferuje (Twój adres IP będzie udostępniony podczas połączenia)", + "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Pozwól na awaryjny serwer wspomagania połączeń turn.matrix.org, gdy Twój serwer domowy takiego nie oferuje (Twój adres IP będzie udostępniony podczas połączenia)", "Messages containing my username": "Wiadomości zawierające moją nazwę użytkownika", "Encrypted messages in one-to-one chats": "Zaszyforwane wiadomości w rozmowach jeden-do-jednego", "Encrypted messages in group chats": "Zaszyfrowane wiadomości w rozmowach grupowych", @@ -1619,7 +1619,7 @@ "Disconnect Identity Server": "Odłącz Serwer Tożsamości", "Disconnect": "Odłącz", "Identity Server (%(server)s)": "Serwer tożsamości (%(server)s)", - "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Używasz aby odkrywać i być odkrywanym przez isteniejące kontakty, które znasz. Możesz zmienić serwer tożsamości poniżej.", + "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Używasz , aby odnajdywać i móc być odnajdywanym przez istniejące kontakty, które znasz. Możesz zmienić serwer tożsamości poniżej.", "Identity Server": "Serwer Tożsamości", "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "Nie używasz serwera tożsamości. Aby odkrywać i być odkrywanym przez istniejące kontakty które znasz, dodaj jeden poniżej.", "Disconnecting from your identity server will mean you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Odłączenie się od serwera tożsamości oznacza, że nie będzie możliwości wykrycia przez innych użytkowników oraz nie będzie możliwości zaproszenia innych e-mailem lub za pomocą telefonu.", @@ -1653,5 +1653,41 @@ "You do not have the required permissions to use this command.": "Nie posiadasz wymaganych uprawnień do użycia tego polecenia.", "Changes the avatar of the current room": "Zmienia awatar dla obecnego pokoju", "Use an identity server": "Użyj serwera tożsamości", - "Show previews/thumbnails for images": "Pokaż podgląd/miniatury obrazów" + "Show previews/thumbnails for images": "Pokaż podgląd/miniatury obrazów", + "Trust": "Zaufaj", + "Custom (%(level)s)": "Własny (%(level)s)", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Użyj serwera tożsamości, by zaprosić z użyciem adresu e-mail. Kliknij dalej, żeby użyć domyślnego serwera tożsamości (%(defaultIdentityServerName)s), lub zmień w Ustawieniach.", + "Use an identity server to invite by email. Manage in Settings.": "Użyj serwera tożsamości, by zaprosić za pomocą adresu e-mail. Zarządzaj w ustawieniach.", + "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", + "Use the new, consistent UserInfo panel for Room Members and Group Members": "Użyj nowego, spójnego panelu informacji o użytkowniku dla członków pokoju i grup", + "Try out new ways to ignore people (experimental)": "Wypróbuj nowe sposoby na ignorowanie ludzi (eksperymentalne)", + "Send verification requests in direct message": "Wysyłaj prośby o weryfikację w bezpośredniej wiadomości", + "Use the new, faster, composer for writing messages": "Używaj nowego, szybszego kompozytora do pisania wiadomości", + "My Ban List": "Moja lista zablokowanych", + "This is your list of users/servers you have blocked - don't leave the room!": "To jest Twoja lista zablokowanych użytkowników/serwerów – nie opuszczaj tego pokoju!", + "Change identity server": "Zmień serwer tożsamości", + "Disconnect from the identity server and connect to instead?": "Rozłączyć się z serwerem tożsamości i połączyć się w jego miejsce z ?", + "Disconnect identity server": "Odłączanie serwera tożsamości", + "You should:": "Należy:", + "check your browser plugins for anything that might block the identity server (such as Privacy Badger)": "sprawdzić rozszerzenia przeglądarki, które mogą blokować serwer tożsamości (takie jak Privacy Badger)", + "contact the administrators of identity server ": "skontaktować się z administratorami serwera tożsamości ", + "wait and try again later": "zaczekaj i spróbuj ponownie później", + "Disconnect anyway": "Odłącz mimo to", + "You are still sharing your personal data on the identity server .": "W dalszym ciągu udostępniasz swoje dane osobowe na serwerze tożsamości .", + "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Zalecamy, by usunąć swój adres e-mail i numer telefonu z serwera tożsamości przed odłączeniem.", + "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.": "Jeżeli nie chcesz używać do odnajdywania i bycia odnajdywanym przez osoby, które znasz, wpisz inny serwer tożsamości poniżej.", + "Using an identity server is optional. If you choose not to use an identity server, you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Używanie serwera tożsamości jest opcjonalne. Jeżeli postanowisz nie używać serwera tożsamości, pozostali użytkownicy nie będą w stanie Cię odnaleźć ani nie będziesz mógł zaprosić innych po adresie e-mail czy numerze telefonu.", + "Do not use an identity server": "Nie używaj serwera tożsamości", + "Clear cache and reload": "Wyczyść pamięć podręczną i przeładuj", + "Something went wrong. Please try again or view your console for hints.": "Coś poszło nie tak. Spróbuj ponownie lub sprawdź konsolę przeglądarki dla wskazówek.", + "Please verify the room ID or alias and try again.": "Zweryfikuj poprawność ID pokoju lub nazwy zastępczej i spróbuj ponownie.", + "Please try again or view your console for hints.": "Spróbuj ponownie lub sprawdź konsolę przeglądarki dla wskazówek.", + "Personal ban list": "Osobista lista zablokowanych", + "Server or user ID to ignore": "ID serwera lub użytkownika do zignorowania", + "eg: @bot:* or example.org": "np: @bot:* lub przykład.pl", + "Composer": "Kompozytor", + "Autocomplete delay (ms)": "Opóźnienie autouzupełniania (ms)", + "Explore": "Przeglądaj", + "Filter": "Filtruj", + "Add room": "Dodaj pokój" } From 0eedab4154c18a39d9ae193a9eaf0ed2a34a3077 Mon Sep 17 00:00:00 2001 From: Alexis Date: Tue, 19 Nov 2019 05:46:11 +0000 Subject: [PATCH 103/116] Translated using Weblate (Portuguese) Currently translated at 33.3% (638 of 1918 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/pt/ --- src/i18n/strings/pt.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/pt.json b/src/i18n/strings/pt.json index 7cc80cfc78..5a56e807e4 100644 --- a/src/i18n/strings/pt.json +++ b/src/i18n/strings/pt.json @@ -842,5 +842,11 @@ "Collapse panel": "Colapsar o painel", "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!": "Com o seu navegador atual, a aparência e sensação de uso da aplicação podem estar completamente incorretas, e algumas das funcionalidades poderão não funcionar. Se quiser tentar de qualquer maneira pode continuar, mas está por sua conta com algum problema que possa encontrar!", "Checking for an update...": "A procurar uma atualização...", - "There are advanced notifications which are not shown here": "Existem notificações avançadas que não são exibidas aqui" + "There are advanced notifications which are not shown here": "Existem notificações avançadas que não são exibidas aqui", + "Add Email Address": "Adicione adresso de e-mail", + "Add Phone Number": "Adicione número de telefone", + "The platform you're on": "A plataforma em que se encontra", + "The version of Riot.im": "A versão do RIOT.im", + "Whether or not you're logged in (we don't record your username)": "Tenha ou não, iniciado sessão (não iremos guardar o seu username)", + "Your language of choice": "O seu idioma de escolha" } From de0287213e240aff60a3e9e4fff9421dab42715f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 19 Nov 2019 14:42:35 +0100 Subject: [PATCH 104/116] use general warning icon instead of e2e one for room status --- src/components/structures/RoomStatusBar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index 21dd06767c..b0aa4cb59b 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -289,7 +289,7 @@ module.exports = createReactClass({ } return
- +
{ title } @@ -306,7 +306,7 @@ module.exports = createReactClass({ if (this._shouldShowConnectionError()) { return (
- /!\ + /!\
{ _t('Connectivity to the server has been lost.') } From 80ee68a42f468e5754cad43797e37a0a8e668811 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 19 Nov 2019 22:36:55 +0000 Subject: [PATCH 105/116] Use a settings watcher to set the theme Rather than listening for account data updates manually --- src/components/structures/MatrixChat.js | 20 +++++++++---------- .../tabs/user/GeneralUserSettingsTab.js | 3 +++ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 620e73bf93..c6efb56a9d 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -274,6 +274,7 @@ export default createReactClass({ componentDidMount: function() { this.dispatcherRef = dis.register(this.onAction); + this._themeWatchRef = SettingsStore.watchSetting("theme", null, this._onThemeChanged); this.focusComposer = false; @@ -360,6 +361,7 @@ export default createReactClass({ componentWillUnmount: function() { Lifecycle.stopMatrixClient(); dis.unregister(this.dispatcherRef); + SettingsStore.unwatchSetting(this._themeWatchRef); window.removeEventListener("focus", this.onFocus); window.removeEventListener('resize', this.handleResize); this.state.resizeNotifier.removeListener("middlePanelResized", this._dispatchTimelineResize); @@ -382,6 +384,13 @@ export default createReactClass({ } }, + _onThemeChanged: function(settingName, roomId, atLevel, newValue) { + dis.dispatch({ + action: 'set_theme', + value: newValue, + }); + }, + startPageChangeTimer() { // Tor doesn't support performance if (!performance || !performance.mark) return null; @@ -1376,17 +1385,6 @@ export default createReactClass({ }, null, true); }); - cli.on("accountData", function(ev) { - if (ev.getType() === 'im.vector.web.settings') { - if (ev.getContent() && ev.getContent().theme) { - dis.dispatch({ - action: 'set_theme', - value: ev.getContent().theme, - }); - } - } - }); - const dft = new DecryptionFailureTracker((total, errorCode) => { Analytics.trackEvent('E2E', 'Decryption failure', errorCode, total); }, (errorCode) => { diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 78961ad663..42324f1379 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -175,6 +175,9 @@ export default class GeneralUserSettingsTab extends React.Component { SettingsStore.setValue("theme", null, SettingLevel.ACCOUNT, newTheme); this.setState({theme: newTheme}); + // The settings watcher doesn't fire until the echo comes back from the + // server, so to make the theme change immediately we need to manually + // do the dispatch now dis.dispatch({action: 'set_theme', value: newTheme}); }; From a31d222570f7159d922ca0a94161574e92578678 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 19 Nov 2019 23:00:54 +0000 Subject: [PATCH 106/116] Add catch handler for theme setting --- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 42324f1379..d400e7a839 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -173,7 +173,13 @@ export default class GeneralUserSettingsTab extends React.Component { const newTheme = e.target.value; if (this.state.theme === newTheme) return; - SettingsStore.setValue("theme", null, SettingLevel.ACCOUNT, newTheme); + // doing getValue in the .catch will still return the value we failed to set, + // so remember what the value was before we tried to set it so we can revert + const oldTheme = SettingsStore.getValue('theme'); + SettingsStore.setValue("theme", null, SettingLevel.ACCOUNT, newTheme).catch(() => { + dis.dispatch({action: 'set_theme', value: oldTheme}); + this.setState({theme: oldTheme}); + }); this.setState({theme: newTheme}); // The settings watcher doesn't fire until the echo comes back from the // server, so to make the theme change immediately we need to manually From ab8a9dd0e9d70ed2e340cb2594fb13ab62377161 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 20 Nov 2019 03:58:41 +0000 Subject: [PATCH 107/116] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (1917 of 1917 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 5c6e69c864..1dfdc34f1a 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2330,5 +2330,19 @@ "%(role)s in %(roomName)s": "%(role)s 在 %(roomName)s", "Messages in this room are end-to-end encrypted.": "在此聊天室中的訊息為端到端加密。", "Security": "安全", - "Verify": "驗證" + "Verify": "驗證", + "Send verification requests in direct message, including a new verification UX in the member panel.": "在直接訊息中傳送驗證請求,包含成員面板中新的驗證使用者體驗。", + "Enable cross-signing to verify per-user instead of per-device": "啟用交叉簽章以驗證每個使用者而非每個裝置", + "Any of the following data may be shared:": "可能會分享以下資料:", + "Your display name": "您的顯示名稱", + "Your avatar URL": "您的大頭貼 URL", + "Your user ID": "您的使用 ID", + "Your theme": "您的佈景主題", + "Riot URL": "Riot URL", + "Room ID": "聊天室 ID", + "Widget ID": "小工具 ID", + "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "使用這個小工具可能會與 %(widgetDomain)s 以及您的整合管理員分享資料 。", + "Using this widget may share data with %(widgetDomain)s.": "使用這個小工具可能會與 %(widgetDomain)s 分享資料 。", + "Widget added by": "小工具新增由", + "This widget may use cookies.": "這個小工具可能會使用 cookies。" } From df868a6b0971be5b62c28563e9cb40308f3623ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Wed, 20 Nov 2019 08:43:16 +0000 Subject: [PATCH 108/116] Translated using Weblate (French) Currently translated at 100.0% (1917 of 1917 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 824da9d3ff..64272bb839 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2350,5 +2350,6 @@ "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "L’utilisation de ce widget pourrait partager des données avec %(widgetDomain)s et votre gestionnaire d’intégrations.", "Using this widget may share data with %(widgetDomain)s.": "L’utilisation de ce widget pourrait partager des données avec %(widgetDomain)s.", "Widget added by": "Widget ajouté par", - "This widget may use cookies.": "Ce widget pourrait utiliser des cookies." + "This widget may use cookies.": "Ce widget pourrait utiliser des cookies.", + "Send verification requests in direct message, including a new verification UX in the member panel.": "Envoyer les demandes de vérification en message direct, en incluant une nouvelle expérience de vérification dans le tableau des membres." } From 8df0aee12b15b963c0398049d54bf179bdb062c7 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Tue, 19 Nov 2019 18:58:32 +0000 Subject: [PATCH 109/116] Translated using Weblate (Hungarian) Currently translated at 100.0% (1917 of 1917 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 003af8240c..892f21dbb1 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2337,5 +2337,6 @@ "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Ennek a kisalkalmazásnak a használata adatot oszthat meg %(widgetDomain)s domain-nel és az Integrációs Menedzserrel.", "Using this widget may share data with %(widgetDomain)s.": "Ennek a kisalkalmazásnak a használata adatot oszthat meg %(widgetDomain)s domain-nel.", "Widget added by": "A kisalkalmazást hozzáadta", - "This widget may use cookies.": "Ez a kisalkalmazás sütiket használhat." + "This widget may use cookies.": "Ez a kisalkalmazás sütiket használhat.", + "Send verification requests in direct message, including a new verification UX in the member panel.": "Ellenőrzés küldése közvetlen üzenetben, beleértve az új ellenőrzési felhasználói élményt a résztvevői panelen." } From f25236c3fb902b109ac1c480058843d17e2536f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=ED=83=9C=EC=84=AD?= Date: Wed, 20 Nov 2019 07:16:06 +0000 Subject: [PATCH 110/116] Translated using Weblate (Korean) Currently translated at 100.0% (1917 of 1917 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/ko/ --- src/i18n/strings/ko.json | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/ko.json b/src/i18n/strings/ko.json index 8d34fab025..757edbfa4b 100644 --- a/src/i18n/strings/ko.json +++ b/src/i18n/strings/ko.json @@ -2181,5 +2181,19 @@ "Messages in this room are end-to-end encrypted.": "이 방의 메시지는 종단간 암호화되었습니다.", "Security": "보안", "Verify": "확인", - "You have ignored this user, so their message is hidden. Show anyways.": "이 사용자를 무시했습니다. 사용자의 메시지는 숨겨집니다. 무시하고 보이기." + "You have ignored this user, so their message is hidden. Show anyways.": "이 사용자를 무시했습니다. 사용자의 메시지는 숨겨집니다. 무시하고 보이기.", + "Send verification requests in direct message, including a new verification UX in the member panel.": "다이렉트 메시지에서 구성원 패널에 새 확인 UX가 적용된 확인 요청을 보냅니다.", + "Enable cross-signing to verify per-user instead of per-device": "기기 당 확인이 아닌 사람 당 확인을 위한 교차 서명 켜기", + "Any of the following data may be shared:": "다음 데이터가 공유됩니다:", + "Your display name": "당신의 표시 이름", + "Your avatar URL": "당신의 아바타 URL", + "Your user ID": "당신의 사용자 ID", + "Your theme": "당신의 테마", + "Riot URL": "Riot URL", + "Room ID": "방 ID", + "Widget ID": "위젯 ID", + "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "이 위젯을 사용하면 %(widgetDomain)s & 통합 관리자와 데이터를 공유합니다.", + "Using this widget may share data with %(widgetDomain)s.": "이 위젯을 사용하면 %(widgetDomain)s와(과) 데이터를 공유합니다.", + "Widget added by": "위젯을 추가했습니다", + "This widget may use cookies.": "이 위젯은 쿠키를 사용합니다." } From 870def5d858b2a7431cdf55b0fd4099a645bfdc9 Mon Sep 17 00:00:00 2001 From: fenuks Date: Tue, 19 Nov 2019 19:22:27 +0000 Subject: [PATCH 111/116] Translated using Weblate (Polish) Currently translated at 76.0% (1456 of 1917 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/pl/ --- src/i18n/strings/pl.json | 48 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/pl.json b/src/i18n/strings/pl.json index 4054c48f97..f9c056b02b 100644 --- a/src/i18n/strings/pl.json +++ b/src/i18n/strings/pl.json @@ -1492,7 +1492,7 @@ "You can register, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "Możesz się zarejestrować, lecz niektóre funkcje nie będą dostępne dopóki Serwer Tożsamości nie będzie znów online. Jeśli ciągle widzisz to ostrzeżenie, sprawdź swoją konfigurację lub skontaktuj się z administratorem serwera.", "You can reset your password, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "Możesz zresetować hasło, lecz niektóre funkcje nie będą dostępne dopóki Serwer Tożsamości nie będzie znów online. Jeśli ciągle widzisz to ostrzeżenie, sprawdź swoją konfigurację lub skontaktuj się z administratorem serwera.", "You can log in, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "Możesz się zalogować, lecz niektóre funkcje nie będą dostępne dopóki Serwer Tożsamości nie będzie znów online. Jeśli ciągle widzisz to ostrzeżenie, sprawdź swoją konfigurację lub skontaktuj się z administratorem serwera.", - "No homeserver URL provided": "Nie podano URL serwera głównego.", + "No homeserver URL provided": "Nie podano URL serwera głównego", "The server does not support the room version specified.": "Serwer nie wspiera tej wersji pokoju.", "Name or Matrix ID": "Imię lub identyfikator Matrix", "Email, name or Matrix ID": "E-mail, imię lub Matrix ID", @@ -1528,7 +1528,7 @@ "%(senderDisplayName)s disabled flair for %(groups)s in this room.": "%(senderDisplayName)s dezaktywował Flair dla %(groups)s w tym pokoju.", "%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for %(oldGroups)s in this room.": "%(senderDisplayName)s aktywował Flair dla %(newGroups)s i dezaktywował Flair dla %(oldGroups)s w tym pokoju.", "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s odwołał zaproszednie dla %(targetDisplayName)s aby dołączył do pokoju.", - "%(names)s and %(count)s others are typing …|one": "%(names)s i jedna osoba pisze.", + "%(names)s and %(count)s others are typing …|one": "%(names)s i jedna osoba pisze…", "Cannot reach homeserver": "Błąd połączenia z serwerem domowym", "Ensure you have a stable internet connection, or get in touch with the server admin": "Upewnij się, że posiadasz stabilne połączenie internetowe lub skontaktuj się z administratorem serwera", "Your Riot is misconfigured": "Twój Riot jest źle skonfigurowany", @@ -1689,5 +1689,47 @@ "Autocomplete delay (ms)": "Opóźnienie autouzupełniania (ms)", "Explore": "Przeglądaj", "Filter": "Filtruj", - "Add room": "Dodaj pokój" + "Add room": "Dodaj pokój", + "A device's public name is visible to people you communicate with": "Publiczna nazwa urządzenia jest widoczna dla ludzi, z którymi się komunikujesz", + "Request media permissions": "Zapytaj o uprawnienia", + "Voice & Video": "Głos & Wideo", + "this room": "ten pokój", + "View older messages in %(roomName)s.": "Wyświetl starsze wiadomości w %(roomName)s.", + "Room information": "Informacje o pokoju", + "Internal room ID:": "Wewnętrzne ID pokoju:", + "Uploaded sound": "Przesłano dźwięk", + "Change history visibility": "Zmień widoczność historii", + "Upgrade the room": "Zaktualizuj pokój", + "Enable room encryption": "Włącz szyfrowanie pokoju", + "Select the roles required to change various parts of the room": "Wybierz role wymagane do zmieniania różnych części pokoju", + "Enable encryption?": "Włączyć szyfrowanie?", + "Your email address hasn't been verified yet": "Twój adres e-mail nie został jeszcze zweryfikowany", + "Verification code": "Kod weryfikacyjny", + "Remove %(email)s?": "Usunąć %(email)s?", + "Remove %(phone)s?": "Usunąć %(phone)s?", + "Some devices in this encrypted room are not trusted": "Niektóre urządzenia w tym zaszyfrowanym pokoju nie są zaufane", + "Loading …": "Ładowanie…", + "Loading room preview": "Wczytywanie podglądu pokoju", + "Try to join anyway": "Spróbuj dołączyć mimo tego", + "You can still join it because this is a public room.": "Możesz mimo to dołączyć, gdyż pokój jest publiczny.", + "This invite to %(roomName)s was sent to %(email)s which is not associated with your account": "To zaproszenie do %(roomName)s zostało wysłane na adres %(email)s, który nie jest przypisany do Twojego konta", + "Link this email with your account in Settings to receive invites directly in Riot.": "Połącz ten adres e-mail z Twoim kontem w Ustawieniach, aby otrzymywać zaproszenia bezpośrednio w Riot.", + "This invite to %(roomName)s was sent to %(email)s": "To zaproszenie do %(roomName)s zostało wysłane do %(email)s", + "Use an identity server in Settings to receive invites directly in Riot.": "Użyj serwera tożsamości w Ustawieniach, aby otrzymywać zaproszenia bezpośrednio w Riot.", + "Do you want to chat with %(user)s?": "Czy chcesz rozmawiać z %(user)s?", + "Do you want to join %(roomName)s?": "Czy chcesz dołączyć do %(roomName)s?", + " invited you": " zaprosił(a) CIę", + "You're previewing %(roomName)s. Want to join it?": "Przeglądasz %(roomName)s. Czy chcesz dołączyć do pokoju?", + "Not now": "Nie teraz", + "Don't ask me again": "Nie pytaj ponownie", + "%(count)s unread messages including mentions.|other": "%(count)s nieprzeczytanych wiadomości, wliczając wzmianki.", + "%(count)s unread messages including mentions.|one": "1 nieprzeczytana wzmianka.", + "%(count)s unread messages.|other": "%(count)s nieprzeczytanych wiadomości.", + "%(count)s unread messages.|one": "1 nieprzeczytana wiadomość.", + "Unread mentions.": "Nieprzeczytane wzmianki.", + "Unread messages.": "Nieprzeczytane wiadomości.", + "Join": "Dołącz", + "%(creator)s created and configured the room.": "%(creator)s stworzył(a) i skonfigurował(a) pokój.", + "Preview": "Przejrzyj", + "View": "Wyświetl" } From d277a1946ba6270e30a61b3ec3566e898df0c256 Mon Sep 17 00:00:00 2001 From: Karol Kosek Date: Tue, 19 Nov 2019 19:43:44 +0000 Subject: [PATCH 112/116] Translated using Weblate (Polish) Currently translated at 76.0% (1456 of 1917 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/pl/ --- src/i18n/strings/pl.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/pl.json b/src/i18n/strings/pl.json index f9c056b02b..a0ce517404 100644 --- a/src/i18n/strings/pl.json +++ b/src/i18n/strings/pl.json @@ -1731,5 +1731,6 @@ "Join": "Dołącz", "%(creator)s created and configured the room.": "%(creator)s stworzył(a) i skonfigurował(a) pokój.", "Preview": "Przejrzyj", - "View": "Wyświetl" + "View": "Wyświetl", + "Missing media permissions, click the button below to request.": "Brakuje uprawnień do mediów, kliknij przycisk poniżej, aby o nie zapytać." } From 8f796617257758f3b91752e8eef5f167de5c6578 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 20 Nov 2019 10:30:34 +0000 Subject: [PATCH 113/116] Update code style for our 90 char life We've been using 90 chars for JS code for quite a while now, but for some reason, the code style guide hasn't admitted that, so this adjusts it to match ESLint settings. --- code_style.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_style.md b/code_style.md index e7844b939c..4b2338064c 100644 --- a/code_style.md +++ b/code_style.md @@ -22,7 +22,7 @@ number throgh from the original code to the final application. General Style ------------- - 4 spaces to indent, for consistency with Matrix Python. -- 120 columns per line, but try to keep JavaScript code around the 80 column mark. +- 120 columns per line, but try to keep JavaScript code around the 90 column mark. Inline JSX in particular can be nicer with more columns per line. - No trailing whitespace at end of lines. - Don't indent empty lines. From 2f5b0a9652629cb75bdf39926ff2f045511286ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 20 Nov 2019 12:30:03 +0100 Subject: [PATCH 114/116] EventIndex: Use property initializer style for the bound callbacks. --- src/indexing/EventIndex.js | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index df81667c6e..e6a1d4007b 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -31,19 +31,6 @@ export default class EventIndex { this._eventsPerCrawl = 100; this._crawler = null; this.liveEventsForIndex = new Set(); - - this.boundOnSync = async (state, prevState, data) => { - await this.onSync(state, prevState, data); - }; - this.boundOnRoomTimeline = async ( ev, room, toStartOfTimeline, removed, - data) => { - await this.onRoomTimeline(ev, room, toStartOfTimeline, removed, data); - }; - this.boundOnEventDecrypted = async (ev, err) => { - await this.onEventDecrypted(ev, err); - }; - this.boundOnTimelineReset = async (room, timelineSet, - resetAllTimelines) => await this.onTimelineReset(room); } async init() { @@ -56,23 +43,23 @@ export default class EventIndex { registerListeners() { const client = MatrixClientPeg.get(); - client.on('sync', this.boundOnSync); - client.on('Room.timeline', this.boundOnRoomTimeline); - client.on('Event.decrypted', this.boundOnEventDecrypted); - client.on('Room.timelineReset', this.boundOnTimelineReset); + client.on('sync', this.onSync); + client.on('Room.timeline', this.onRoomTimeline); + client.on('Event.decrypted', this.onEventDecrypted); + client.on('Room.timelineReset', this.onTimelineReset); } removeListeners() { const client = MatrixClientPeg.get(); if (client === null) return; - client.removeListener('sync', this.boundOnSync); - client.removeListener('Room.timeline', this.boundOnRoomTimeline); - client.removeListener('Event.decrypted', this.boundOnEventDecrypted); - client.removeListener('Room.timelineReset', this.boundOnTimelineReset); + client.removeListener('sync', this.onSync); + client.removeListener('Room.timeline', this.onRoomTimeline); + client.removeListener('Event.decrypted', this.onEventDecrypted); + client.removeListener('Room.timelineReset', this.onTimelineReset); } - async onSync(state, prevState, data) { + onSync = async (state, prevState, data) => { const indexManager = PlatformPeg.get().getEventIndexingManager(); if (prevState === null && state === "PREPARED") { @@ -146,7 +133,7 @@ export default class EventIndex { } } - async onRoomTimeline(ev, room, toStartOfTimeline, removed, data) { + onRoomTimeline = async (ev, room, toStartOfTimeline, removed, data) => { // We only index encrypted rooms locally. if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; @@ -169,7 +156,7 @@ export default class EventIndex { } } - async onEventDecrypted(ev, err) { + onEventDecrypted = async (ev, err) => { const eventId = ev.getId(); // If the event isn't in our live event set, ignore it. @@ -377,7 +364,7 @@ export default class EventIndex { console.log("EventIndex: Stopping crawler function"); } - async onTimelineReset(room) { + onTimelineReset = async (room, timelineSet, resetAllTimelines) => { if (room === null) return; const indexManager = PlatformPeg.get().getEventIndexingManager(); From 0631faf902c5870b263d8f2745745c6ae2281a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 20 Nov 2019 12:31:07 +0100 Subject: [PATCH 115/116] Settings: Fix the supportedLevels for event indexing feature. --- src/settings/Settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 8abd845f0c..2cf9509aca 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -122,7 +122,7 @@ export const SETTINGS = { }, "feature_event_indexing": { isFeature: true, - supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + supportedLevels: LEVELS_FEATURE, displayName: _td("Enable local event indexing and E2EE search (requires restart)"), default: false, }, From 4bd46f9d694f03aeaad667e35ab64083f7d4479f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 20 Nov 2019 12:47:20 +0100 Subject: [PATCH 116/116] EventIndex: Silence the linter complaining about missing docs. --- src/indexing/EventIndex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index e6a1d4007b..6bad992017 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -17,7 +17,7 @@ limitations under the License. import PlatformPeg from "../PlatformPeg"; import MatrixClientPeg from "../MatrixClientPeg"; -/** +/* * Event indexing class that wraps the platform specific event indexing. */ export default class EventIndex {