diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index 809f5e4a..fe52c85f 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -16,13 +16,12 @@ "<0>Why not finish by setting up a password to keep your account?<1>You'll be able to keep your name and set an avatar for use on future calls": "<0>Why not finish by setting up a password to keep your account?<1>You'll be able to keep your name and set an avatar for use on future calls", "Accept camera/microphone permissions to join the call.": "Accept camera/microphone permissions to join the call.", "Accept microphone permissions to join the call.": "Accept microphone permissions to join the call.", - "Advanced": "Advanced", - "Allow analytics": "Allow analytics", "Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.", "Audio": "Audio", "Avatar": "Avatar", "By clicking \"Go\", you agree to our <2>Terms and conditions": "By clicking \"Go\", you agree to our <2>Terms and conditions", "By clicking \"Join call now\", you agree to our <2>Terms and conditions": "By clicking \"Join call now\", you agree to our <2>Terms and conditions", + "By ticking this box you consent to the collection of anonymous data, which we use to improve your experience. You can find more information about which data we track in our ": "By ticking this box you consent to the collection of anonymous data, which we use to improve your experience. You can find more information about which data we track in our ", "Call link copied": "Call link copied", "Call type menu": "Call type menu", "Camera": "Camera", @@ -41,10 +40,12 @@ "Description (optional)": "Description (optional)", "Details": "Details", "Developer": "Developer", + "Developer Settings": "Developer Settings", "Display name": "Display name", "Download debug logs": "Download debug logs", "Element Call Home": "Element Call Home", "Exit full screen": "Exit full screen", + "Expose developer settings in the settings window.": "Expose developer settings in the settings window.", "Fetching group call timed out.": "Fetching group call timed out.", "Freedom": "Freedom", "Full screen": "Full screen", @@ -84,6 +85,7 @@ "Press and hold spacebar to talk over {{name}}": "Press and hold spacebar to talk over {{name}}", "Press and hold to talk": "Press and hold to talk", "Press and hold to talk over {{name}}": "Press and hold to talk over {{name}}", + "Privacy Policy": "Privacy Policy", "Profile": "Profile", "Recaptcha dismissed": "Recaptcha dismissed", "Recaptcha not loaded": "Recaptcha not loaded", @@ -120,7 +122,6 @@ "This feature is only supported on Firefox.": "This feature is only supported on Firefox.", "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>Terms and conditions": "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>Terms and conditions", "This will make a speaker's audio seem as if it is coming from where their tile is positioned on screen. (Experimental feature: this may impact the stability of audio.)": "This will make a speaker's audio seem as if it is coming from where their tile is positioned on screen. (Experimental feature: this may impact the stability of audio.)", - "This will send anonymised data (such as the duration of a call and the number of participants) to the Element Call team to help us optimise the application based on how it is used.": "This will send anonymised data (such as the duration of a call and the number of participants) to the Element Call team to help us optimise the application based on how it is used.", "Turn off camera": "Turn off camera", "Turn on camera": "Turn on camera", "Unmute microphone": "Unmute microphone", diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx index fa83f5d9..46dfe5a5 100644 --- a/src/ClientContext.tsx +++ b/src/ClientContext.tsx @@ -36,7 +36,10 @@ import { fallbackICEServerAllowed, } from "./matrix-utils"; import { widget } from "./widget"; -import { PosthogAnalytics, RegistrationType } from "./PosthogAnalytics"; +import { + PosthogAnalytics, + RegistrationType, +} from "./analytics/PosthogAnalytics"; import { translatedError } from "./TranslatedError"; import { useEventTarget } from "./useEvents"; import { Config } from "./config/Config"; diff --git a/src/analytics/AnalyticsOptInDescription.tsx b/src/analytics/AnalyticsOptInDescription.tsx new file mode 100644 index 00000000..46727f5f --- /dev/null +++ b/src/analytics/AnalyticsOptInDescription.tsx @@ -0,0 +1,20 @@ +import { t } from "i18next"; +import React from "react"; + +import { Link } from "../typography/Typography"; + +export const optInDescription: () => JSX.Element = () => { + return ( + <> + <> + {t( + "By ticking this box you consent to the collection of anonymous data, which we use to improve your experience. You can find more information about which data we track in our " + )} + + + <>{t("Privacy Policy")} + + . + + ); +}; diff --git a/src/PosthogAnalytics.ts b/src/analytics/PosthogAnalytics.ts similarity index 98% rename from src/PosthogAnalytics.ts rename to src/analytics/PosthogAnalytics.ts index 4e91a6d6..e2e8fdae 100644 --- a/src/PosthogAnalytics.ts +++ b/src/analytics/PosthogAnalytics.ts @@ -19,8 +19,8 @@ import { logger } from "matrix-js-sdk/src/logger"; import { MatrixClient } from "matrix-js-sdk"; import { Buffer } from "buffer"; -import { widget } from "./widget"; -import { getSetting, setSetting, settingsBus } from "./settings/useSetting"; +import { widget } from "../widget"; +import { getSetting, setSetting, settingsBus } from "../settings/useSetting"; import { CallEndedTracker, CallStartedTracker, @@ -30,8 +30,8 @@ import { MuteMicrophoneTracker, UndecryptableToDeviceEventTracker, } from "./PosthogEvents"; -import { Config } from "./config/Config"; -import { getUrlParams } from "./UrlParams"; +import { Config } from "../config/Config"; +import { getUrlParams } from "../UrlParams"; /* Posthog analytics tracking. * diff --git a/src/PosthogEvents.ts b/src/analytics/PosthogEvents.ts similarity index 100% rename from src/PosthogEvents.ts rename to src/analytics/PosthogEvents.ts diff --git a/src/auth/LoginPage.tsx b/src/auth/LoginPage.tsx index 88296db4..20f6e00b 100644 --- a/src/auth/LoginPage.tsx +++ b/src/auth/LoginPage.tsx @@ -25,7 +25,7 @@ import { Button } from "../button"; import styles from "./LoginPage.module.css"; import { useInteractiveLogin } from "./useInteractiveLogin"; import { usePageTitle } from "../usePageTitle"; -import { PosthogAnalytics } from "../PosthogAnalytics"; +import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; import { Config } from "../config/Config"; export const LoginPage: FC = () => { diff --git a/src/auth/RegisterPage.tsx b/src/auth/RegisterPage.tsx index f5025b88..0464266e 100644 --- a/src/auth/RegisterPage.tsx +++ b/src/auth/RegisterPage.tsx @@ -38,7 +38,7 @@ import { LoadingView } from "../FullScreenView"; import { useRecaptcha } from "./useRecaptcha"; import { Caption, Link } from "../typography/Typography"; import { usePageTitle } from "../usePageTitle"; -import { PosthogAnalytics } from "../PosthogAnalytics"; +import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; import { Config } from "../config/Config"; export const RegisterPage: FC = () => { diff --git a/src/home/RegisteredView.tsx b/src/home/RegisteredView.tsx index b08f22a6..a6278c2e 100644 --- a/src/home/RegisteredView.tsx +++ b/src/home/RegisteredView.tsx @@ -42,6 +42,8 @@ import { JoinExistingCallModal } from "./JoinExistingCallModal"; import { Title } from "../typography/Typography"; import { Form } from "../form/Form"; import { CallType, CallTypeDropdown } from "./CallTypeDropdown"; +import { useOptInAnalytics } from "../settings/useSetting"; +import { optInDescription } from "../analytics/AnalyticsOptInDescription"; interface Props { client: MatrixClient; @@ -52,6 +54,7 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) { const [callType, setCallType] = useState(CallType.Video); const [loading, setLoading] = useState(false); const [error, setError] = useState(); + const [optInAnalytics, setOptInAnalytics] = useOptInAnalytics(); const history = useHistory(); const { t } = useTranslation(); const { modalState, modalProps } = useModalTriggerState(); @@ -141,6 +144,15 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) { {loading ? t("Loading…") : t("Go")} + ) => + setOptInAnalytics(event.target.checked) + } + /> {error && ( diff --git a/src/home/UnauthenticatedView.tsx b/src/home/UnauthenticatedView.tsx index 826468b2..6339e42d 100644 --- a/src/home/UnauthenticatedView.tsx +++ b/src/home/UnauthenticatedView.tsx @@ -39,12 +39,15 @@ import { CallType, CallTypeDropdown } from "./CallTypeDropdown"; import styles from "./UnauthenticatedView.module.css"; import commonStyles from "./common.module.css"; import { generateRandomName } from "../auth/generateRandomName"; +import { useOptInAnalytics } from "../settings/useSetting"; +import { optInDescription } from "../analytics/AnalyticsOptInDescription"; export const UnauthenticatedView: FC = () => { const { setClient } = useClient(); const [callType, setCallType] = useState(CallType.Video); const [loading, setLoading] = useState(false); const [error, setError] = useState(); + const [optInAnalytics, setOptInAnalytics] = useOptInAnalytics(); const [privacyPolicyUrl, recaptchaKey, register] = useInteractiveRegistration(); const { execute, reset, recaptchaId } = useRecaptcha(recaptchaKey); @@ -152,6 +155,15 @@ export const UnauthenticatedView: FC = () => { autoComplete="off" /> + ) => + setOptInAnalytics(event.target.checked) + } + /> By clicking "Go", you agree to our{" "} diff --git a/src/input/Input.module.css b/src/input/Input.module.css index 60b69936..2420cc43 100644 --- a/src/input/Input.module.css +++ b/src/input/Input.module.css @@ -209,3 +209,7 @@ limitations under the License. margin-left: 26px; width: 100%; /* Ensure that it breaks onto the next row */ } + +.description.noLabel { + margin-top: -20px; /* Ensures that there is no weired spacing if the checkbox doesn't have a label */ +} diff --git a/src/input/Input.tsx b/src/input/Input.tsx index bfc17d74..afecd6ae 100644 --- a/src/input/Input.tsx +++ b/src/input/Input.tsx @@ -55,14 +55,14 @@ export function Field({ children, className }: FieldProps): JSX.Element { } interface InputFieldProps { - label: string; + label?: string; type: string; prefix?: string; suffix?: string; id?: string; checked?: boolean; className?: string; - description?: string; + description?: string | ReactNode; disabled?: boolean; required?: boolean; // this is a hack. Those variables should be part of `HTMLAttributes | HTMLAttributes` @@ -140,7 +140,14 @@ export const InputField = forwardRef< {suffix && {suffix}} {description && ( -

+

{description}

)} diff --git a/src/room/GroupCallInspector.tsx b/src/room/GroupCallInspector.tsx index 73b2a000..648a0a1f 100644 --- a/src/room/GroupCallInspector.tsx +++ b/src/room/GroupCallInspector.tsx @@ -35,7 +35,7 @@ import { CallEvent } from "matrix-js-sdk/src/webrtc/call"; import styles from "./GroupCallInspector.module.css"; import { SelectInput } from "../input/SelectInput"; -import { PosthogAnalytics } from "../PosthogAnalytics"; +import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; interface InspectorContextState { eventsByUserId?: { [userId: string]: SequenceDiagramMatrixEvent[] }; diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 8867ed4d..7af31d63 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -32,7 +32,7 @@ import { CallEndedView } from "./CallEndedView"; import { useRoomAvatar } from "./useRoomAvatar"; import { useSentryGroupCallHandler } from "./useSentryGroupCallHandler"; import { useLocationNavigation } from "../useLocationNavigation"; -import { PosthogAnalytics } from "../PosthogAnalytics"; +import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; import { useMediaHandler } from "../settings/useMediaHandler"; import { findDeviceByName, getDevices } from "../media-utils"; diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 17a0fde4..48b1cbf1 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -63,7 +63,7 @@ import { import { useModalTriggerState } from "../Modal"; import { useAudioContext } from "../video-grid/useMediaStream"; import { useFullscreen } from "../video-grid/useFullscreen"; -import { PosthogAnalytics } from "../PosthogAnalytics"; +import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; import { widget, ElementWidgetActions } from "../widget"; import { useJoinRule } from "./useJoinRule"; import { useUrlParams } from "../UrlParams"; diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts index 9d509872..37484b43 100644 --- a/src/room/useGroupCall.ts +++ b/src/room/useGroupCall.ts @@ -29,7 +29,7 @@ import { useTranslation } from "react-i18next"; import { IWidgetApiRequest } from "matrix-widget-api"; import { usePageUnload } from "./usePageUnload"; -import { PosthogAnalytics } from "../PosthogAnalytics"; +import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; import { TranslatedError, translatedError } from "../TranslatedError"; import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget"; diff --git a/src/settings/SettingsModal.module.css b/src/settings/SettingsModal.module.css index 7eb39159..9b4951b4 100644 --- a/src/settings/SettingsModal.module.css +++ b/src/settings/SettingsModal.module.css @@ -26,3 +26,12 @@ limitations under the License. .fieldRowText { margin-bottom: 0; } + +/* +This style guarantees a fixed width of the tab bar in the settings window. +The "Developer" item in the tab bar can be toggled. +Without a defined width activating the developer tab makes the tab container jump to the right. +*/ +.tabLabel { + width: 80px; +} diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 244d1d15..90a1cb5f 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -34,11 +34,13 @@ import { useOptInAnalytics, canEnableSpatialAudio, useNewGrid, + useDeveloperSettingsTab, } from "./useSetting"; import { FieldRow, InputField } from "../input/Input"; import { Button } from "../button"; import { useDownloadDebugLog } from "./submit-rageshake"; import { Body } from "../typography/Typography"; +import { optInDescription } from "../analytics/AnalyticsOptInDescription"; interface Props { isOpen: boolean; @@ -62,6 +64,8 @@ export const SettingsModal = (props: Props) => { const [spatialAudio, setSpatialAudio] = useSpatialAudio(); const [showInspector, setShowInspector] = useShowInspector(); const [optInAnalytics, setOptInAnalytics] = useOptInAnalytics(); + const [developerSettingsTab, setDeveloperSettingsTab] = + useDeveloperSettingsTab(); const [keyboardShortcuts, setKeyboardShortcuts] = useKeyboardShortcuts(); const [newGrid, setNewGrid] = useNewGrid(); @@ -80,7 +84,7 @@ export const SettingsModal = (props: Props) => { title={ <> - {t("Audio")} + {t("Audio")} } > @@ -158,24 +162,11 @@ export const SettingsModal = (props: Props) => { title={ <> - {t("Advanced")} + {t("More")} } > - - ) => - setOptInAnalytics(event.target.checked) - } - /> - +

Keyboard

{ } /> - - - - {t("Developer")} - - } - > - - - {t("Version: {{version}}", { - version: import.meta.env.VITE_APP_VERSION || "dev", - })} - - +

Analytics

) => - setShowInspector(e.target.checked) + checked={optInAnalytics} + description={optInDescription()} + onChange={(event: React.ChangeEvent) => + setOptInAnalytics(event.target.checked) } /> ) => - setNewGrid(e.target.checked) + checked={developerSettingsTab} + label={t("Developer Settings")} + description={t( + "Expose developer settings in the settings window." + )} + onChange={(event: React.ChangeEvent) => + setDeveloperSettingsTab(event.target.checked) } /> - - -
+ {developerSettingsTab && ( + + + {t("Developer")} + + } + > + + + {t("Version: {{version}}", { + version: import.meta.env.VITE_APP_VERSION || "dev", + })} + + + + ) => + setShowInspector(e.target.checked) + } + /> + + + ) => + setNewGrid(e.target.checked) + } + /> + + + + + + )} ); diff --git a/src/settings/useSetting.ts b/src/settings/useSetting.ts index 6729a301..756ac74b 100644 --- a/src/settings/useSetting.ts +++ b/src/settings/useSetting.ts @@ -91,3 +91,5 @@ export const useOptInAnalytics = () => useSetting("opt-in-analytics", false); export const useKeyboardShortcuts = () => useSetting("keyboard-shortcuts", true); export const useNewGrid = () => useSetting("new-grid", false); +export const useDeveloperSettingsTab = () => + useSetting("developer-settings-tab", false);