mirror of
https://github.com/vector-im/element-call.git
synced 2024-11-15 00:04:59 +08:00
Add a prompt to launch Element X on mobile
This shows a bottom sheet on mobile asking the user whether they want to open the call in Element X, as soon as the page is loaded.
This commit is contained in:
parent
f609ec3f4c
commit
0f97d655d2
@ -27,6 +27,7 @@
|
|||||||
"Close": "Close",
|
"Close": "Close",
|
||||||
"Confirm password": "Confirm password",
|
"Confirm password": "Confirm password",
|
||||||
"Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
|
"Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
|
||||||
|
"Continue in browser": "Continue in browser",
|
||||||
"Copied!": "Copied!",
|
"Copied!": "Copied!",
|
||||||
"Copy": "Copy",
|
"Copy": "Copy",
|
||||||
"Copy and share this call link": "Copy and share this call link",
|
"Copy and share this call link": "Copy and share this call link",
|
||||||
@ -70,9 +71,11 @@
|
|||||||
"Not encrypted": "Not encrypted",
|
"Not encrypted": "Not encrypted",
|
||||||
"Not now, return to home screen": "Not now, return to home screen",
|
"Not now, return to home screen": "Not now, return to home screen",
|
||||||
"Not registered yet? <2>Create an account</2>": "Not registered yet? <2>Create an account</2>",
|
"Not registered yet? <2>Create an account</2>": "Not registered yet? <2>Create an account</2>",
|
||||||
|
"Open in the app": "Open in the app",
|
||||||
"Password": "Password",
|
"Password": "Password",
|
||||||
"Passwords must match": "Passwords must match",
|
"Passwords must match": "Passwords must match",
|
||||||
"Profile": "Profile",
|
"Profile": "Profile",
|
||||||
|
"Ready to join?": "Ready to join?",
|
||||||
"Recaptcha dismissed": "Recaptcha dismissed",
|
"Recaptcha dismissed": "Recaptcha dismissed",
|
||||||
"Recaptcha not loaded": "Recaptcha not loaded",
|
"Recaptcha not loaded": "Recaptcha not loaded",
|
||||||
"Reconnect": "Reconnect",
|
"Reconnect": "Reconnect",
|
||||||
@ -82,6 +85,7 @@
|
|||||||
"Retry sending logs": "Retry sending logs",
|
"Retry sending logs": "Retry sending logs",
|
||||||
"Return to home screen": "Return to home screen",
|
"Return to home screen": "Return to home screen",
|
||||||
"Select an option": "Select an option",
|
"Select an option": "Select an option",
|
||||||
|
"Select app": "Select app",
|
||||||
"Send debug logs": "Send debug logs",
|
"Send debug logs": "Send debug logs",
|
||||||
"Sending debug logs…": "Sending debug logs…",
|
"Sending debug logs…": "Sending debug logs…",
|
||||||
"Sending…": "Sending…",
|
"Sending…": "Sending…",
|
||||||
|
@ -94,6 +94,22 @@ interface UrlParams {
|
|||||||
password: string | null;
|
password: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function editFragmentQuery(
|
||||||
|
hash: string,
|
||||||
|
edit: (params: URLSearchParams) => URLSearchParams
|
||||||
|
): string {
|
||||||
|
const fragmentQueryStart = hash.indexOf("?");
|
||||||
|
const fragmentParams = edit(
|
||||||
|
new URLSearchParams(
|
||||||
|
fragmentQueryStart === -1 ? "" : hash.substring(fragmentQueryStart)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return `${hash.substring(
|
||||||
|
0,
|
||||||
|
fragmentQueryStart
|
||||||
|
)}?${fragmentParams.toString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the app parameters for the current URL.
|
* Gets the app parameters for the current URL.
|
||||||
* @param ignoreRoomAlias If true, does not try to parse a room alias from the URL
|
* @param ignoreRoomAlias If true, does not try to parse a room alias from the URL
|
||||||
|
33
src/room/AppSelectionModal.module.css
Normal file
33
src/room/AppSelectionModal.module.css
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.modal p {
|
||||||
|
text-align: center;
|
||||||
|
margin-block-end: var(--cpd-space-8x);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal button,
|
||||||
|
.modal a {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal button {
|
||||||
|
margin-block-end: var(--cpd-space-6x);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal a {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
77
src/room/AppSelectionModal.tsx
Normal file
77
src/room/AppSelectionModal.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 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 { FC, MouseEvent, useCallback, useMemo, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Button, Text } from "@vector-im/compound-web";
|
||||||
|
import { ReactComponent as PopOutIcon } from "@vector-im/compound-design-tokens/icons/pop-out.svg";
|
||||||
|
|
||||||
|
import { Modal } from "../NewModal";
|
||||||
|
import { useRoomSharedKey } from "../e2ee/sharedKeyManagement";
|
||||||
|
import { getRoomUrl } from "../matrix-utils";
|
||||||
|
import styles from "./AppSelectionModal.module.css";
|
||||||
|
import { editFragmentQuery } from "../UrlParams";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
roomId: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AppSelectionModal: FC<Props> = ({ roomId }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(true);
|
||||||
|
const onBrowserClick = useCallback(
|
||||||
|
(e: MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
setOpen(false);
|
||||||
|
},
|
||||||
|
[setOpen]
|
||||||
|
);
|
||||||
|
|
||||||
|
const roomSharedKey = useRoomSharedKey(roomId ?? "");
|
||||||
|
const appUrl = useMemo(() => {
|
||||||
|
// If the room ID is not known, fall back to the URL of the current page
|
||||||
|
const url = new URL(
|
||||||
|
roomId === null
|
||||||
|
? window.location.href
|
||||||
|
: getRoomUrl(roomId, roomSharedKey ?? undefined)
|
||||||
|
);
|
||||||
|
// Edit the URL so that it opens in embedded mode
|
||||||
|
url.hash = editFragmentQuery(url.hash, (params) => {
|
||||||
|
params.set("isEmbedded", "");
|
||||||
|
return params;
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = new URL("element://call");
|
||||||
|
result.searchParams.set("url", url.toString());
|
||||||
|
return result.toString();
|
||||||
|
}, [roomId, roomSharedKey]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal className={styles.modal} title={t("Select app")} open={open}>
|
||||||
|
<Text size="md" weight="semibold">
|
||||||
|
{t("Ready to join?")}
|
||||||
|
</Text>
|
||||||
|
<Button kind="secondary" onClick={onBrowserClick}>
|
||||||
|
{t("Continue in browser")}
|
||||||
|
</Button>
|
||||||
|
<Button as="a" href={appUrl} Icon={PopOutIcon}>
|
||||||
|
{t("Open in the app")}
|
||||||
|
</Button>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FC, useEffect, useState, useCallback } from "react";
|
import { FC, useEffect, useState, useCallback, ReactNode } from "react";
|
||||||
import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
|
import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
|
||||||
|
|
||||||
import { useClientLegacy } from "../ClientContext";
|
import { useClientLegacy } from "../ClientContext";
|
||||||
@ -26,6 +26,8 @@ import { useUrlParams } from "../UrlParams";
|
|||||||
import { useRegisterPasswordlessUser } from "../auth/useRegisterPasswordlessUser";
|
import { useRegisterPasswordlessUser } from "../auth/useRegisterPasswordlessUser";
|
||||||
import { useOptInAnalytics } from "../settings/useSetting";
|
import { useOptInAnalytics } from "../settings/useSetting";
|
||||||
import { HomePage } from "../home/HomePage";
|
import { HomePage } from "../home/HomePage";
|
||||||
|
import { platform } from "../Platform";
|
||||||
|
import { AppSelectionModal } from "./AppSelectionModal";
|
||||||
|
|
||||||
export const RoomPage: FC = () => {
|
export const RoomPage: FC = () => {
|
||||||
const {
|
const {
|
||||||
@ -86,30 +88,37 @@ export const RoomPage: FC = () => {
|
|||||||
[client, passwordlessUser, isEmbedded, preload, hideHeader]
|
[client, passwordlessUser, isEmbedded, preload, hideHeader]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let content: ReactNode;
|
||||||
if (loading || isRegistering) {
|
if (loading || isRegistering) {
|
||||||
return <LoadingView />;
|
content = <LoadingView />;
|
||||||
}
|
} else if (error) {
|
||||||
|
content = <ErrorView error={error} />;
|
||||||
if (error) {
|
} else if (!client) {
|
||||||
return <ErrorView error={error} />;
|
content = <RoomAuthView />;
|
||||||
}
|
} else if (!roomIdOrAlias) {
|
||||||
|
// TODO: This doesn't belong here, the app routes need to be reworked
|
||||||
if (!client) {
|
content = <HomePage />;
|
||||||
return <RoomAuthView />;
|
} else {
|
||||||
}
|
content = (
|
||||||
|
<GroupCallLoader
|
||||||
if (!roomIdOrAlias) {
|
client={client}
|
||||||
return <HomePage />;
|
roomIdOrAlias={roomIdOrAlias}
|
||||||
|
viaServers={viaServers}
|
||||||
|
createPtt={isPtt}
|
||||||
|
>
|
||||||
|
{groupCallView}
|
||||||
|
</GroupCallLoader>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GroupCallLoader
|
<>
|
||||||
client={client}
|
{content}
|
||||||
roomIdOrAlias={roomIdOrAlias}
|
{/* On mobile, show a prompt to launch the mobile app. If in embedded mode,
|
||||||
viaServers={viaServers}
|
that means we *are* in the mobile app and should show no further prompt. */}
|
||||||
createPtt={isPtt}
|
{(platform === "android" || platform === "ios") && !isEmbedded && (
|
||||||
>
|
<AppSelectionModal roomId={roomId} />
|
||||||
{groupCallView}
|
)}
|
||||||
</GroupCallLoader>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user