diff --git a/.gitignore b/.gitignore index 2ad05012..14030632 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,6 @@ electron/dist electron/pub **/.idea /config.json +/config.json.* +/config.local*.json /src/component-index.js diff --git a/.travis.yml b/.travis.yml index 94ed745c..bc3fce38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,10 @@ dist: trusty # we don't need sudo, so can run in a container, which makes startup much # quicker. -sudo: false +# +# unfortunately we do temporarily require sudo as a workaround for +# https://github.com/travis-ci/travis-ci/issues/8836 +sudo: required language: node_js node_js: diff --git a/CHANGELOG.md b/CHANGELOG.md index c72ea8da..70fd8c62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,233 @@ +Changes in [0.14.0](https://github.com/vector-im/riot-web/releases/tag/v0.14.0) (2018-04-11) +============================================================================================ +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.14.0-rc.6...v0.14.0) + + * Cosmetic changes for group UI + +Changes in [0.14.0-rc.6](https://github.com/vector-im/riot-web/releases/tag/v0.14.0-rc.6) (2018-04-09) +====================================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.14.0-rc.5...v0.14.0-rc.6) + + * Bump react-sdk to [rc.6](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.12.0-rc.6) + +Changes in [0.14.0-rc.5](https://github.com/vector-im/riot-web/releases/tag/v0.14.0-rc.5) (2018-04-09) +====================================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.14.0-rc.4...v0.14.0-rc.5) + +* Add CSS for new control to set group join policy + +Changes in [0.14.0-rc.4](https://github.com/vector-im/riot-web/releases/tag/v0.14.0-rc.4) (2018-03-22) +====================================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.14.0-rc.3...v0.14.0-rc.4) + + * Fix tagging rooms as direct messages + +Changes in [0.14.0-rc.3](https://github.com/vector-im/riot-web/releases/tag/v0.14.0-rc.3) (2018-03-20) +====================================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.14.0-rc.2...v0.14.0-rc.3) + + * Fix a bug where the badge on a room tile would not update + when a room was read from a different device. + +Changes in [0.14.0-rc.2](https://github.com/vector-im/riot-web/releases/tag/v0.14.0-rc.2) (2018-03-19) +====================================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.14.0-rc.1...v0.14.0-rc.2) + + * Take TagPanel out of labs + [\#6347](https://github.com/vector-im/riot-web/pull/6347) + * Add languages (czech, galician and serbian) + [\#6343](https://github.com/vector-im/riot-web/pull/6343) + +Changes in [0.14.0-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.14.0-rc.1) (2018-03-19) +====================================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.13.5...v0.14.0-rc.1) + + * Force update RoomSubList after reading a room + [\#6342](https://github.com/vector-im/riot-web/pull/6342) + * Ensure entire LeftPanel is faded when settings open + [\#6340](https://github.com/vector-im/riot-web/pull/6340) + * Update from Weblate. + [\#6330](https://github.com/vector-im/riot-web/pull/6330) + * Implement a simple shouldComponentUpdate for DNDRoomTile + [\#6313](https://github.com/vector-im/riot-web/pull/6313) + * Remove og:image with status.im URL + [\#6317](https://github.com/vector-im/riot-web/pull/6317) + * Add change delay warning in GroupView settings + [\#6316](https://github.com/vector-im/riot-web/pull/6316) + * Correctly position mx_TagPanel_clearButton + [\#6289](https://github.com/vector-im/riot-web/pull/6289) + * Fix gap between avatar and border + [\#6290](https://github.com/vector-im/riot-web/pull/6290) + * Fix bug where cannot send group invite on GroupMemberInfo phase + [\#6303](https://github.com/vector-im/riot-web/pull/6303) + * Fix themeing bug with Firefox where "disabled" ignored + [\#6301](https://github.com/vector-im/riot-web/pull/6301) + * Changes for E2E "fudge-button" + [\#6288](https://github.com/vector-im/riot-web/pull/6288) + * Make sure mx_TagPanel_tagTileContainer occupies full height + [\#6286](https://github.com/vector-im/riot-web/pull/6286) + * Add transparent CSS class for RoomTile + [\#6281](https://github.com/vector-im/riot-web/pull/6281) + * Fix crash; fs event received /w langauge file empty + [\#6273](https://github.com/vector-im/riot-web/pull/6273) + * Add setting to disable TagPanel + [\#6269](https://github.com/vector-im/riot-web/pull/6269) + * CSS for my groups microcopy + [\#6257](https://github.com/vector-im/riot-web/pull/6257) + * Add Bulgarian to the list of languages + [\#6246](https://github.com/vector-im/riot-web/pull/6246) + * Make media dropdown wider + [\#6245](https://github.com/vector-im/riot-web/pull/6245) + * Make dropdowns with long options degrade more gracefully + [\#6244](https://github.com/vector-im/riot-web/pull/6244) + * Fix un-tinted "View Community" icon in TagTile context menu + [\#6223](https://github.com/vector-im/riot-web/pull/6223) + * Fix RoomDropTarget and emptySubListTip to have containers + [\#6160](https://github.com/vector-im/riot-web/pull/6160) + * Fix syntax error of wrong use of self-closing HTML tag + [\#6154](https://github.com/vector-im/riot-web/pull/6154) + * Use translucent black for RoomSubList bg to fix tinting + [\#6227](https://github.com/vector-im/riot-web/pull/6227) + * CSS for changing "R" to "X" for clearing group filter + [\#6216](https://github.com/vector-im/riot-web/pull/6216) + * CSS for new global TagPanel filter + [\#6187](https://github.com/vector-im/riot-web/pull/6187) + * Separate the middle panel from the room list + [\#6194](https://github.com/vector-im/riot-web/pull/6194) + * Only use DNDRoomTile for editable sub lists + [\#6176](https://github.com/vector-im/riot-web/pull/6176) + * Adjust CSS to prevent scrollbars on message panel spinner + [\#6131](https://github.com/vector-im/riot-web/pull/6131) + * Implement riot-web side of dragging GroupTile avatars to TagPanel + [\#6143](https://github.com/vector-im/riot-web/pull/6143) + * Fix LeftPanel size being incorrect when TagPanel disabled + [\#6140](https://github.com/vector-im/riot-web/pull/6140) + * Fix TagPanel from collapsing to < 60px when LP collapsed + [\#6134](https://github.com/vector-im/riot-web/pull/6134) + * Temporary hack to constrain LLP container size. + [\#6138](https://github.com/vector-im/riot-web/pull/6138) + * Fix typo + [\#6137](https://github.com/vector-im/riot-web/pull/6137) + * Add context menu to TagPanel + [\#6127](https://github.com/vector-im/riot-web/pull/6127) + * Make room tagging flux-y + [\#6096](https://github.com/vector-im/riot-web/pull/6096) + * Move groups button to TagPanel + [\#6130](https://github.com/vector-im/riot-web/pull/6130) + * Fix long group name pushing settings cog into void + [\#6106](https://github.com/vector-im/riot-web/pull/6106) + * Fix horizontal scrollbar under certain circumstances + [\#6103](https://github.com/vector-im/riot-web/pull/6103) + * Split MImageBody into MFileBody to match JS Classes. + [\#6067](https://github.com/vector-im/riot-web/pull/6067) + * Add Catalan + [\#6040](https://github.com/vector-im/riot-web/pull/6040) + * Update from Weblate. + [\#5777](https://github.com/vector-im/riot-web/pull/5777) + * make FilteredList controlled, such that it can externally persist filter + [\#5718](https://github.com/vector-im/riot-web/pull/5718) + * Linear Rich Quoting + [\#6017](https://github.com/vector-im/riot-web/pull/6017) + * Highlight ViewSource and Devtools ViewSource + [\#5995](https://github.com/vector-im/riot-web/pull/5995) + * default url, not domain + [\#6022](https://github.com/vector-im/riot-web/pull/6022) + * T3chguy/num members tooltip + [\#5929](https://github.com/vector-im/riot-web/pull/5929) + * Swap RoomList to react-beautiful-dnd + [\#6008](https://github.com/vector-im/riot-web/pull/6008) + * CSS required as part of moving TagPanel from react-dnd to react-beautiful- + dnd + [\#5992](https://github.com/vector-im/riot-web/pull/5992) + * fix&refactor DateSeparator and MessageTimestamp + [\#5984](https://github.com/vector-im/riot-web/pull/5984) + * Iterative fixes on Rich Quoting + [\#5978](https://github.com/vector-im/riot-web/pull/5978) + * move piwik whitelists to conf and add piwik config.json info to readme + [\#5653](https://github.com/vector-im/riot-web/pull/5653) + * Implement Rich Quoting/Replies + [\#5804](https://github.com/vector-im/riot-web/pull/5804) + * Change author + [\#5950](https://github.com/vector-im/riot-web/pull/5950) + * Revert "Add a   after timestamp" + [\#5944](https://github.com/vector-im/riot-web/pull/5944) + * Add a   after timestamp + [\#3046](https://github.com/vector-im/riot-web/pull/3046) + * Corrected language name + [\#5938](https://github.com/vector-im/riot-web/pull/5938) + * Hide Options button from copy to clipboard + [\#2892](https://github.com/vector-im/riot-web/pull/2892) + * Fix for `If riot is narrow enough, such that 'Send a message (unecrypted)' + wraps to a second line, the timeline doesn't fit the window.` + [\#5900](https://github.com/vector-im/riot-web/pull/5900) + * Screenshot UI + [\#5849](https://github.com/vector-im/riot-web/pull/5849) + * add missing config.json entry such that scalar-staging widgets work + [\#5855](https://github.com/vector-im/riot-web/pull/5855) + * add dark theme styling to devtools input box + [\#5610](https://github.com/vector-im/riot-web/pull/5610) + * Fixes #1953 by adding oivoodoo as author + [\#5851](https://github.com/vector-im/riot-web/pull/5851) + * Instructions on security issues + [\#5824](https://github.com/vector-im/riot-web/pull/5824) + * Move DND wrapper to top level component + [\#5790](https://github.com/vector-im/riot-web/pull/5790) + * Widget title bar max / min visual cues. + [\#5786](https://github.com/vector-im/riot-web/pull/5786) + * Implement renumeration of ordered tags upon collision + [\#5759](https://github.com/vector-im/riot-web/pull/5759) + * Update imports for accessing KeyCode + [\#5751](https://github.com/vector-im/riot-web/pull/5751) + * Set html lang attribute from language setting + [\#5685](https://github.com/vector-im/riot-web/pull/5685) + * CSS for new TagPanel + [\#5723](https://github.com/vector-im/riot-web/pull/5723) + * getGroupStore no longer needs a matrix client + [\#5707](https://github.com/vector-im/riot-web/pull/5707) + * CSS required for moving group publication toggles to UserSettings + [\#5702](https://github.com/vector-im/riot-web/pull/5702) + * Make sure the SettingsStore is ready to load the theme before loading it + [\#5630](https://github.com/vector-im/riot-web/pull/5630) + * Add some aria-labels to RightPanel + [\#5661](https://github.com/vector-im/riot-web/pull/5661) + * Use badge count format for member count in RightPanel + [\#5657](https://github.com/vector-im/riot-web/pull/5657) + * Exclude the default language on page load + [\#5640](https://github.com/vector-im/riot-web/pull/5640) + * Use SettingsStore to get the default theme + [\#5615](https://github.com/vector-im/riot-web/pull/5615) + * Refactor translations + [\#5613](https://github.com/vector-im/riot-web/pull/5613) + * TintableSvgButton styling + [\#5605](https://github.com/vector-im/riot-web/pull/5605) + * Granular settings + [\#5468](https://github.com/vector-im/riot-web/pull/5468) + * CSS/components for custom presence controls + [\#5286](https://github.com/vector-im/riot-web/pull/5286) + * Set widget tile background colour + [\#5574](https://github.com/vector-im/riot-web/pull/5574) + * Widget styling tweaks + [\#5573](https://github.com/vector-im/riot-web/pull/5573) + * Center mixed content warnings in panel. + [\#5567](https://github.com/vector-im/riot-web/pull/5567) + * Status.im theme + [\#5578](https://github.com/vector-im/riot-web/pull/5578) + +Changes in [0.13.5](https://github.com/vector-im/riot-web/releases/tag/v0.13.5) (2018-02-09) +============================================================================================ +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.13.4...v0.13.5) + + * SECURITY UPDATE: Sanitise URLs from 'external_url'. Thanks to walle303 for contacting + us about this vulnerability. + +Changes in [0.13.4](https://github.com/vector-im/riot-web/releases/tag/v0.13.4) (2018-01-03) +============================================================================================ +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.13.3...v0.13.4) + + * Change config of riot.im electron build to fix some widgets not working. This only affects + electron builds using the riot.im config - for all other builds, this is identical to + v0.13.3. + Changes in [0.13.3](https://github.com/vector-im/riot-web/releases/tag/v0.13.3) (2017-12-04) ============================================================================================ [Full Changelog](https://github.com/vector-im/riot-web/compare/v0.13.2...v0.13.3) diff --git a/README.md b/README.md index 1da20632..705f269d 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,9 @@ config.json You can configure the app by copying `config.sample.json` to `config.json` and customising it: -1. `default_hs_url` is the default home server url. +For a good example, see https://riot.im/develop/config.json + +1. `default_hs_url` is the default homeserver url. 1. `default_is_url` is the default identity server url (this is the server used for verifying third party identifiers like email addresses). If this is blank, registering with an email address, adding an email address to your account, @@ -115,21 +117,49 @@ You can configure the app by copying `config.sample.json` to addresses) to matrix IDs: see http://matrix.org/docs/spec/identity_service/unstable.html for more details. Currently the only public matrix identity servers are https://matrix.org and https://vector.im. In future identity servers will be decentralised. +1. `features`: Lookup of optional features that may be `enable`d, `disable`d, or exposed to the user + in the `labs` section of settings. The available optional experimental features vary from + release to release. +1. `brand`: String to pass to your homeserver when configuring email notifications, to let the + homeserver know what email template to use when talking to you. 1. `integrations_ui_url`: URL to the web interface for the integrations server. The integrations server is not Riot and normally not your Home Server either. The integration server settings may be left blank to disable integrations. 1. `integrations_rest_url`: URL to the REST interface for the integrations server. +1. `integrations_widgets_urls`: list of URLs to the REST interface for the widget integrations server. +1. `bug_report_endpoint_url`: endpoint to send bug reports to (must be running a + https://github.com/matrix-org/rageshake server) 1. `roomDirectory`: config for the public room directory. This section is optional. -1. `roomDirectory.servers`: List of other Home Servers' directories to include in the drop +1. `roomDirectory.servers`: List of other homeservers' directories to include in the drop down list. Optional. +1. `default_theme`: name of theme to use by default (e.g. 'light') 1. `update_base_url` (electron app only): HTTPS URL to a web server to download updates from. This should be the path to the directory containing `macos` and `win32` (for update packages, not installer packages). 1. `cross_origin_renderer_url`: URL to a static HTML page hosting code to help display encrypted file attachments. This MUST be hosted on a completely separate domain to anything else since it is used to isolate the privileges of file attachments to this - domain. Default: `usercontent.riot.im`. This needs to contain v1.html from + domain. Default: `https://usercontent.riot.im/v1.html`. This needs to contain v1.html from https://github.com/matrix-org/usercontent/blob/master/v1.html +1. `piwik`: an object containing the following properties: + 1. `url`: The URL of the Piwik instance to use for collecting Analytics + 1. `whitelistedHSUrls`: a list of HS URLs to not redact from the Analytics + 1. `whitelistedISUrls`: a list of IS URLs to not redact from the Analytics + 1. `siteId`: The Piwik Site ID to use when sending Analytics to the Piwik server configured above +1. `teamServerConfig`, `teamTokenMap`, `referralBaseUrl`: an obsolete precursor to communities + with referral tracking; please ignore it. +1. `welcomeUserId`: the user ID of a bot to invite whenever users register that can give them a tour + + +Note that `index.html` also has an og:image meta tag that is set to an image +hosted on riot.im. This is the image used if links to your copy of Riot +appear in some websites like Facebook, and indeed Riot itself. This has to be +static in the HTML and an absolute URL (and HTTP rather than HTTPS), so it's +not possible for this to be an option in config.json. If you'd like to change +it, you can build Riot as above, but run +`RIOT_OG_IMAGE_URL="http://example.com/logo.png" npm run build`. +Alternatively, you can edit the `og:image` meta tag in `index.html` directly +each time you download a new version of Riot. Running as a Desktop app ======================== @@ -314,31 +344,51 @@ For a developer guide, see the [translating dev doc](docs/translating-dev.md). Triaging issues =============== -Issues will be triaged by the core team using the following primary set of tags: +Issues will be triaged by the core team using the below set of tags. -priority: +Tags are meant to be used in combination - e.g.: + * P1 critical bug == really urgent stuff that should be next in the bugfixing todo list + * "release blocker" == stuff which is blocking us from cutting the next release. + * P1 feature type:voip == what VoIP features should we be working on next? -* P1: top priority; typically blocks releases +priority: **compulsory** + +* P1: top priority - i.e. pool of stuff which we should be working on next * P2: still need to fix, but lower than P1 * P3: non-urgent -* P4: intereseting idea - bluesky some day +* P4: interesting idea - bluesky some day * P5: recorded for posterity/to avoid duplicates. No intention to resolves right now. -bug or feature: +bug or feature: **compulsory** * bug * feature -bug severity: +bug severity: **compulsory, if bug** -* cosmetic - feature works functionally but UI/UX is broken * critical - whole app doesn't work * major - entire feature doesn't work * minor - partially broken feature (but still usable) +* cosmetic - feature works functionally but UI/UX is broken -additional categories: +types +* type:* - refers to a particular part of the app; used to filter bugs + on a given topic - e.g. VOIP, signup, timeline, etc. + +additional categories (self-explanatory): * release blocker * ui/ux (think of this as cosmetic) * network (specific to network conditions) -* platform (platform specific) +* platform specific +* accessibility +* maintenance +* performance +* i18n +* blocked - whether this issue currently can't be progressed due to outside factors + +community engagement +* easy +* hacktoberfest +* bounty? - proposal to be included in a bounty programme +* bounty - included in Status Open Bounty diff --git a/config.sample.json b/config.sample.json index 016517cd..b80746f6 100644 --- a/config.sample.json +++ b/config.sample.json @@ -24,6 +24,11 @@ "welcomeUserId": "@riot-bot:matrix.org", "piwik": { "url": "https://piwik.riot.im/", + "whitelistedHSUrls": ["https://matrix.org"], + "whitelistedISUrls": ["https://vector.im", "https://matrix.org"], "siteId": 1 + }, + "enable_presence_by_hs_url": { + "https://matrix.org": false } } diff --git a/docs/translating.md b/docs/translating.md index 3fd0731d..5cfd9eaa 100644 --- a/docs/translating.md +++ b/docs/translating.md @@ -35,7 +35,7 @@ Head to the explanations under Steb 2b ## Step 2b: Adding a new language 1. Go to one of the projects listed https://translate.riot.im/projects/riot-web/ -2. Click the ``Start new language`` button at the bottom +2. Click the ``Start new translation`` button at the bottom 3. Select a language 4. Start translating like in 2a.3 5. Repeat these steps for the other projects which are listed at the link of step 2b.1 diff --git a/electron_app/package.json b/electron_app/package.json index ad237ebe..5c2fc386 100644 --- a/electron_app/package.json +++ b/electron_app/package.json @@ -2,7 +2,7 @@ "name": "riot-web", "productName": "Riot", "main": "src/electron-main.js", - "version": "0.13.3", + "version": "0.14.0", "description": "A feature-rich client for Matrix.org", "author": "Vector Creations Ltd.", "dependencies": { diff --git a/package.json b/package.json index 050c9f32..d421887a 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,9 @@ "name": "riot-web", "productName": "Riot", "main": "electron_app/src/electron-main.js", - "version": "0.13.3", + "version": "0.14.0", "description": "A feature-rich client for Matrix.org", - "author": "Vector Creations Ltd.", + "author": "New Vector Ltd.", "repository": { "type": "git", "url": "https://github.com/vector-im/riot-web" @@ -68,14 +68,13 @@ "gfm.css": "^1.1.1", "highlight.js": "^9.0.0", "linkifyjs": "^2.1.3", - "matrix-js-sdk": "0.9.2", - "matrix-react-sdk": "0.11.3", + "matrix-js-sdk": "0.10.0", + "matrix-react-sdk": "0.12.1", "modernizr": "^3.1.0", "pako": "^1.0.5", "prop-types": "^15.5.10", "react": "^15.6.0", - "react-dnd": "^2.1.4", - "react-dnd-html5-backend": "^2.1.2", + "react-beautiful-dnd": "^4.0.1", "react-dom": "^15.6.0", "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef", "sanitize-html": "^1.11.1", @@ -144,7 +143,7 @@ "react-addons-perf": "^15.4.0", "react-addons-test-utils": "^15.6.0", "rimraf": "^2.4.3", - "source-map-loader": "^0.1.5", + "source-map-loader": "^0.2.3", "webpack": "^1.12.14", "webpack-dev-server": "^1.16.2" }, @@ -154,7 +153,7 @@ "build": { "appId": "im.riot.app", "category": "Network", - "electronVersion": "1.7.9", + "electronVersion": "1.8.3", "//asar=false": "https://github.com/electron-userland/electron-builder/issues/675", "asar": false, "dereference": true, diff --git a/res/home.html b/res/home.html index 4b8da3da..934e491e 100644 --- a/res/home.html +++ b/res/home.html @@ -284,9 +284,9 @@
- Riot-Web Translations + Riot Translations - _t("Co-ordination for Riot/Web translators") + _t("Co-ordination for Riot translators")
diff --git a/scripts/copy-res.js b/scripts/copy-res.js index 775a9c13..eb3a3daa 100755 --- a/scripts/copy-res.js +++ b/scripts/copy-res.js @@ -7,18 +7,22 @@ // a translation in the app (because having a translation with only // 3 strings translated is just frustrating) // This could readily be automated, but it's nice to explicitly -// control when we languages are available. +// control when new languages are available. const INCLUDE_LANGS = [ + {'value': 'bg', 'label': 'Български'}, + {'value': 'ca', 'label': 'Català'}, + {'value': 'cs', 'label': 'čeština'}, {'value': 'da', 'label': 'Dansk'}, {'value': 'de_DE', 'label': 'Deutsch'}, + {'value': 'el', 'label': 'Ελληνικά'}, {'value': 'en_EN', 'label': 'English'}, {'value': 'en_US', 'label': 'English (US)'}, - {'value': 'el', 'label': 'Ελληνικά'}, {'value': 'eo', 'label': 'Esperanto'}, {'value': 'es', 'label': 'Español'}, - {'value': 'eu', 'label': 'Euskal'}, + {'value': 'eu', 'label': 'Euskara'}, {'value': 'fi', 'label': 'Suomi'}, {'value': 'fr', 'label': 'Français'}, + {'value': 'gl', 'label': 'Galego'}, {'value': 'hu', 'label': 'Magyar'}, {'value': 'ko', 'label': '한국어'}, {'value': 'lv', 'label': 'Latviešu'}, @@ -28,10 +32,11 @@ const INCLUDE_LANGS = [ {'value': 'pt', 'label': 'Português'}, {'value': 'pt_BR', 'label': 'Português do Brasil'}, {'value': 'ru', 'label': 'Русский'}, - {'value': 'sv', 'label': 'Svenska'}, {'value': 'sk', 'label': 'Slovenčina'}, - {'value': 'th', 'label': 'ไทย'}, + {'value': 'sr', 'label': 'српски'}, + {'value': 'sv', 'label': 'Svenska'}, {'value': 'te', 'label': 'తెలుగు'}, + {'value': 'th', 'label': 'ไทย'}, {'value': 'tr', 'label': 'Türk'}, {'value': 'zh_Hans', 'label': '简体中文'}, // simplified chinese {'value': 'zh_Hant', 'label': '繁體中文'}, // traditional chinese @@ -132,8 +137,19 @@ function next(i, err) { const reactSdkFile = 'node_modules/matrix-react-sdk/src/i18n/strings/' + source + '.json'; const riotWebFile = 'src/i18n/strings/' + source + '.json'; - const translations = {}; - const makeLang = () => { genLangFile(source, dest) }; + // XXX: Use a debounce because for some reason if we read the language + // file immediately after the FS event is received, the file contents + // appears empty. Possibly https://github.com/nodejs/node/issues/6112 + let makeLangDebouncer; + const makeLang = () => { + if (makeLangDebouncer) { + clearTimeout(makeLangDebouncer); + } + makeLangDebouncer = setTimeout(() => { + genLangFile(source, dest); + }, 500); + }; + [reactSdkFile, riotWebFile].forEach(function(f) { chokidar.watch(f) .on('add', makeLang) @@ -168,13 +184,13 @@ function genLangFile(lang, dest) { JSON.parse(fs.readFileSync(f).toString()) ); } catch (e) { - console.error("Failed: "+f, e); + console.error("Failed: " + f, e); throw e; } } }); - translations = weblateToCounterpart(translations) + translations = weblateToCounterpart(translations); fs.writeFileSync(dest + lang + '.json', JSON.stringify(translations, null, 4)); if (verbose) { diff --git a/src/components/structures/BottomLeftMenu.js b/src/components/structures/BottomLeftMenu.js index 3aab61a1..3271d5ae 100644 --- a/src/components/structures/BottomLeftMenu.js +++ b/src/components/structures/BottomLeftMenu.js @@ -167,8 +167,11 @@ module.exports = React.createClass({ const StartChatButton = sdk.getComponent('elements.StartChatButton'); const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton'); const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton'); - const GroupsButton = sdk.getComponent('elements.GroupsButton'); const SettingsButton = sdk.getComponent('elements.SettingsButton'); + const GroupsButton = sdk.getComponent('elements.GroupsButton'); + + const groupsButton = SettingsStore.getValue("TagPanel.disableTagPanel") ? + : null; return (
@@ -183,7 +186,7 @@ module.exports = React.createClass({
- + { groupsButton } diff --git a/src/components/structures/HomePage.js b/src/components/structures/HomePage.js index bdba55eb..c46ceeba 100644 --- a/src/components/structures/HomePage.js +++ b/src/components/structures/HomePage.js @@ -18,10 +18,10 @@ limitations under the License. 'use strict'; import React from 'react'; -import GeminiScrollbar from 'react-gemini-scrollbar'; import request from 'browser-request'; import { _t } from 'matrix-react-sdk/lib/languageHandler'; import sanitizeHtml from 'sanitize-html'; +import sdk from 'matrix-react-sdk/lib'; module.exports = React.createClass({ displayName: 'HomePage', @@ -99,11 +99,12 @@ module.exports = React.createClass({ ); } else { + const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper"); return ( - +
-
+ ); } } diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js index f78835b4..0fc3e025 100644 --- a/src/components/structures/LeftPanel.js +++ b/src/components/structures/LeftPanel.js @@ -17,22 +17,28 @@ limitations under the License. 'use strict'; import React from 'react'; +import PropTypes from 'prop-types'; import classNames from 'classnames'; +import { MatrixClient } from 'matrix-js-sdk'; import { KeyCode } from 'matrix-react-sdk/lib/Keyboard'; import sdk from 'matrix-react-sdk'; import dis from 'matrix-react-sdk/lib/dispatcher'; -import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg'; -import CallHandler from 'matrix-react-sdk/lib/CallHandler'; -import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton'; import VectorConferenceHandler from '../../VectorConferenceHandler'; +import SettingsStore from 'matrix-react-sdk/lib/settings/SettingsStore'; + + var LeftPanel = React.createClass({ displayName: 'LeftPanel', // NB. If you add props, don't forget to update // shouldComponentUpdate! propTypes: { - collapsed: React.PropTypes.bool.isRequired, + collapsed: PropTypes.bool.isRequired, + }, + + contextTypes: { + matrixClient: PropTypes.instanceOf(MatrixClient), }, getInitialState: function() { @@ -161,13 +167,18 @@ var LeftPanel = React.createClass({ this.setState({ searchFilter: term }); }, + collectRoomList: function(ref) { + this._roomList = ref; + }, + render: function() { const RoomList = sdk.getComponent('rooms.RoomList'); + const TagPanel = sdk.getComponent('structures.TagPanel'); const BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu'); const CallPreview = sdk.getComponent('voip.CallPreview'); let topBox; - if (MatrixClientPeg.get().isGuest()) { + if (this.context.matrixClient.isGuest()) { const LoginBox = sdk.getComponent('structures.LoginBox'); topBox = ; } else { @@ -175,24 +186,39 @@ var LeftPanel = React.createClass({ topBox = ; } - let classes = classNames( - "mx_LeftPanel", "mx_fadable", + const classes = classNames( + "mx_LeftPanel", { "collapsed": this.props.collapsed, + }, + ); + + const tagPanelEnabled = !SettingsStore.getValue("TagPanel.disableTagPanel"); + const tagPanel = tagPanelEnabled ? :
; + + const containerClasses = classNames( + "mx_LeftPanel_container", "mx_fadable", + { + "mx_LeftPanel_container_collapsed": this.props.collapsed, + "mx_LeftPanel_container_hasTagPanel": tagPanelEnabled, "mx_fadable_faded": this.props.disabled, - } + }, ); return ( - +
+ { tagPanel } + +
); } }); diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js index 2a19794f..39463a67 100644 --- a/src/components/structures/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -24,7 +24,7 @@ import sdk from 'matrix-react-sdk'; import dis from 'matrix-react-sdk/lib/dispatcher'; import { MatrixClient } from 'matrix-js-sdk'; import Analytics from 'matrix-react-sdk/lib/Analytics'; -import rate_limited_func from 'matrix-react-sdk/lib/ratelimitedfunc'; +import RateLimitedFunc from 'matrix-react-sdk/lib/ratelimitedfunc'; import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton'; import { showGroupInviteDialog, showGroupAddRoomDialog } from 'matrix-react-sdk/lib/GroupAddressPicker'; import GroupStoreCache from 'matrix-react-sdk/lib/stores/GroupStoreCache'; @@ -58,8 +58,8 @@ class HeaderButton extends React.Component {
{ this.props.badge ? this.props.badge :   }
- - { this.props.isHighlighted ?
:
} + + { this.props.isHighlighted ?
:
} ; } @@ -167,35 +167,40 @@ module.exports = React.createClass({ return; } - if (this.state.phase === this.Phase.GroupMemberList) { - showGroupInviteDialog(this.props.groupId); - } else if (this.state.phase === this.Phase.GroupRoomList) { - showGroupAddRoomDialog(this.props.groupId).then(() => { - this.forceUpdate(); + // call AddressPickerDialog + dis.dispatch({ + action: 'view_invite', + roomId: this.props.roomId, + }); + }, + + onInviteToGroupButtonClick: function() { + showGroupInviteDialog(this.props.groupId).then(() => { + this.setState({ + phase: this.Phase.GroupMemberList, }); - } else { - // call AddressPickerDialog - dis.dispatch({ - action: 'view_invite', - roomId: this.props.roomId, - }); - } + }); + }, + + onAddRoomToGroupButtonClick: function() { + showGroupAddRoomDialog(this.props.groupId).then(() => { + this.forceUpdate(); + }); }, onRoomStateMember: function(ev, state, member) { // redraw the badge on the membership list - if (this.state.phase == this.Phase.RoomMemberList && member.roomId === this.props.roomId) { + if (this.state.phase === this.Phase.RoomMemberList && member.roomId === this.props.roomId) { this._delayedUpdate(); - } - else if (this.state.phase === this.Phase.RoomMemberInfo && member.roomId === this.props.roomId && + } else if (this.state.phase === this.Phase.RoomMemberInfo && member.roomId === this.props.roomId && member.userId === this.state.member.userId) { // refresh the member info (e.g. new power level) this._delayedUpdate(); } }, - _delayedUpdate: new rate_limited_func(function() { - this.forceUpdate(); + _delayedUpdate: new RateLimitedFunc(function() { + this.forceUpdate(); // eslint-disable-line babel/no-invalid-this }, 500), onAction: function(payload) { @@ -234,6 +239,10 @@ module.exports = React.createClass({ this.setState({ phase: this.Phase.GroupRoomList, }); + } else if (payload.action === "view_group_member_list") { + this.setState({ + phase: this.Phase.GroupMemberList, + }); } else if (payload.action === "view_group_user") { this.setState({ phase: this.Phase.GroupMemberInfo, @@ -266,22 +275,23 @@ module.exports = React.createClass({ let inviteGroup; let membersBadge; - if ((this.state.phase == this.Phase.RoomMemberList || this.state.phase === this.Phase.RoomMemberInfo) + let membersTitle = _t('Members'); + if ((this.state.phase === this.Phase.RoomMemberList || this.state.phase === this.Phase.RoomMemberInfo) && this.props.roomId ) { const cli = this.context.matrixClient; const room = cli.getRoom(this.props.roomId); - let userIsInRoom; + let isUserInRoom; if (room) { - membersBadge = formatCount(room.getJoinedMembers().length); - userIsInRoom = room.hasMembershipState( - this.context.matrixClient.credentials.userId, 'join', - ); + const numMembers = room.getJoinedMembers().length; + membersTitle = _t('%(count)s Members', { count: numMembers }); + membersBadge =
{ formatCount(numMembers) }
; + isUserInRoom = room.hasMembershipState(this.context.matrixClient.credentials.userId, 'join'); } - if (userIsInRoom) { + if (isUserInRoom) { inviteGroup = - +
@@ -292,13 +302,13 @@ module.exports = React.createClass({ const isPhaseGroup = [ this.Phase.GroupMemberInfo, - this.Phase.GroupMemberList + this.Phase.GroupMemberList, ].includes(this.state.phase); let headerButtons = []; if (this.props.roomId) { headerButtons = [ - - +
, ); } let panel =
; if (!this.props.collapsed) { - if (this.props.roomId && this.state.phase == this.Phase.RoomMemberList) { + if (this.props.roomId && this.state.phase === this.Phase.RoomMemberList) { panel = ; - } else if (this.props.groupId && this.state.phase == this.Phase.GroupMemberList) { + } else if (this.props.groupId && this.state.phase === this.Phase.GroupMemberList) { panel = ; } else if (this.state.phase === this.Phase.GroupRoomList) { panel = ; - } else if (this.state.phase == this.Phase.RoomMemberInfo) { + } else if (this.state.phase === this.Phase.RoomMemberInfo) { panel = ; - } else if (this.state.phase == this.Phase.GroupMemberInfo) { + } else if (this.state.phase === this.Phase.GroupMemberInfo) { panel = ; - } else if (this.state.phase == this.Phase.GroupRoomInfo) { + } else if (this.state.phase === this.Phase.GroupRoomInfo) { panel = ; - } else if (this.state.phase == this.Phase.NotificationPanel) { + } else if (this.state.phase === this.Phase.NotificationPanel) { panel = ; - } else if (this.state.phase == this.Phase.FilePanel) { + } else if (this.state.phase === this.Phase.FilePanel) { panel = ; } } if (!panel) { - panel =
; + panel =
; } if (this.props.groupId && this.state.isUserPrivilegedInGroup) { inviteGroup = isPhaseGroup ? ( - +
{ _t('Invite to this community') }
) : ( - +
@@ -392,19 +402,16 @@ module.exports = React.createClass({ ); } - let classes = classNames( - "mx_RightPanel", "mx_fadable", - { - "collapsed": this.props.collapsed, - "mx_fadable_faded": this.props.disabled, - } - ); + const classes = classNames("mx_RightPanel", "mx_fadable", { + "collapsed": this.props.collapsed, + "mx_fadable_faded": this.props.disabled, + }); return (
); + if (SettingsStore.isFeatureEnabled("feature_rich_quoting")) { + replyButton = ( +
+ { _t('Reply') } +
+ ); + } + if (this.state.canPin) { pinButton = (
- {this._isPinned() ? _t('Unpin Message') : _t('Pin Message')} + { this._isPinned() ? _t('Unpin Message') : _t('Pin Message') }
); } } } - viewSourceButton = ( + const viewSourceButton = (
{ _t('View Source') }
@@ -259,10 +277,10 @@ module.exports = React.createClass({ } // XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID) - permalinkButton = ( + const permalinkButton = ( ); @@ -275,29 +293,33 @@ module.exports = React.createClass({ } // Bridges can provide a 'external_url' to link back to the source. - if( typeof(this.props.mxEvent.event.content.external_url) === "string") { - externalURLButton = ( - + if ( + typeof(this.props.mxEvent.event.content.external_url) === "string" && + isUrlPermitted(this.props.mxEvent.event.content.external_url) + ) { + externalURLButton = ( + ); } return (
- {resendButton} - {redactButton} - {cancelButton} - {forwardButton} - {pinButton} - {viewSourceButton} - {viewClearSourceButton} - {unhidePreviewButton} - {permalinkButton} - {quoteButton} - {externalURLButton} + { resendButton } + { redactButton } + { cancelButton } + { forwardButton } + { pinButton } + { viewSourceButton } + { viewClearSourceButton } + { unhidePreviewButton } + { permalinkButton } + { quoteButton } + { replyButton } + { externalURLButton }
); }, diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js index 1518bb22..06eb347d 100644 --- a/src/components/views/context_menus/RoomTileContextMenu.js +++ b/src/components/views/context_menus/RoomTileContextMenu.js @@ -20,6 +20,7 @@ limitations under the License. import Promise from 'bluebird'; import React from 'react'; import classNames from 'classnames'; +import PropTypes from 'prop-types'; import sdk from 'matrix-react-sdk'; import { _t, _td } from 'matrix-react-sdk/lib/languageHandler'; import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg'; @@ -28,14 +29,15 @@ import DMRoomMap from 'matrix-react-sdk/lib/utils/DMRoomMap'; import * as Rooms from 'matrix-react-sdk/lib/Rooms'; import * as RoomNotifs from 'matrix-react-sdk/lib/RoomNotifs'; import Modal from 'matrix-react-sdk/lib/Modal'; +import RoomListActions from 'matrix-react-sdk/lib/actions/RoomListActions'; module.exports = React.createClass({ displayName: 'RoomTileContextMenu', propTypes: { - room: React.PropTypes.object.isRequired, + room: PropTypes.object.isRequired, /* callback called when the menu is dismissed */ - onFinished: React.PropTypes.func, + onFinished: PropTypes.func, }, getInitialState() { @@ -45,7 +47,7 @@ module.exports = React.createClass({ isFavourite: this.props.room.tags.hasOwnProperty("m.favourite"), isLowPriority: this.props.room.tags.hasOwnProperty("m.lowpriority"), isDirectMessage: Boolean(dmRoomMap.getUserIdForRoomId(this.props.room.roomId)), - } + }; }, componentWillMount: function() { @@ -57,42 +59,16 @@ module.exports = React.createClass({ }, _toggleTag: function(tagNameOn, tagNameOff) { - var self = this; - const roomId = this.props.room.roomId; - var cli = MatrixClientPeg.get(); - if (!cli.isGuest()) { - Promise.delay(500).then(function() { - if (tagNameOff !== null && tagNameOff !== undefined) { - cli.deleteRoomTag(roomId, tagNameOff).finally(function() { - // Close the context menu - if (self.props.onFinished) { - self.props.onFinished(); - }; - }).catch(function(err) { - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Failed to remove tag from room 1', '', ErrorDialog, { - title: _t('Failed to remove tag %(tagName)s from room', {tagName: tagNameOff}), - description: ((err && err.message) ? err.message : _t('Operation failed')), - }); - }); - } + if (!MatrixClientPeg.get().isGuest()) { + Promise.delay(500).then(() => { + dis.dispatch(RoomListActions.tagRoom( + MatrixClientPeg.get(), + this.props.room, + tagNameOff, tagNameOn, + undefined, 0, + ), true); - if (tagNameOn !== null && tagNameOn !== undefined) { - // If the tag ordering meta data is required, it is added by - // the RoomSubList when it sorts its rooms - cli.setRoomTag(roomId, tagNameOn, {}).finally(function() { - // Close the context menu - if (self.props.onFinished) { - self.props.onFinished(); - }; - }).catch(function(err) { - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Failed to remove tag from room 2', '', ErrorDialog, { - title: _t('Failed to remove tag %(tagName)s from room', {tagName: tagNameOn}), - description: ((err && err.message) ? err.message : _t('Operation failed')), - }); - }); - } + this.props.onFinished(); }); } }, @@ -132,22 +108,22 @@ module.exports = React.createClass({ }, _onClickDM: function() { + if (MatrixClientPeg.get().isGuest()) return; + const newIsDirectMessage = !this.state.isDirectMessage; this.setState({ isDirectMessage: newIsDirectMessage, }); - if (MatrixClientPeg.get().isGuest()) return; - Rooms.guessAndSetDMRoom( - this.props.room, newIsDirectMessage + this.props.room, newIsDirectMessage, ).delay(500).finally(() => { // Close the context menu if (this.props.onFinished) { this.props.onFinished(); - }; + } }, (err) => { - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to set Direct Message status of room', '', ErrorDialog, { title: _t('Failed to set Direct Message status of room'), description: ((err && err.message) ? err.message : _t('Operation failed')), @@ -165,7 +141,7 @@ module.exports = React.createClass({ // Close the context menu if (this.props.onFinished) { this.props.onFinished(); - }; + } }, _onClickReject: function() { @@ -177,7 +153,7 @@ module.exports = React.createClass({ // Close the context menu if (this.props.onFinished) { this.props.onFinished(); - }; + } }, _onClickForget: function() { @@ -185,8 +161,8 @@ module.exports = React.createClass({ MatrixClientPeg.get().forget(this.props.room.roomId).done(function() { dis.dispatch({ action: 'view_next_room' }); }, function(err) { - var errCode = err.errcode || _td("unknown error code"); - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + const errCode = err.errcode || _td("unknown error code"); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to forget room', '', ErrorDialog, { title: _t('Failed to forget room %(errCode)s', {errCode: errCode}), description: ((err && err.message) ? err.message : _t('Operation failed')), @@ -196,20 +172,19 @@ module.exports = React.createClass({ // Close the context menu if (this.props.onFinished) { this.props.onFinished(); - }; + } }, _saveNotifState: function(newState) { + if (MatrixClientPeg.get().isGuest()) return; + const oldState = this.state.roomNotifState; const roomId = this.props.room.roomId; - var cli = MatrixClientPeg.get(); - - if (cli.isGuest()) return; this.setState({ roomNotifState: newState, }); - RoomNotifs.setRoomNotifsState(this.props.room.roomId, newState).done(() => { + RoomNotifs.setRoomNotifsState(roomId, newState).done(() => { // delay slightly so that the user can see their state change // before closing the menu return Promise.delay(500).then(() => { @@ -217,7 +192,7 @@ module.exports = React.createClass({ // Close the context menu if (this.props.onFinished) { this.props.onFinished(); - }; + } }); }, (error) => { // TODO: some form of error notification to the user @@ -247,22 +222,22 @@ module.exports = React.createClass({ }, _renderNotifMenu: function() { - var alertMeClasses = classNames({ + const alertMeClasses = classNames({ 'mx_RoomTileContextMenu_notif_field': true, 'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.ALL_MESSAGES_LOUD, }); - var allNotifsClasses = classNames({ + const allNotifsClasses = classNames({ 'mx_RoomTileContextMenu_notif_field': true, 'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.ALL_MESSAGES, }); - var mentionsClasses = classNames({ + const mentionsClasses = classNames({ 'mx_RoomTileContextMenu_notif_field': true, 'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.MENTIONS_ONLY, }); - var muteNotifsClasses = classNames({ + const muteNotifsClasses = classNames({ 'mx_RoomTileContextMenu_notif_field': true, 'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.MUTE, }); @@ -272,22 +247,22 @@ module.exports = React.createClass({
-
+
- { _t('All messages (loud)') } + { _t('All messages (noisy)') }
-
+
{ _t('All messages') }
-
+
{ _t('Mentions only') }
-
+
{ _t('Mute') } @@ -322,7 +297,7 @@ module.exports = React.createClass({ return (
-
+
{ leaveText }
@@ -351,17 +326,17 @@ module.exports = React.createClass({ return (
-
+
{ _t('Favourite') }
-
+
{ _t('Low Priority') }
-
+
{ _t('Direct Chat') } @@ -372,7 +347,7 @@ module.exports = React.createClass({ render: function() { const myMember = this.props.room.getMember( - MatrixClientPeg.get().credentials.userId + MatrixClientPeg.get().credentials.userId, ); // Can't set notif level or tags on non-join rooms @@ -389,5 +364,5 @@ module.exports = React.createClass({ { this._renderRoomTagMenu() }
); - } + }, }); diff --git a/src/components/views/context_menus/TagTileContextMenu.js b/src/components/views/context_menus/TagTileContextMenu.js new file mode 100644 index 00000000..576e8485 --- /dev/null +++ b/src/components/views/context_menus/TagTileContextMenu.js @@ -0,0 +1,75 @@ +/* +Copyright 2018 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 React from 'react'; +import PropTypes from 'prop-types'; +import { _t } from 'matrix-react-sdk/lib/languageHandler'; +import dis from 'matrix-react-sdk/lib/dispatcher'; +import TagOrderActions from 'matrix-react-sdk/lib/actions/TagOrderActions'; +import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg'; +import sdk from 'matrix-react-sdk/lib/index'; + +export default class TagTileContextMenu extends React.Component { + static propTypes = { + tag: PropTypes.string.isRequired, + /* callback called when the menu is dismissed */ + onFinished: PropTypes.func.isRequired, + }; + + constructor() { + super(); + + this._onViewCommunityClick = this._onViewCommunityClick.bind(this); + this._onRemoveClick = this._onRemoveClick.bind(this); + } + + _onViewCommunityClick() { + dis.dispatch({ + action: 'view_group', + group_id: this.props.tag, + }); + this.props.onFinished(); + } + + _onRemoveClick() { + dis.dispatch(TagOrderActions.removeTag( + // XXX: Context menus don't have a MatrixClient context + MatrixClientPeg.get(), + this.props.tag, + )); + this.props.onFinished(); + } + + render() { + const TintableSvg = sdk.getComponent("elements.TintableSvg"); + return
+
+ + { _t('View Community') } +
+
+
+ + { _t('Remove') } +
+
; + } +} diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js index 849d32f8..95b5a6a0 100644 --- a/src/components/views/dialogs/BugReportDialog.js +++ b/src/components/views/dialogs/BugReportDialog.js @@ -27,6 +27,7 @@ export default class BugReportDialog extends React.Component { sendLogs: true, busy: false, err: null, + issueUrl: "", text: "", progress: null, }; @@ -34,6 +35,7 @@ export default class BugReportDialog extends React.Component { this._onSubmit = this._onSubmit.bind(this); this._onCancel = this._onCancel.bind(this); this._onTextChange = this._onTextChange.bind(this); + this._onIssueUrlChange = this._onIssueUrlChange.bind(this); this._onSendLogsChange = this._onSendLogsChange.bind(this); this._sendProgressCallback = this._sendProgressCallback.bind(this); } @@ -47,28 +49,24 @@ export default class BugReportDialog extends React.Component { } _onSubmit(ev) { - const sendLogs = this.state.sendLogs; - const userText = this.state.text; - if (!sendLogs && userText.trim().length === 0) { - this.setState({ - err: _t("Please describe the bug and/or send logs."), - }); - return; - } + const userText = + (this.state.text.length > 0 ? this.state.text + '\n\n': '') + 'Issue: ' + + (this.state.issueUrl.length > 0 ? this.state.issueUrl : 'No issue link given'); + this.setState({ busy: true, progress: null, err: null }); - this._sendProgressCallback(_t("Loading bug report module")); + this._sendProgressCallback(_t("Preparing to send logs")); require(['../../../vector/submit-rageshake'], (s) => { s(SdkConfig.get().bug_report_endpoint_url, { - userText: userText, - sendLogs: sendLogs, + userText, + sendLogs: true, progressCallback: this._sendProgressCallback, }).then(() => { if (!this._unmounted) { this.props.onFinished(false); const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); Modal.createTrackedDialog('Bug report sent', '', QuestionDialog, { - title: _t('Bug report sent'), + title: _t('Logs sent'), description: _t('Thank you!'), hasCancelButton: false, }); @@ -78,7 +76,7 @@ export default class BugReportDialog extends React.Component { this.setState({ busy: false, progress: null, - err: _t("Failed to send report: ") + `${err.message}`, + err: _t("Failed to send logs: ") + `${err.message}`, }); } }); @@ -89,7 +87,11 @@ export default class BugReportDialog extends React.Component { this.setState({ text: ev.target.value }); } - _onSendLogsChange(ev) { + _onIssueUrlChange(ev) { + this.setState({ issueUrl: ev.target.value }); + } + + _onSendLogsChange(ev) { this.setState({ sendLogs: ev.target.checked }); } @@ -130,27 +132,61 @@ export default class BugReportDialog extends React.Component { return (
- { _t("Report a bug") } + { _t("Submit debug logs") }

- { _t("Please describe the bug. What did you do? What did you expect to happen? What actually happened?") } + { _t( + "Debug logs contain application usage data including your " + + "username, the IDs or aliases of the rooms or groups you " + + "have visited and the usernames of other users. They do " + + "not contain messages.", + ) }

-