diff --git a/res/css/views/beta/_BetaCard.scss b/res/css/views/beta/_BetaCard.scss index 05149b7f6c..3463a653fc 100644 --- a/res/css/views/beta/_BetaCard.scss +++ b/res/css/views/beta/_BetaCard.scss @@ -39,19 +39,21 @@ limitations under the License. font-size: $font-15px; line-height: $font-20px; color: $secondary-fg-color; + margin-bottom: 20px; } .mx_AccessibleButton { display: block; - margin: 20px 0; - padding: 12px 40px; - width: max-content; + margin: 12px 0; + padding: 7px 40px; + width: auto; } .mx_BetaCard_disclaimer { font-size: $font-12px; line-height: $font-15px; color: $secondary-fg-color; + margin-top: 20px; } } diff --git a/src/components/views/beta/BetaCard.tsx b/src/components/views/beta/BetaCard.tsx index 96b1fbabe5..c12b05d119 100644 --- a/src/components/views/beta/BetaCard.tsx +++ b/src/components/views/beta/BetaCard.tsx @@ -22,6 +22,9 @@ import AccessibleButton from "../elements/AccessibleButton"; import SettingsStore from "../../../settings/SettingsStore"; import {SettingLevel} from "../../../settings/SettingLevel"; import TextWithTooltip from "../elements/TextWithTooltip"; +import Modal from "../../../Modal"; +import BetaFeedbackDialog from "../dialogs/BetaFeedbackDialog"; +import SdkConfig from "../../../SdkConfig"; interface IProps { title?: string; @@ -63,9 +66,23 @@ const BetaCard = ({ title: titleOverride, featureId }: IProps) => { const info = SettingsStore.getBetaInfo(featureId); if (!info) return null; // Beta is invalid/disabled - const { title, caption, disclaimer, image } = info; + const { title, caption, disclaimer, image, feedbackLabel, feedbackSubheading } = info; const value = SettingsStore.getValue(featureId); + let feedbackButton; + if (value && feedbackLabel && feedbackSubheading && SdkConfig.get().bug_report_endpoint_url) { + feedbackButton = { + Modal.createTrackedDialog("Beta Feedback", featureId, BetaFeedbackDialog, { + featureId, + }); + }} + kind="primary" + > + { _t("Feedback") } + ; + } + return

@@ -73,12 +90,15 @@ const BetaCard = ({ title: titleOverride, featureId }: IProps) => {

{ _t(caption) } - SettingsStore.setValue(featureId, null, SettingLevel.DEVICE, !value)} - kind="primary" - > - { value ? _t("Leave the beta") : _t("Join the beta") } - +
+ { feedbackButton } + SettingsStore.setValue(featureId, null, SettingLevel.DEVICE, !value)} + kind={feedbackButton ? "primary_outline" : "primary"} + > + { value ? _t("Leave the beta") : _t("Join the beta") } + +
{ disclaimer &&
{ disclaimer(value) }
} diff --git a/src/components/views/dialogs/BetaFeedbackDialog.tsx b/src/components/views/dialogs/BetaFeedbackDialog.tsx new file mode 100644 index 0000000000..702adc8fb1 --- /dev/null +++ b/src/components/views/dialogs/BetaFeedbackDialog.tsx @@ -0,0 +1,69 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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, {useState} from "react"; + +import QuestionDialog from './QuestionDialog'; +import { _t } from '../../../languageHandler'; +import Field from "../elements/Field"; +import SdkConfig from "../../../SdkConfig"; +import {IDialogProps} from "./IDialogProps"; +import SettingsStore from "../../../settings/SettingsStore"; +import {submitFeedback} from "../../../rageshake/submit-rageshake"; + +interface IProps extends IDialogProps { + featureId: string; +} + +const BetaFeedbackDialog: React.FC = ({featureId, onFinished}) => { + const info = SettingsStore.getBetaInfo(featureId); + + const [comment, setComment] = useState(""); + + const sendFeedback = async (ok: boolean) => { + if (!ok) return onFinished(false); + + submitFeedback(SdkConfig.get().bug_report_endpoint_url, info.feedbackLabel, comment); + onFinished(true); + }; + + return ( + { _t(info.feedbackSubheading) } + + { + setComment(ev.target.value); + }} + /> + } + button={_t("Send feedback")} + buttonDisabled={!comment} + onFinished={sendFeedback} + />); +}; + +export default BetaFeedbackDialog; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f9d8d8afc9..0f1e8c8fbd 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -787,11 +787,12 @@ "Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.": "Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.", "Spaces": "Spaces", "Spaces are a new way to group rooms and people.": "Spaces are a new way to group rooms and people.", - "%(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.": "%(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.", + "If you leave, %(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.": "If you leave, %(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.", "Beta available for web, desktop and Android. Thank you for trying the beta.": "Beta available for web, desktop and Android. Thank you for trying the beta.", "%(brand)s will reload with Spaces enabled. Communities and custom tags will be hidden.": "%(brand)s will reload with Spaces enabled. Communities and custom tags will be hidden.", "You can leave the beta any time from settings or tapping on a beta badge, like the one above.": "You can leave the beta any time from settings or tapping on a beta badge, like the one above.", "Beta available for web, desktop and Android. Some features may be unavailable on your homeserver.": "Beta available for web, desktop and Android. Some features may be unavailable on your homeserver.", + "You’re using an early version of Spaces, your feedback will really help inform the next versions.": "You’re using an early version of Spaces, your feedback will really help inform the next versions.", "Show options to enable 'Do not disturb' mode": "Show options to enable 'Do not disturb' mode", "Send and receive voice messages (in development)": "Send and receive voice messages (in development)", "Render LaTeX maths in messages": "Render LaTeX maths in messages", @@ -2063,6 +2064,10 @@ "Invite anyway and never warn me again": "Invite anyway and never warn me again", "Invite anyway": "Invite anyway", "Close dialog": "Close dialog", + "Beta feedback": "Beta feedback", + "Add comment": "Add comment", + "Comment": "Comment", + "Send feedback": "Send feedback", "Please tell us what went wrong or, better, create a GitHub issue that describes the problem.": "Please tell us what went wrong or, better, create a GitHub issue that describes the problem.", "Preparing to send logs": "Preparing to send logs", "Logs sent": "Logs sent", @@ -2189,14 +2194,11 @@ "Rate %(brand)s": "Rate %(brand)s", "Tell us below how you feel about %(brand)s so far.": "Tell us below how you feel about %(brand)s so far.", "Please go into as much detail as you like, so we can track down the problem.": "Please go into as much detail as you like, so we can track down the problem.", - "Add comment": "Add comment", - "Comment": "Comment", "There are two ways you can provide feedback and help us improve %(brand)s.": "There are two ways you can provide feedback and help us improve %(brand)s.", "PRO TIP: If you start a bug, please submit debug logs to help us track down the problem.": "PRO TIP: If you start a bug, please submit debug logs to help us track down the problem.", "Feedback": "Feedback", "Report a bug": "Report a bug", "Please view existing bugs on Github first. No match? Start a new one.": "Please view existing bugs on Github first. No match? Start a new one.", - "Send feedback": "Send feedback", "Confirm abort of host creation": "Confirm abort of host creation", "Are you sure you wish to abort creation of the host? The process cannot be continued.": "Are you sure you wish to abort creation of the host? The process cannot be continued.", "Abort": "Abort", diff --git a/src/rageshake/submit-rageshake.ts b/src/rageshake/submit-rageshake.ts index 29856b1a86..9c4fe1f95b 100644 --- a/src/rageshake/submit-rageshake.ts +++ b/src/rageshake/submit-rageshake.ts @@ -28,6 +28,7 @@ import * as rageshake from './rageshake'; // polyfill textencoder if necessary import * as TextEncodingUtf8 from 'text-encoding-utf-8'; import SettingsStore from "../settings/SettingsStore"; +import SdkConfig from "../SdkConfig"; let TextEncoder = window.TextEncoder; if (!TextEncoder) { TextEncoder = TextEncodingUtf8.TextEncoder; @@ -268,6 +269,23 @@ function uint8ToString(buf: Buffer) { return out; } +export async function submitFeedback(endpoint: string, label: string, comment: string) { + let version = "UNKNOWN"; + try { + version = await PlatformPeg.get().getAppVersion(); + } catch (err) {} // PlatformPeg already logs this. + + const body = new FormData(); + body.append("label", label); + body.append("text", comment); + + body.append("app", "element-web"); + body.append("version", version); + body.append("platform", PlatformPeg.get().getHumanReadableName()); + + await _submitReport(SdkConfig.get().bug_report_endpoint_url, body, () => {}); +} + function _submitReport(endpoint: string, body: FormData, progressCallback: (string) => void) { return new Promise((resolve, reject) => { const req = new XMLHttpRequest(); diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 47e63376bb..4420ed1c5d 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -125,6 +125,8 @@ export interface ISetting { caption: string; // _td disclaimer?: (enabled: boolean) => ReactNode; image: string; // require(...) + feedbackSubheading?: string; + feedbackLabel?: string; }; } @@ -142,7 +144,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { disclaimer: (enabled) => { if (enabled) { return <> -

{ _t("%(brand)s will reload with Spaces disabled. " + +

{ _t("If you leave, %(brand)s will reload with Spaces disabled. " + "Communities and custom tags will be visible again.", { brand: SdkConfig.get().brand, }) }

@@ -162,6 +164,9 @@ export const SETTINGS: {[setting: string]: ISetting} = { ; }, image: require("../../res/img/betas/spaces.png"), + feedbackSubheading: _td("You’re using an early version of Spaces, " + + "your feedback will really help inform the next versions."), + feedbackLabel: "spaces-feedback", }, }, "feature_dnd": {