diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index 809f5e4..fe52c85 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 fa83f5d..46dfe5a 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 0000000..46727f5 --- /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 4e91a6d..e2e8fda 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 88296db..20f6e00 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 f5025b8..0464266 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 b08f22a..a6278c2 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 826468b..6339e42 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 60b6993..2420cc4 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 bfc17d7..afecd6a 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 73b2a00..648a0a1 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 8867ed4..7af31d6 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 17a0fde..48b1cbf 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 9d50987..37484b4 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 7eb3915..9b4951b 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 244d1d1..90a1cb5 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 6729a30..756ac74 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);