Merge remote-tracking branch 'origin/develop' into develop
						commit
						d305f8aacb
					
				|  | @ -6,7 +6,6 @@ src/autocomplete/Autocompleter.js | |||
| src/autocomplete/Components.js | ||||
| src/autocomplete/DuckDuckGoProvider.js | ||||
| src/autocomplete/EmojiProvider.js | ||||
| src/autocomplete/RoomProvider.js | ||||
| src/autocomplete/UserProvider.js | ||||
| src/CallHandler.js | ||||
| src/component-index.js | ||||
|  | @ -35,7 +34,6 @@ src/components/views/create_room/RoomAlias.js | |||
| src/components/views/dialogs/ChatCreateOrReuseDialog.js | ||||
| src/components/views/dialogs/DeactivateAccountDialog.js | ||||
| src/components/views/dialogs/InteractiveAuthDialog.js | ||||
| src/components/views/dialogs/SetMxIdDialog.js | ||||
| src/components/views/dialogs/UnknownDeviceDialog.js | ||||
| src/components/views/elements/AccessibleButton.js | ||||
| src/components/views/elements/ActionButton.js | ||||
|  | @ -89,7 +87,6 @@ src/components/views/rooms/MemberList.js | |||
| src/components/views/rooms/MemberTile.js | ||||
| src/components/views/rooms/MessageComposer.js | ||||
| src/components/views/rooms/MessageComposerInput.js | ||||
| src/components/views/rooms/MessageComposerInputOld.js | ||||
| src/components/views/rooms/PresenceLabel.js | ||||
| src/components/views/rooms/ReadReceiptMarker.js | ||||
| src/components/views/rooms/RoomList.js | ||||
|  | @ -100,7 +97,6 @@ src/components/views/rooms/RoomTile.js | |||
| src/components/views/rooms/RoomTopicEditor.js | ||||
| src/components/views/rooms/SearchableEntityList.js | ||||
| src/components/views/rooms/SearchResultTile.js | ||||
| src/components/views/rooms/TabCompleteBar.js | ||||
| src/components/views/rooms/TopUnreadMessagesBar.js | ||||
| src/components/views/rooms/UserTile.js | ||||
| src/components/views/settings/AddPhoneNumber.js | ||||
|  | @ -128,8 +124,6 @@ src/Roles.js | |||
| src/Rooms.js | ||||
| src/ScalarAuthClient.js | ||||
| src/ScalarMessaging.js | ||||
| src/TabComplete.js | ||||
| src/TabCompleteEntries.js | ||||
| src/TextForEvent.js | ||||
| src/Tinter.js | ||||
| src/UiEffects.js | ||||
|  | @ -142,7 +136,7 @@ src/utils/Receipt.js | |||
| src/Velociraptor.js | ||||
| src/VelocityBounce.js | ||||
| src/WhoIsTyping.js | ||||
| src/wrappers/WithMatrixClient.js | ||||
| src/wrappers/withMatrixClient.js | ||||
| test/all-tests.js | ||||
| test/components/structures/login/Registration-test.js | ||||
| test/components/structures/MessagePanel-test.js | ||||
|  |  | |||
							
								
								
									
										73
									
								
								CHANGELOG.md
								
								
								
								
							
							
						
						
									
										73
									
								
								CHANGELOG.md
								
								
								
								
							|  | @ -1,3 +1,76 @@ | |||
| Changes in [0.10.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.10.2) (2017-08-24) | ||||
| ===================================================================================================== | ||||
| [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.10.1...v0.10.2) | ||||
| 
 | ||||
|  * Force update on timelinepanel when event decrypted | ||||
|    [\#1334](https://github.com/matrix-org/matrix-react-sdk/pull/1334) | ||||
|  * Dispatch incoming_call synchronously | ||||
|    [\#1337](https://github.com/matrix-org/matrix-react-sdk/pull/1337) | ||||
|  * Fix React crying on machines without internet due to return undefined | ||||
|    [\#1335](https://github.com/matrix-org/matrix-react-sdk/pull/1335) | ||||
|  * Catch the promise rejection if scalar fails | ||||
|    [\#1333](https://github.com/matrix-org/matrix-react-sdk/pull/1333) | ||||
|  * Update from Weblate. | ||||
|    [\#1329](https://github.com/matrix-org/matrix-react-sdk/pull/1329) | ||||
| 
 | ||||
| Changes in [0.10.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.10.1) (2017-08-23) | ||||
| ===================================================================================================== | ||||
| [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.10.1-rc.1...v0.10.1) | ||||
| 
 | ||||
|  * [No changes] | ||||
| 
 | ||||
| Changes in [0.10.1-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.10.1-rc.1) (2017-08-22) | ||||
| =============================================================================================================== | ||||
| [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.10.0-rc.2...v0.10.1-rc.1) | ||||
| 
 | ||||
|  * Matthew/multiple widgets | ||||
|    [\#1327](https://github.com/matrix-org/matrix-react-sdk/pull/1327) | ||||
|  * Fix proptypes on UserPickerDialog | ||||
|    [\#1326](https://github.com/matrix-org/matrix-react-sdk/pull/1326) | ||||
|  * AppsDrawer: Remove unnecessary bind | ||||
|    [\#1325](https://github.com/matrix-org/matrix-react-sdk/pull/1325) | ||||
|  * Position add app widget link | ||||
|    [\#1322](https://github.com/matrix-org/matrix-react-sdk/pull/1322) | ||||
|  * Remove app tile beta tag. | ||||
|    [\#1323](https://github.com/matrix-org/matrix-react-sdk/pull/1323) | ||||
|  * Add missing translation. | ||||
|    [\#1324](https://github.com/matrix-org/matrix-react-sdk/pull/1324) | ||||
|  * Note that apps are not E2EE | ||||
|    [\#1319](https://github.com/matrix-org/matrix-react-sdk/pull/1319) | ||||
|  * Only render appTile body (including warnings) if drawer shown. | ||||
|    [\#1321](https://github.com/matrix-org/matrix-react-sdk/pull/1321) | ||||
|  * Timeline improvements | ||||
|    [\#1320](https://github.com/matrix-org/matrix-react-sdk/pull/1320) | ||||
|  * Add a space between widget name and "widget" in widget event tiles | ||||
|    [\#1318](https://github.com/matrix-org/matrix-react-sdk/pull/1318) | ||||
|  * Move manage integrations button from settings page to room header as a | ||||
|    stand-alone component | ||||
|    [\#1286](https://github.com/matrix-org/matrix-react-sdk/pull/1286) | ||||
|  * Don't apply case logic to app names | ||||
|    [\#1316](https://github.com/matrix-org/matrix-react-sdk/pull/1316) | ||||
|  * Stop integ manager opening on every room switch | ||||
|    [\#1315](https://github.com/matrix-org/matrix-react-sdk/pull/1315) | ||||
|  * Add behaviour to toggle app draw on app tile header click | ||||
|    [\#1313](https://github.com/matrix-org/matrix-react-sdk/pull/1313) | ||||
|  * Change OOO so that MELS generation will continue over hidden events | ||||
|    [\#1308](https://github.com/matrix-org/matrix-react-sdk/pull/1308) | ||||
|  * Implement TextualEvent tiles for im.vector.modular.widgets | ||||
|    [\#1312](https://github.com/matrix-org/matrix-react-sdk/pull/1312) | ||||
|  * Don't show widget security warning to the person that added it to the room | ||||
|    [\#1314](https://github.com/matrix-org/matrix-react-sdk/pull/1314) | ||||
|  * remove unused strings introduced by string change | ||||
|    [\#1311](https://github.com/matrix-org/matrix-react-sdk/pull/1311) | ||||
|  * hotfix bad fn signature regression | ||||
|    [\#1310](https://github.com/matrix-org/matrix-react-sdk/pull/1310) | ||||
|  * Show a dialog if the maximum number of widgets allowed has been reached. | ||||
|    [\#1291](https://github.com/matrix-org/matrix-react-sdk/pull/1291) | ||||
|  * Fix Robot translation | ||||
|    [\#1309](https://github.com/matrix-org/matrix-react-sdk/pull/1309) | ||||
|  * Refactor ChatInviteDialog to be UserPickerDialog | ||||
|    [\#1300](https://github.com/matrix-org/matrix-react-sdk/pull/1300) | ||||
|  * Update Link to Translation status | ||||
|    [\#1302](https://github.com/matrix-org/matrix-react-sdk/pull/1302) | ||||
| 
 | ||||
| Changes in [0.9.7](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.9.7) (2017-06-22) | ||||
| =================================================================================================== | ||||
| [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.9.6...v0.9.7) | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| { | ||||
|   "name": "matrix-react-sdk", | ||||
|   "version": "0.9.7", | ||||
|   "version": "0.10.2", | ||||
|   "description": "SDK for matrix.org using React", | ||||
|   "author": "matrix.org", | ||||
|   "repository": { | ||||
|  | @ -66,7 +66,7 @@ | |||
|     "isomorphic-fetch": "^2.2.1", | ||||
|     "linkifyjs": "^2.1.3", | ||||
|     "lodash": "^4.13.1", | ||||
|     "matrix-js-sdk": "matrix-org/matrix-js-sdk#develop", | ||||
|     "matrix-js-sdk": "0.8.2", | ||||
|     "optimist": "^0.6.1", | ||||
|     "prop-types": "^15.5.8", | ||||
|     "react": "^15.4.0", | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| /* | ||||
| Copyright 2015, 2016 OpenMarket Ltd | ||||
| Copyright 2017 Vector Creations Ltd | ||||
| Copyright 2017 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. | ||||
|  | @ -33,9 +34,16 @@ import Modal from './Modal'; | |||
|  * } | ||||
|  */ | ||||
| 
 | ||||
| const MAX_PENDING_ENCRYPTED = 20; | ||||
| 
 | ||||
| const Notifier = { | ||||
|     notifsByRoom: {}, | ||||
| 
 | ||||
|     // A list of event IDs that we've received but need to wait until
 | ||||
|     // they're decrypted until we decide whether to notify for them
 | ||||
|     // or not
 | ||||
|     pendingEncryptedEventIds: [], | ||||
| 
 | ||||
|     notificationMessageForEvent: function(ev) { | ||||
|         return TextForEvent.textForEvent(ev); | ||||
|     }, | ||||
|  | @ -89,17 +97,18 @@ const Notifier = { | |||
|     _playAudioNotification: function(ev, room) { | ||||
|         const e = document.getElementById("messageAudio"); | ||||
|         if (e) { | ||||
|             e.load(); | ||||
|             e.play(); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     start: function() { | ||||
|         this.boundOnRoomTimeline = this.onRoomTimeline.bind(this); | ||||
|         this.boundOnEvent = this.onEvent.bind(this); | ||||
|         this.boundOnSyncStateChange = this.onSyncStateChange.bind(this); | ||||
|         this.boundOnRoomReceipt = this.onRoomReceipt.bind(this); | ||||
|         MatrixClientPeg.get().on('Room.timeline', this.boundOnRoomTimeline); | ||||
|         this.boundOnEventDecrypted = this.onEventDecrypted.bind(this); | ||||
|         MatrixClientPeg.get().on('event', this.boundOnEvent); | ||||
|         MatrixClientPeg.get().on('Room.receipt', this.boundOnRoomReceipt); | ||||
|         MatrixClientPeg.get().on('Event.decrypted', this.boundOnEventDecrypted); | ||||
|         MatrixClientPeg.get().on("sync", this.boundOnSyncStateChange); | ||||
|         this.toolbarHidden = false; | ||||
|         this.isSyncing = false; | ||||
|  | @ -107,8 +116,9 @@ const Notifier = { | |||
| 
 | ||||
|     stop: function() { | ||||
|         if (MatrixClientPeg.get() && this.boundOnRoomTimeline) { | ||||
|             MatrixClientPeg.get().removeListener('Room.timeline', this.boundOnRoomTimeline); | ||||
|             MatrixClientPeg.get().removeListener('Event', this.boundOnEvent); | ||||
|             MatrixClientPeg.get().removeListener('Room.receipt', this.boundOnRoomReceipt); | ||||
|             MatrixClientPeg.get().removeListener('Event.decrypted', this.boundOnEventDecrypted); | ||||
|             MatrixClientPeg.get().removeListener('sync', this.boundOnSyncStateChange); | ||||
|         } | ||||
|         this.isSyncing = false; | ||||
|  | @ -237,23 +247,30 @@ const Notifier = { | |||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onRoomTimeline: function(ev, room, toStartOfTimeline, removed, data) { | ||||
|         if (toStartOfTimeline) return; | ||||
|         if (!room) return; | ||||
|     onEvent: function(ev) { | ||||
|         if (!this.isSyncing) return; // don't alert for any messages initially
 | ||||
|         if (ev.sender && ev.sender.userId === MatrixClientPeg.get().credentials.userId) return; | ||||
|         if (data.timeline.getTimelineSet() !== room.getUnfilteredTimelineSet()) return; | ||||
| 
 | ||||
|         const actions = MatrixClientPeg.get().getPushActionsForEvent(ev); | ||||
|         if (actions && actions.notify) { | ||||
|             if (this.isEnabled()) { | ||||
|                 this._displayPopupNotification(ev, room); | ||||
|             } | ||||
|             if (actions.tweaks.sound && this.isAudioEnabled()) { | ||||
|                 PlatformPeg.get().loudNotification(ev, room); | ||||
|                 this._playAudioNotification(ev, room); | ||||
|         // If it's an encrypted event and the type is still 'm.room.encrypted',
 | ||||
|         // it hasn't yet been decrypted, so wait until it is.
 | ||||
|         if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) { | ||||
|             this.pendingEncryptedEventIds.push(ev.getId()); | ||||
|             // don't let the list fill up indefinitely
 | ||||
|             while (this.pendingEncryptedEventIds.length > MAX_PENDING_ENCRYPTED) { | ||||
|                 this.pendingEncryptedEventIds.shift(); | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this._evaluateEvent(ev); | ||||
|     }, | ||||
| 
 | ||||
|     onEventDecrypted: function(ev) { | ||||
|         const idx = this.pendingEncryptedEventIds.indexOf(ev.getId()); | ||||
|         if (idx === -1) return; | ||||
| 
 | ||||
|         this.pendingEncryptedEventIds.splice(idx, 1); | ||||
|         this._evaluateEvent(ev); | ||||
|     }, | ||||
| 
 | ||||
|     onRoomReceipt: function(ev, room) { | ||||
|  | @ -273,6 +290,20 @@ const Notifier = { | |||
|             delete this.notifsByRoom[room.roomId]; | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     _evaluateEvent: function(ev) { | ||||
|         const room = MatrixClientPeg.get().getRoom(ev.getRoomId()); | ||||
|         const actions = MatrixClientPeg.get().getPushActionsForEvent(ev); | ||||
|         if (actions && actions.notify) { | ||||
|             if (this.isEnabled()) { | ||||
|                 this._displayPopupNotification(ev, room); | ||||
|             } | ||||
|             if (actions.tweaks.sound && this.isAudioEnabled()) { | ||||
|                 PlatformPeg.get().loudNotification(ev, room); | ||||
|                 this._playAudioNotification(ev, room); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| if (!global.mxNotifier) { | ||||
|  |  | |||
|  | @ -244,15 +244,16 @@ function textForPowerEvent(event) { | |||
|     } | ||||
|     return _t('%(senderName)s changed the power level of %(powerLevelDiffText)s.', { | ||||
|         senderName: senderName, | ||||
|         powerLevelDiffText: diff.join(", ") | ||||
|         powerLevelDiffText: diff.join(", "), | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| function textForWidgetEvent(event) { | ||||
|     const senderName = event.sender ? event.sender.name : event.getSender(); | ||||
|     const previousContent = event.getPrevContent() || {}; | ||||
|     const senderName = event.getSender(); | ||||
|     const {name: prevName, type: prevType, url: prevUrl} = event.getPrevContent(); | ||||
|     const {name, type, url} = event.getContent() || {}; | ||||
|     let widgetName = name || previousContent.name || type || previousContent.type || ''; | ||||
| 
 | ||||
|     let widgetName = name || prevName || type || prevType || ''; | ||||
|     // Apply sentence case to widget name
 | ||||
|     if (widgetName && widgetName.length > 0) { | ||||
|         widgetName = widgetName[0].toUpperCase() + widgetName.slice(1) + ' '; | ||||
|  | @ -261,9 +262,15 @@ function textForWidgetEvent(event) { | |||
|     // If the widget was removed, its content should be {}, but this is sufficiently
 | ||||
|     // equivalent to that condition.
 | ||||
|     if (url) { | ||||
|         return _t('%(widgetName)s widget added by %(senderName)s', { | ||||
|             widgetName, senderName, | ||||
|         }); | ||||
|         if (prevUrl) { | ||||
|             return _t('%(widgetName)s widget modified by %(senderName)s', { | ||||
|                 widgetName, senderName, | ||||
|             }); | ||||
|         } else { | ||||
|             return _t('%(widgetName)s widget added by %(senderName)s', { | ||||
|                 widgetName, senderName, | ||||
|             }); | ||||
|         } | ||||
|     } else { | ||||
|         return _t('%(widgetName)s widget removed by %(senderName)s', { | ||||
|             widgetName, senderName, | ||||
|  |  | |||
|  | @ -1068,10 +1068,13 @@ module.exports = React.createClass({ | |||
|             self.setState({ready: true}); | ||||
|         }); | ||||
|         cli.on('Call.incoming', function(call) { | ||||
|             // we dispatch this synchronously to make sure that the event
 | ||||
|             // handlers on the call are set up immediately (so that if
 | ||||
|             // we get an immediate hangup, we don't get a stuck call)
 | ||||
|             dis.dispatch({ | ||||
|                 action: 'incoming_call', | ||||
|                 call: call, | ||||
|             }); | ||||
|             }, true); | ||||
|         }); | ||||
|         cli.on('Session.logged_out', function(call) { | ||||
|             const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); | ||||
|  |  | |||
|  | @ -65,7 +65,7 @@ module.exports = React.createClass({ | |||
|         suppressFirstDateSeparator: React.PropTypes.bool, | ||||
| 
 | ||||
|         // whether to show read receipts
 | ||||
|         manageReadReceipts: React.PropTypes.bool, | ||||
|         showReadReceipts: React.PropTypes.bool, | ||||
| 
 | ||||
|         // true if updates to the event list should cause the scroll panel to
 | ||||
|         // scroll down when we are at the bottom of the window. See ScrollPanel
 | ||||
|  | @ -491,7 +491,7 @@ module.exports = React.createClass({ | |||
|         var scrollToken = mxEv.status ? undefined : eventId; | ||||
| 
 | ||||
|         var readReceipts; | ||||
|         if (this.props.manageReadReceipts) { | ||||
|         if (this.props.showReadReceipts) { | ||||
|             readReceipts = this._getReadReceiptsForEvent(mxEv); | ||||
|         } | ||||
|         ret.push( | ||||
|  |  | |||
|  | @ -20,6 +20,8 @@ limitations under the License. | |||
| //  - Drag and drop
 | ||||
| //  - File uploading - uploadFile()
 | ||||
| 
 | ||||
| import shouldHideEvent from "../../shouldHideEvent"; | ||||
| 
 | ||||
| var React = require("react"); | ||||
| var ReactDOM = require("react-dom"); | ||||
| import Promise from 'bluebird'; | ||||
|  | @ -143,6 +145,8 @@ module.exports = React.createClass({ | |||
|         MatrixClientPeg.get().on("RoomMember.membership", this.onRoomMemberMembership); | ||||
|         MatrixClientPeg.get().on("accountData", this.onAccountData); | ||||
| 
 | ||||
|         this._syncedSettings = UserSettingsStore.getSyncedSettings(); | ||||
| 
 | ||||
|         // Start listening for RoomViewStore updates
 | ||||
|         this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate); | ||||
|         this._onRoomViewStoreUpdate(true); | ||||
|  | @ -497,8 +501,7 @@ module.exports = React.createClass({ | |||
|             // update unread count when scrolled up
 | ||||
|             if (!this.state.searchResults && this.state.atEndOfLiveTimeline) { | ||||
|                 // no change
 | ||||
|             } | ||||
|             else { | ||||
|             } else if (!shouldHideEvent(ev, this._syncedSettings)) { | ||||
|                 this.setState((state, props) => { | ||||
|                     return {numUnreadMessages: state.numUnreadMessages + 1}; | ||||
|                 }); | ||||
|  | @ -1716,7 +1719,8 @@ module.exports = React.createClass({ | |||
|         var messagePanel = ( | ||||
|             <TimelinePanel ref={this._gatherTimelinePanelRef} | ||||
|                 timelineSet={this.state.room.getUnfilteredTimelineSet()} | ||||
|                 manageReadReceipts={!UserSettingsStore.getSyncedSetting('hideReadReceipts', false)} | ||||
|                 showReadReceipts={!UserSettingsStore.getSyncedSetting('hideReadReceipts', false)} | ||||
|                 manageReadReceipts={true} | ||||
|                 manageReadMarkers={true} | ||||
|                 hidden={hideMessagePanel} | ||||
|                 highlightedEventId={highlightedEventId} | ||||
|  |  | |||
|  | @ -59,6 +59,7 @@ var TimelinePanel = React.createClass({ | |||
|         // that room.
 | ||||
|         timelineSet: React.PropTypes.object.isRequired, | ||||
| 
 | ||||
|         showReadReceipts: React.PropTypes.bool, | ||||
|         // Enable managing RRs and RMs. These require the timelineSet to have a room.
 | ||||
|         manageReadReceipts: React.PropTypes.bool, | ||||
|         manageReadMarkers: React.PropTypes.bool, | ||||
|  | @ -197,6 +198,7 @@ var TimelinePanel = React.createClass({ | |||
|         MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt); | ||||
|         MatrixClientPeg.get().on("Room.localEchoUpdated", this.onLocalEchoUpdated); | ||||
|         MatrixClientPeg.get().on("Room.accountData", this.onAccountData); | ||||
|         MatrixClientPeg.get().on("Event.decrypted", this.onEventDecrypted); | ||||
|         MatrixClientPeg.get().on("sync", this.onSync); | ||||
| 
 | ||||
|         this._initTimeline(this.props); | ||||
|  | @ -266,6 +268,7 @@ var TimelinePanel = React.createClass({ | |||
|             client.removeListener("Room.receipt", this.onRoomReceipt); | ||||
|             client.removeListener("Room.localEchoUpdated", this.onLocalEchoUpdated); | ||||
|             client.removeListener("Room.accountData", this.onAccountData); | ||||
|             client.removeListener("Event.decrypted", this.onEventDecrypted); | ||||
|             client.removeListener("sync", this.onSync); | ||||
|         } | ||||
|     }, | ||||
|  | @ -341,9 +344,16 @@ var TimelinePanel = React.createClass({ | |||
|                 newState[canPaginateOtherWayKey] = true; | ||||
|             } | ||||
| 
 | ||||
|             this.setState(newState); | ||||
| 
 | ||||
|             return r; | ||||
|             // Don't resolve until the setState has completed: we need to let
 | ||||
|             // the component update before we consider the pagination completed,
 | ||||
|             // otherwise we'll end up paginating in all the history the js-sdk
 | ||||
|             // has in memory because we never gave the component a chance to scroll
 | ||||
|             // itself into the right place
 | ||||
|             return new Promise((resolve) => { | ||||
|                 this.setState(newState, () => { | ||||
|                     resolve(r); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|  | @ -503,6 +513,18 @@ var TimelinePanel = React.createClass({ | |||
|         }, this.props.onReadMarkerUpdated); | ||||
|     }, | ||||
| 
 | ||||
|     onEventDecrypted: function(ev) { | ||||
|         // Need to update as we don't display event tiles for events that
 | ||||
|         // haven't yet been decrypted. The event will have just been updated
 | ||||
|         // in place so we just need to re-render.
 | ||||
|         // TODO: We should restrict this to only events in our timeline,
 | ||||
|         // but possibly the event tile itself should just update when this
 | ||||
|         // happens to save us re-rendering the whole timeline.
 | ||||
|         if (ev.getRoomId() === this.props.timelineSet.room.roomId) { | ||||
|             this.forceUpdate(); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onSync: function(state, prevState, data) { | ||||
|         this.setState({clientSyncState: state}); | ||||
|     }, | ||||
|  | @ -1126,8 +1148,8 @@ var TimelinePanel = React.createClass({ | |||
|                           readMarkerEventId={ this.state.readMarkerEventId } | ||||
|                           readMarkerVisible={ this.state.readMarkerVisible } | ||||
|                           suppressFirstDateSeparator={ this.state.canBackPaginate } | ||||
|                           showUrlPreview = { this.props.showUrlPreview } | ||||
|                           manageReadReceipts = { this.props.manageReadReceipts } | ||||
|                           showUrlPreview={ this.props.showUrlPreview } | ||||
|                           showReadReceipts={ this.props.showReadReceipts } | ||||
|                           ourUserId={ MatrixClientPeg.get().credentials.userId } | ||||
|                           stickyBottom={ stickyBottom } | ||||
|                           onScroll={ this.onMessageListScroll } | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| /* | ||||
| Copyright 2015, 2016 OpenMarket Ltd | ||||
| Copyright 2017 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. | ||||
|  | @ -136,16 +137,15 @@ module.exports = React.createClass({ | |||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onHsUrlChanged: function(newHsUrl) { | ||||
|         this.setState({ | ||||
|             enteredHomeserverUrl: newHsUrl | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onIsUrlChanged: function(newIsUrl) { | ||||
|         this.setState({ | ||||
|             enteredIdentityServerUrl: newIsUrl | ||||
|         }); | ||||
|     onServerConfigChange: function(config) { | ||||
|         const newState = {}; | ||||
|         if (config.hsUrl !== undefined) { | ||||
|             newState.enteredHomeserverUrl = config.hsUrl; | ||||
|         } | ||||
|         if (config.isUrl !== undefined) { | ||||
|             newState.enteredIdentityServerUrl = config.isUrl; | ||||
|         } | ||||
|         this.setState(newState); | ||||
|     }, | ||||
| 
 | ||||
|     showErrorDialog: function(body, title) { | ||||
|  | @ -170,7 +170,7 @@ module.exports = React.createClass({ | |||
|         else if (this.state.progress === "sent_email") { | ||||
|             resetPasswordJsx = ( | ||||
|                 <div> | ||||
|                     { _t('An email has been sent to') } {this.state.email}. { _t('Once you've followed the link it contains, click below') }. | ||||
|                     { _t('An email has been sent to') } {this.state.email}. { _t("Once you've followed the link it contains, click below") }. | ||||
|                     <br /> | ||||
|                     <input className="mx_Login_submit" type="button" onClick={this.onVerify} | ||||
|                         value={ _t('I have verified my email address') } /> | ||||
|  | @ -221,8 +221,7 @@ module.exports = React.createClass({ | |||
|                         defaultIsUrl={this.props.defaultIsUrl} | ||||
|                         customHsUrl={this.props.customHsUrl} | ||||
|                         customIsUrl={this.props.customIsUrl} | ||||
|                         onHsUrlChanged={this.onHsUrlChanged} | ||||
|                         onIsUrlChanged={this.onIsUrlChanged} | ||||
|                         onServerConfigChange={this.onServerConfigChange} | ||||
|                         delayTimeMs={0}/> | ||||
|                     <div className="mx_Login_error"> | ||||
|                     </div> | ||||
|  |  | |||
|  | @ -72,8 +72,17 @@ export default React.createClass({ | |||
| 
 | ||||
|     // Returns true if props.url is a scalar URL, typically https://scalar.vector.im/api
 | ||||
|     isScalarUrl: function() { | ||||
|         const scalarUrl = SdkConfig.get().integrations_rest_url; | ||||
|         return scalarUrl && this.props.url.startsWith(scalarUrl); | ||||
|         let scalarUrls = SdkConfig.get().integrations_widgets_urls; | ||||
|         if (!scalarUrls || scalarUrls.length == 0) { | ||||
|             scalarUrls = [SdkConfig.get().integrations_rest_url]; | ||||
|         } | ||||
| 
 | ||||
|         for (let i = 0; i < scalarUrls.length; i++) { | ||||
|             if (this.props.url.startsWith(scalarUrls[i])) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     }, | ||||
| 
 | ||||
|     isMixedContent: function() { | ||||
|  |  | |||
|  | @ -75,7 +75,7 @@ export default class ManageIntegsButton extends React.Component { | |||
|     } | ||||
| 
 | ||||
|     render() { | ||||
|         let integrationsButton; | ||||
|         let integrationsButton = <div />; | ||||
|         let integrationsError; | ||||
|         if (this.scalarClient !== null) { | ||||
|             if (this.state.showIntegrationsError && this.state.scalarError) { | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ limitations under the License. | |||
| 'use strict'; | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import ReactDOM from 'react-dom'; | ||||
| import { _t, _tJsx } from '../../../languageHandler'; | ||||
| 
 | ||||
| var DIV_ID = 'mx_recaptcha'; | ||||
|  | @ -66,11 +67,10 @@ module.exports = React.createClass({ | |||
|                 // * jumping straight to a hosted captcha page (but we don't support that yet)
 | ||||
|                 // * embedding the captcha in an iframe (if that works)
 | ||||
|                 // * using a better captcha lib
 | ||||
|                 warning.innerHTML = _tJsx( | ||||
|                      "Robot check is currently unavailable on desktop - please use a <a>web browser</a>", | ||||
|                      /<a>(.*?)<\/a>/, | ||||
|                      (sub) => { return "<a href='https://riot.im/app'>{ sub }</a>"; } | ||||
|                  ); | ||||
|                 ReactDOM.render(_tJsx( | ||||
|                     "Robot check is currently unavailable on desktop - please use a <a>web browser</a>", | ||||
|                     /<a>(.*?)<\/a>/, | ||||
|                     (sub) => { return <a href='https://riot.im/app'>{ sub }</a>; }), warning); | ||||
|                 this.refs.recaptchaContainer.appendChild(warning); | ||||
|             } | ||||
|             else { | ||||
|  |  | |||
|  | @ -53,14 +53,14 @@ module.exports = React.createClass({ | |||
|         this.scalarClient = null; | ||||
|         if (SdkConfig.get().integrations_ui_url && SdkConfig.get().integrations_rest_url) { | ||||
|             this.scalarClient = new ScalarAuthClient(); | ||||
|             this.scalarClient.connect().done(() => { | ||||
|             this.scalarClient.connect().then(() => { | ||||
|                 this.forceUpdate(); | ||||
|             // TODO -- Handle Scalar errors
 | ||||
|             // },
 | ||||
|             // (err) => {
 | ||||
|             //     this.setState({
 | ||||
|             //         scalar_error: err,
 | ||||
|             //     });
 | ||||
|             }).catch((e) => { | ||||
|                 console.log("Failed to connect to integrations server"); | ||||
|                 // TODO -- Handle Scalar errors
 | ||||
|                 //     this.setState({
 | ||||
|                 //         scalar_error: err,
 | ||||
|                 //     });
 | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -143,7 +143,6 @@ export default class Autocomplete extends React.Component { | |||
|             return null; | ||||
|         } | ||||
|         this.setSelection(selectionOffset); | ||||
|         return selectionOffset === COMPOSER_SELECTED ? null : this.state.completionList[selectionOffset - 1]; | ||||
|     } | ||||
| 
 | ||||
|     // called from MessageComposerInput
 | ||||
|  | @ -155,7 +154,6 @@ export default class Autocomplete extends React.Component { | |||
|             return null; | ||||
|         } | ||||
|         this.setSelection(selectionOffset); | ||||
|         return selectionOffset === COMPOSER_SELECTED ? null : this.state.completionList[selectionOffset - 1]; | ||||
|     } | ||||
| 
 | ||||
|     onEscape(e): boolean { | ||||
|  | @ -201,6 +199,9 @@ export default class Autocomplete extends React.Component { | |||
| 
 | ||||
|     setSelection(selectionOffset: number) { | ||||
|         this.setState({selectionOffset, hide: false}); | ||||
|         if (this.props.onSelectionChange) { | ||||
|             this.props.onSelectionChange(this.state.completionList[selectionOffset - 1]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     componentDidUpdate() { | ||||
|  |  | |||
|  | @ -949,8 +949,7 @@ export default class MessageComposerInput extends React.Component { | |||
|     }; | ||||
| 
 | ||||
|     moveAutocompleteSelection = (up) => { | ||||
|         const completion = up ? this.autocomplete.onUpArrow() : this.autocomplete.onDownArrow(); | ||||
|         return this.setDisplayedCompletion(completion); | ||||
|         up ? this.autocomplete.onUpArrow() : this.autocomplete.onDownArrow(); | ||||
|     }; | ||||
| 
 | ||||
|     onEscape = async (e) => { | ||||
|  | @ -1133,6 +1132,7 @@ export default class MessageComposerInput extends React.Component { | |||
|                     <Autocomplete | ||||
|                         ref={(e) => this.autocomplete = e} | ||||
|                         onConfirm={this.setDisplayedCompletion} | ||||
|                         onSelectionChange={this.setDisplayedCompletion} | ||||
|                         query={this.getAutocompleteQuery(content)} | ||||
|                         selection={selection}/> | ||||
|                 </div> | ||||
|  |  | |||
|  | @ -123,7 +123,19 @@ module.exports = React.createClass({ | |||
|         } | ||||
| 
 | ||||
|         var newElement = ReactDOM.findDOMNode(this); | ||||
|         var startTopOffset = oldTop - newElement.offsetParent.getBoundingClientRect().top; | ||||
|         let startTopOffset; | ||||
|         if (!newElement.offsetParent) { | ||||
|             // this seems to happen sometimes for reasons I don't understand
 | ||||
|             // the docs for `offsetParent` say it may be null if `display` is
 | ||||
|             // `none`, but I can't see why that would happen.
 | ||||
|             console.warn( | ||||
|                 `ReadReceiptMarker for ${this.props.member.userId} in ` + | ||||
|                 `${this.props.member.roomId} has no offsetParent`, | ||||
|             ); | ||||
|             startTopOffset = 0; | ||||
|         } else { | ||||
|             startTopOffset = oldTop - newElement.offsetParent.getBoundingClientRect().top; | ||||
|         } | ||||
| 
 | ||||
|         var startStyles = []; | ||||
|         var enterTransitionOpts = []; | ||||
|  | @ -131,13 +143,12 @@ module.exports = React.createClass({ | |||
|         if (oldInfo && oldInfo.left) { | ||||
|             // start at the old height and in the old h pos
 | ||||
| 
 | ||||
|             var leftOffset = oldInfo.left; | ||||
|             startStyles.push({ top: startTopOffset+"px", | ||||
|                                left: oldInfo.left+"px" }); | ||||
| 
 | ||||
|             var reorderTransitionOpts = { | ||||
|                 duration: 100, | ||||
|                 easing: 'easeOut' | ||||
|                 easing: 'easeOut', | ||||
|             }; | ||||
| 
 | ||||
|             enterTransitionOpts.push(reorderTransitionOpts); | ||||
|  | @ -175,7 +186,7 @@ module.exports = React.createClass({ | |||
|         if (this.props.timestamp) { | ||||
|             title = _t( | ||||
|                 "Seen by %(userName)s at %(dateTime)s", | ||||
|                 {userName: this.props.member.userId, dateTime: DateUtils.formatDate(new Date(this.props.timestamp), this.props.showTwelveHour)} | ||||
|                 {userName: this.props.member.userId, dateTime: DateUtils.formatDate(new Date(this.props.timestamp), this.props.showTwelveHour)}, | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -555,6 +555,7 @@ module.exports = React.createClass({ | |||
|                              label={ _t('Invites') } | ||||
|                              editable={ false } | ||||
|                              order="recent" | ||||
|                              isInvite={true} | ||||
|                              selectedRoom={ self.props.selectedRoom } | ||||
|                              incomingCall={ self.state.incomingCall } | ||||
|                              collapsed={ self.props.collapsed } | ||||
|  |  | |||
|  | @ -179,7 +179,7 @@ | |||
|     "Profile": "Profil", | ||||
|     "Refer a friend to Riot:": "Freunde zu Riot einladen:", | ||||
|     "rejected": "abgelehnt", | ||||
|     "Once you've followed the link it contains, click below": "Nachdem du dem darin enthaltenen Link gefolgt bist, klicke unten", | ||||
|     "Once you've followed the link it contains, click below": "Nachdem du dem darin enthaltenen Link gefolgt bist, klicke unten", | ||||
|     "rejected the invitation.": "lehnte die Einladung ab.", | ||||
|     "Reject invitation": "Einladung ablehnen", | ||||
|     "Remove Contact Information?": "Kontakt-Informationen entfernen?", | ||||
|  |  | |||
|  | @ -688,7 +688,7 @@ | |||
|     "No display name": "Χωρίς όνομα", | ||||
|     "No users have specific privileges in this room": "Κανένας χρήστης δεν έχει συγκεκριμένα δικαιώματα σε αυτό το δωμάτιο", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "Μόλις ενεργοποιηθεί η κρυπτογράφηση για ένα δωμάτιο, δεν μπορεί να απενεργοποιηθεί ξανά (για τώρα)", | ||||
|     "Once you've followed the link it contains, click below": "Μόλις ακολουθήσετε τον σύνδεσμο που περιέχει, κάντε κλικ παρακάτω", | ||||
|     "Once you've followed the link it contains, click below": "Μόλις ακολουθήσετε τον σύνδεσμο που περιέχει, κάντε κλικ παρακάτω", | ||||
|     "Only people who have been invited": "Μόνο άτομα που έχουν προσκληθεί", | ||||
|     "Otherwise, <a>click here</a> to send a bug report.": "Διαφορετικά, κάντε <a>κλικ εδώ</a> για να αποστείλετε μια αναφορά σφάλματος.", | ||||
|     "%(senderName)s placed a %(callType)s call.": "Ο %(senderName)s πραγματοποίησε μια %(callType)s κλήση.", | ||||
|  |  | |||
|  | @ -440,7 +440,7 @@ | |||
|     "OK": "OK", | ||||
|     "olm version:": "olm version:", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)", | ||||
|     "Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below", | ||||
|     "Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below", | ||||
|     "Only people who have been invited": "Only people who have been invited", | ||||
|     "Operation failed": "Operation failed", | ||||
|     "Otherwise, <a>click here</a> to send a bug report.": "Otherwise, <a>click here</a> to send a bug report.", | ||||
|  | @ -969,5 +969,6 @@ | |||
|     "Hide avatars in user and room mentions": "Hide avatars in user and room mentions", | ||||
|     "%(widgetName)s widget added by %(senderName)s": "%(widgetName)s widget added by %(senderName)s", | ||||
|     "%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget removed by %(senderName)s", | ||||
|     "%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s widget modified by %(senderName)s", | ||||
|     "Robot check is currently unavailable on desktop - please use a <a>web browser</a>": "Robot check is currently unavailable on desktop - please use a <a>web browser</a>" | ||||
| } | ||||
|  |  | |||
|  | @ -402,7 +402,7 @@ | |||
|     "OK": "OK", | ||||
|     "olm version:": "olm version:", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)", | ||||
|     "Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below", | ||||
|     "Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below", | ||||
|     "Only people who have been invited": "Only people who have been invited", | ||||
|     "Operation failed": "Operation failed", | ||||
|     "Password": "Password", | ||||
|  |  | |||
|  | @ -658,7 +658,7 @@ | |||
|     "Hide join/leave messages (invites/kicks/bans unaffected)": "Ocultar mensajes de entrada/salida (no afecta invitaciones/kicks/bans)", | ||||
|     "Hide avatar and display name changes": "Ocultar cambios de avatar y nombre visible", | ||||
|     "Matrix Apps": "Aplicaciones Matrix", | ||||
|     "Once you've followed the link it contains, click below": "Cuando haya seguido el enlace que contiene, haga click debajo", | ||||
|     "Once you've followed the link it contains, click below": "Cuando haya seguido el enlace que contiene, haga click debajo", | ||||
|     "Sets the room topic": "Configura el tema de la sala", | ||||
|     "Show Apps": "Mostrar aplicaciones", | ||||
|     "To get started, please pick a username!": "Para empezar, ¡por favor elija un nombre de usuario!", | ||||
|  |  | |||
|  | @ -495,7 +495,7 @@ | |||
|     "No users have specific privileges in this room": "Ez dago gela honetan baimen zehatzik duen erabiltzailerik", | ||||
|     "olm version:": "olm bertsioa:", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "Behin gela batean zifratzea gaituta ezin da gero desgaitu (oraingoz)", | ||||
|     "Once you've followed the link it contains, click below": "Behin dakarren esteka jarraitu duzula, egin klik azpian", | ||||
|     "Once you've followed the link it contains, click below": "Behin dakarren esteka jarraitu duzula, egin klik azpian", | ||||
|     "Otherwise, <a>click here</a> to send a bug report.": "Bestela, <a>bidali arazte-txosten bat</a>.", | ||||
|     "Server may be unavailable, overloaded, or you hit a bug.": "Agian zerbitzaria ez dago eskuragarri, edo gainezka dago, edo akats bat aurkitu duzu.", | ||||
|     "Changing password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Oraingoz pasahitza aldatzeak gailu guztietako muturretik muturrerako zifratze-gakoak berrezarriko ditu, eta ezin izango dituzu zifratutako txatetako historialak irakurri ez badituzu aurretik zure gelako gakoak esportatzen eta aldaketa eta gero berriro inportatzen. Etorkizunean hau hobetuko da.", | ||||
|  |  | |||
|  | @ -1 +1 @@ | |||
| {} | ||||
| {} | ||||
|  |  | |||
|  | @ -393,7 +393,7 @@ | |||
|     "Mute": "Couper le son", | ||||
|     "No users have specific privileges in this room": "Aucun utilisateur n’a de privilège spécifique dans ce salon", | ||||
|     "olm version:": "version de olm :", | ||||
|     "Once you've followed the link it contains, click below": "Une fois que vous aurez suivi le lien qu’il contient, cliquez ci-dessous", | ||||
|     "Once you've followed the link it contains, click below": "Une fois que vous aurez suivi le lien qu’il contient, cliquez ci-dessous", | ||||
|     "%(senderName)s placed a %(callType)s call.": "%(senderName)s a placé un appel %(callType)s.", | ||||
|     "Please check your email and click on the link it contains. Once this is done, click continue.": "Veuillez vérifier vos e-mails et cliquer sur le lien que vous avez reçu. Puis cliquez sur continuer.", | ||||
|     "Power level must be positive integer.": "Le niveau d'autorité doit être un entier positif.", | ||||
|  |  | |||
|  | @ -443,7 +443,7 @@ | |||
|     "No users have specific privileges in this room": "Egy felhasználónak sincsenek specifikus jogosultságai ebben a szobában", | ||||
|     "olm version:": "olm verzió:", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "Ha egyszer bekapcsolod a titkosítást a szobába utána nem lehet kikapcsolni (egyenlőre)", | ||||
|     "Once you've followed the link it contains, click below": "Miután a linket követted, kattints alulra", | ||||
|     "Once you've followed the link it contains, click below": "Miután a linket követted, kattints alulra", | ||||
|     "Only people who have been invited": "Csak akiket meghívtak", | ||||
|     "Otherwise, <a>click here</a> to send a bug report.": "Különben hiba jelentés küldéséhez <a>kattints ide</a>.", | ||||
|     "Password": "Jelszó", | ||||
|  |  | |||
|  | @ -452,7 +452,7 @@ | |||
|     "People": "사람들", | ||||
|     "Phone": "전화", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "방을 암호화하면 암호화를 도중에 끌 수 없어요. (현재로서는)", | ||||
|     "Once you've followed the link it contains, click below": "포함된 주소를 따라가서, 아래를 누르세요", | ||||
|     "Once you've followed the link it contains, click below": "포함된 주소를 따라가서, 아래를 누르세요", | ||||
|     "Only people who have been invited": "초대받은 사람만", | ||||
|     "Otherwise, <a>click here</a> to send a bug report.": "그 밖에는, <a>여기를 눌러</a> 오류 보고서를 보내주세요.", | ||||
|     "%(senderName)s placed a %(callType)s call.": "%(senderName)s님이 %(callType)s 전화를 걸었어요.", | ||||
|  |  | |||
|  | @ -427,7 +427,7 @@ | |||
|     "OK": "LABI", | ||||
|     "olm version:": "olm versija:", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "Tiklīdz istabai tiks iespējota šifrēšana, tā vairs nebūs atslēdzama (pašlaik)", | ||||
|     "Once you've followed the link it contains, click below": "Tiklīdz sekoji saturā esošajai saitei, noklikšķini zemāk", | ||||
|     "Once you've followed the link it contains, click below": "Tiklīdz sekoji saturā esošajai saitei, noklikšķini zemāk", | ||||
|     "Only people who have been invited": "Vienīgi personas, kuras ir tikušas uzaicinātas", | ||||
|     "Operation failed": "Darbība neizdevās", | ||||
|     "Otherwise, <a>click here</a> to send a bug report.": "pretējā gadījumā, <a>klikšķini šeit</a>, lai nosūtītu paziņojumu par kļūdu.", | ||||
|  |  | |||
|  | @ -1 +1 @@ | |||
| {} | ||||
| {} | ||||
|  |  | |||
|  | @ -502,7 +502,7 @@ | |||
|     "New passwords don't match": "Nieuwe wachtwoorden komen niet overeen", | ||||
|     "New passwords must match each other.": "Nieuwe wachtwoorden moeten overeenkomen.", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "Zodra versleuteling in een kamer is ingeschakeld kan het niet meer worden uitgeschakeld (voor nu)", | ||||
|     "Once you've followed the link it contains, click below": "Zodra je de link dat het bevat hebt gevolgd, klik hieronder", | ||||
|     "Once you've followed the link it contains, click below": "Zodra je de link dat het bevat hebt gevolgd, klik hieronder", | ||||
|     "Only people who have been invited": "Alleen personen die zijn uitgenodigd", | ||||
|     "Otherwise, <a>click here</a> to send a bug report.": "Klik anders <a>hier</a> om een foutmelding te versturen.", | ||||
|     "Please check your email and click on the link it contains. Once this is done, click continue.": "Bekijk je e-mail en klik op de link die het bevat. Zodra dit klaar is, klik op verder gaan.", | ||||
|  |  | |||
|  | @ -149,7 +149,7 @@ | |||
|     "No users have specific privileges in this room": "Nenhum/a usuário/a possui privilégios específicos nesta sala", | ||||
|     "olm version: ": "Versão do olm: ", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "Assim que a criptografia é ativada para uma sala, ela não poderá ser desativada novamente (ainda)", | ||||
|     "Once you've followed the link it contains, click below": "Quando você tiver clicado no link que está no email, clique o botão abaixo", | ||||
|     "Once you've followed the link it contains, click below": "Quando você tiver clicado no link que está no email, clique o botão abaixo", | ||||
|     "Only people who have been invited": "Apenas pessoas que tenham sido convidadas", | ||||
|     "or": "ou", | ||||
|     "other": "outro", | ||||
|  |  | |||
|  | @ -149,7 +149,7 @@ | |||
|     "No users have specific privileges in this room": "Nenhum/a usuário/a possui privilégios específicos nesta sala", | ||||
|     "olm version: ": "Versão do olm: ", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "Assim que a criptografia é ativada para uma sala, ela não poderá ser desativada novamente (ainda)", | ||||
|     "Once you've followed the link it contains, click below": "Quando você tiver clicado no link que está no email, clique o botão abaixo", | ||||
|     "Once you've followed the link it contains, click below": "Quando você tiver clicado no link que está no email, clique o botão abaixo", | ||||
|     "Only people who have been invited": "Apenas pessoas que tenham sido convidadas", | ||||
|     "or": "ou", | ||||
|     "other": "outro", | ||||
|  |  | |||
|  | @ -702,7 +702,7 @@ | |||
|     "Jump to first unread message.": "Перейти к первому непрочитанному сообщению.", | ||||
|     "Message not sent due to unknown devices being present": "Сообщение не отправлено из-за присутствия неизвестных устройств", | ||||
|     "Mobile phone number (optional)": "Номер мобильного телефона (не обязательно)", | ||||
|     "Once you've followed the link it contains, click below": "После перехода по ссылке, нажмите на кнопку ниже", | ||||
|     "Once you've followed the link it contains, click below": "После перехода по ссылке, нажмите на кнопку ниже", | ||||
|     "Password:": "Пароль:", | ||||
|     "Privacy warning": "Предупреждение о конфиденциальности", | ||||
|     "Privileged Users": "Привилегированные пользователи", | ||||
|  |  | |||
|  | @ -427,7 +427,7 @@ | |||
|     "OK": "OK", | ||||
|     "olm version:": "olm-version:", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "När kryptering aktiveras i ett rum kan det inte deaktiveras (tills vidare)", | ||||
|     "Once you've followed the link it contains, click below": "När du har följt länken i meddelandet, klicka här", | ||||
|     "Once you've followed the link it contains, click below": "När du har följt länken i meddelandet, klicka här", | ||||
|     "Only people who have been invited": "Endast inbjudna", | ||||
|     "Operation failed": "Handlingen misslyckades", | ||||
|     "Otherwise, <a>click here</a> to send a bug report.": "Annars kan du <a>klicka här</a> för att skicka en buggrapport.", | ||||
|  |  | |||
|  | @ -251,7 +251,7 @@ | |||
|     "NOT verified": "ยังไม่ได้ยืนยัน", | ||||
|     "No more results": "ไม่มีผลลัพธ์อื่น", | ||||
|     "No results": "ไม่มีผลลัพธ์", | ||||
|     "Once you've followed the link it contains, click below": "หลังจากคุณเปิดลิงก์ข้างในแล้ว คลิกข้างล่าง", | ||||
|     "Once you've followed the link it contains, click below": "หลังจากคุณเปิดลิงก์ข้างในแล้ว คลิกข้างล่าง", | ||||
|     "Passwords can't be empty": "รหัสผ่านต้องไม่ว่าง", | ||||
|     "People": "บุคคล", | ||||
|     "Permissions": "สิทธิ์", | ||||
|  |  | |||
|  | @ -424,7 +424,7 @@ | |||
|     "OK": "Tamam", | ||||
|     "olm version:": "olm versiyon:", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "Bu oda için şifreleme etkinleştirildikten sonra tekrar kapatılamaz (şimdilik)", | ||||
|     "Once you've followed the link it contains, click below": "Bir kere ' içerdiği bağlantıyı takip ettikten sonra , aşağıya tıklayın", | ||||
|     "Once you've followed the link it contains, click below": "Bir kere ' içerdiği bağlantıyı takip ettikten sonra , aşağıya tıklayın", | ||||
|     "Only people who have been invited": "Sadece davet edilmiş insanlar", | ||||
|     "Operation failed": "Operasyon başarısız oldu", | ||||
|     "Otherwise, <a>click here</a> to send a bug report.": "Aksi taktirde , bir hata raporu göndermek için <a> buraya tıklayın </a>.", | ||||
|  |  | |||
|  | @ -556,7 +556,7 @@ | |||
|     "No users have specific privileges in this room": "此房間中沒有使用者有指定的權限", | ||||
|     "olm version:": "olm 版本:", | ||||
|     "Once encryption is enabled for a room it cannot be turned off again (for now)": "這個房間只要啟用加密就不能再關掉了(從現在開始)", | ||||
|     "Once you've followed the link it contains, click below": "一旦您跟著它所包含的連結,點選下方", | ||||
|     "Once you've followed the link it contains, click below": "一旦您跟著它所包含的連結,點選下方", | ||||
|     "Only people who have been invited": "僅有被邀請的夥伴", | ||||
|     "Otherwise, <a>click here</a> to send a bug report.": "否則,請<a>點選此處</a>來傳送錯誤報告。", | ||||
|     "Password": "密碼", | ||||
|  |  | |||
|  | @ -231,35 +231,31 @@ export function getCurrentLanguage() { | |||
| } | ||||
| 
 | ||||
| function getLangsJson() { | ||||
|     const deferred = Promise.defer(); | ||||
| 
 | ||||
|     request( | ||||
|         { method: "GET", url: i18nFolder + 'languages.json' }, | ||||
|         (err, response, body) => { | ||||
|             if (err || response.status < 200 || response.status >= 300) { | ||||
|                 deferred.reject({err: err, response: response}); | ||||
|                 return; | ||||
|     return new Promise((resolve, reject) => { | ||||
|         request( | ||||
|             { method: "GET", url: i18nFolder + 'languages.json' }, | ||||
|             (err, response, body) => { | ||||
|                 if (err || response.status < 200 || response.status >= 300) { | ||||
|                     reject({err: err, response: response}); | ||||
|                     return; | ||||
|                 } | ||||
|                 resolve(JSON.parse(body)); | ||||
|             } | ||||
|             deferred.resolve(JSON.parse(body)); | ||||
|         } | ||||
|     ); | ||||
|     return deferred.promise; | ||||
|         ); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| function getLanguage(langPath) { | ||||
|     const deferred = Promise.defer(); | ||||
| 
 | ||||
|     let response_return = {}; | ||||
|     request( | ||||
|         { method: "GET", url: langPath }, | ||||
|         (err, response, body) => { | ||||
|             if (err || response.status < 200 || response.status >= 300) { | ||||
|                 deferred.reject({err: err, response: response}); | ||||
|                 return; | ||||
|     return new Promise((resolve, reject) => { | ||||
|         request( | ||||
|             { method: "GET", url: langPath }, | ||||
|             (err, response, body) => { | ||||
|                 if (err || response.status < 200 || response.status >= 300) { | ||||
|                     reject({err: err, response: response}); | ||||
|                     return; | ||||
|                 } | ||||
|                 resolve(JSON.parse(body)); | ||||
|             } | ||||
| 
 | ||||
|             deferred.resolve(JSON.parse(body)); | ||||
|         } | ||||
|     ); | ||||
|     return deferred.promise; | ||||
|         ); | ||||
|     }); | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Weblate
						Weblate