Merge branch 'dev' into dm-calls
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": ["config:recommended", ":dependencyDashboardApproval"],
|
"extends": [
|
||||||
|
"config:recommended",
|
||||||
|
":dependencyDashboardApproval",
|
||||||
|
":semanticCommits"
|
||||||
|
],
|
||||||
"labels": ["Dependencies"],
|
"labels": ["Dependencies"],
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Login to the Github Container registry #Do not update this action from a outside PR
|
- name: Login to the Github Container registry #Do not update this action from a outside PR
|
||||||
if: github.event.pull_request.head.repo.fork == false
|
if: github.event.pull_request.head.repo.fork == false
|
||||||
@@ -38,6 +39,7 @@ jobs:
|
|||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker, GHCR
|
- name: Extract metadata (tags, labels) for Docker, GHCR
|
||||||
id: meta
|
id: meta
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
name: Check PR title
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- edited
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
Generated
+26
-47
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "4.10.5",
|
"version": "4.11.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "4.10.5",
|
"version": "4.11.1",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@atlaskit/pragmatic-drag-and-drop": "1.1.6",
|
"@atlaskit/pragmatic-drag-and-drop": "1.1.6",
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"emojibase-data": "15.3.2",
|
"emojibase-data": "15.3.2",
|
||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
"focus-trap-react": "10.0.2",
|
"focus-trap-react": "10.0.2",
|
||||||
"folds": "2.6.1",
|
"folds": "2.6.2",
|
||||||
"html-dom-parser": "4.0.0",
|
"html-dom-parser": "4.0.0",
|
||||||
"html-react-parser": "4.2.0",
|
"html-react-parser": "4.2.0",
|
||||||
"i18next": "23.12.2",
|
"i18next": "23.12.2",
|
||||||
@@ -59,10 +59,10 @@
|
|||||||
"react-range": "1.8.14",
|
"react-range": "1.8.14",
|
||||||
"react-router-dom": "6.30.3",
|
"react-router-dom": "6.30.3",
|
||||||
"sanitize-html": "2.12.1",
|
"sanitize-html": "2.12.1",
|
||||||
"slate": "0.112.0",
|
"slate": "0.123.0",
|
||||||
"slate-dom": "0.112.2",
|
"slate-dom": "0.123.0",
|
||||||
"slate-history": "0.110.3",
|
"slate-history": "0.113.1",
|
||||||
"slate-react": "0.112.1",
|
"slate-react": "0.123.0",
|
||||||
"ua-parser-js": "1.0.35"
|
"ua-parser-js": "1.0.35"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -7166,9 +7166,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/folds": {
|
"node_modules/folds": {
|
||||||
"version": "2.6.1",
|
"version": "2.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/folds/-/folds-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/folds/-/folds-2.6.2.tgz",
|
||||||
"integrity": "sha512-0L1ZSqwjFSg2fesa//C4DgP47Vp/KqDuzjAaOEYN21AvoptyVI+6OEXWrtIdE8DPQCZYr0bV+tqbrLyA6uAhaw==",
|
"integrity": "sha512-1HemxxSnBm8/U5kq1pDQrFkpltWgQN90DmWCZWkZb7D2pe8BhOJSwIRLjk9WxHcw6nn69oz2XNYIXtSw0LvX1w==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@vanilla-extract/css": "1.9.2",
|
"@vanilla-extract/css": "1.9.2",
|
||||||
@@ -10291,20 +10291,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/slate": {
|
"node_modules/slate": {
|
||||||
"version": "0.112.0",
|
"version": "0.123.0",
|
||||||
"resolved": "https://registry.npmjs.org/slate/-/slate-0.112.0.tgz",
|
"resolved": "https://registry.npmjs.org/slate/-/slate-0.123.0.tgz",
|
||||||
"integrity": "sha512-PRnfFgDA3tSop4OH47zu4M1R4Uuhm/AmASu29Qp7sGghVFb713kPBKEnSf1op7Lx/nCHkRlCa3ThfHtCBy+5Yw==",
|
"integrity": "sha512-Oon3HR/QzJQBjuOUJT1jGGlp8Ff7t3Bkr/rJ2lDqxNT4H+cBnXpEVQ/si6hn1ZCHhD2xY/2N91PQoH/rD7kxTg==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"dependencies": {
|
|
||||||
"immer": "^10.0.3",
|
|
||||||
"is-plain-object": "^5.0.0",
|
|
||||||
"tiny-warning": "^1.0.3"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"node_modules/slate-dom": {
|
"node_modules/slate-dom": {
|
||||||
"version": "0.112.2",
|
"version": "0.123.0",
|
||||||
"resolved": "https://registry.npmjs.org/slate-dom/-/slate-dom-0.112.2.tgz",
|
"resolved": "https://registry.npmjs.org/slate-dom/-/slate-dom-0.123.0.tgz",
|
||||||
"integrity": "sha512-cozITMlpcBxrov854reM6+TooiHiqpfM/nZPrnjpN1wSiDsAQmYbWUyftC+jlwcpFj80vywfDHzlG6hXIc5h6A==",
|
"integrity": "sha512-OUinp4tvSrAlt64JL9y20Xin08jgnnj1gJmIuPdGvU5MELKXRNZh17a7EKKNOS6OZPAE8Dk9NI1MAIS/Qz0YBw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@juggle/resize-observer": "^3.4.0",
|
"@juggle/resize-observer": "^3.4.0",
|
||||||
@@ -10316,13 +10311,13 @@
|
|||||||
"tiny-invariant": "1.3.1"
|
"tiny-invariant": "1.3.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"slate": ">=0.99.0"
|
"slate": ">=0.121.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/slate-history": {
|
"node_modules/slate-history": {
|
||||||
"version": "0.110.3",
|
"version": "0.113.1",
|
||||||
"resolved": "https://registry.npmjs.org/slate-history/-/slate-history-0.110.3.tgz",
|
"resolved": "https://registry.npmjs.org/slate-history/-/slate-history-0.113.1.tgz",
|
||||||
"integrity": "sha512-sgdff4Usdflmw5ZUbhDkxFwCBQ2qlDKMMkF93w66KdV48vHOgN2BmLrf+2H8SdX8PYIpP/cTB0w8qWC2GwhDVA==",
|
"integrity": "sha512-J9NSJ+UG2GxoW0lw5mloaKcN0JI0x2IA5M5FxyGiInpn+QEutxT1WK7S/JneZCMFJBoHs1uu7S7e6pxQjubHmQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-plain-object": "^5.0.0"
|
"is-plain-object": "^5.0.0"
|
||||||
@@ -10332,15 +10327,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/slate-react": {
|
"node_modules/slate-react": {
|
||||||
"version": "0.112.1",
|
"version": "0.123.0",
|
||||||
"resolved": "https://registry.npmjs.org/slate-react/-/slate-react-0.112.1.tgz",
|
"resolved": "https://registry.npmjs.org/slate-react/-/slate-react-0.123.0.tgz",
|
||||||
"integrity": "sha512-V9b+waxPweXqAkSQmKQ1afG4Me6nVQACPpxQtHPIX02N7MXa5f5WilYv+bKt7vKKw+IZC2F0Gjzhv5BekVgP/A==",
|
"integrity": "sha512-nQwXL1FEacrY9ZFmatRhoBnsySNUX2x6qB77V3oNHd7wWxBJWuzz4GMrBXcVoRE8Gac7Angf8xaNGzb6zcPlHg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@juggle/resize-observer": "^3.4.0",
|
"@juggle/resize-observer": "^3.4.0",
|
||||||
"direction": "^1.0.4",
|
"direction": "^1.0.4",
|
||||||
"is-hotkey": "^0.2.0",
|
"is-hotkey": "^0.2.0",
|
||||||
"is-plain-object": "^5.0.0",
|
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"scroll-into-view-if-needed": "^3.1.0",
|
"scroll-into-view-if-needed": "^3.1.0",
|
||||||
"tiny-invariant": "1.3.1"
|
"tiny-invariant": "1.3.1"
|
||||||
@@ -10348,18 +10342,8 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=18.2.0",
|
"react": ">=18.2.0",
|
||||||
"react-dom": ">=18.2.0",
|
"react-dom": ">=18.2.0",
|
||||||
"slate": ">=0.99.0",
|
"slate": ">=0.121.0",
|
||||||
"slate-dom": ">=0.110.2"
|
"slate-dom": ">=0.119.1"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/slate/node_modules/immer": {
|
|
||||||
"version": "10.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
|
|
||||||
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/immer"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/smob": {
|
"node_modules/smob": {
|
||||||
@@ -10729,11 +10713,6 @@
|
|||||||
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==",
|
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/tiny-warning": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
|
|
||||||
},
|
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.10",
|
"version": "0.2.10",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz",
|
||||||
|
|||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "4.10.5",
|
"version": "4.11.1",
|
||||||
"description": "Yet another matrix client",
|
"description": "Yet another matrix client",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
"emojibase-data": "15.3.2",
|
"emojibase-data": "15.3.2",
|
||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
"focus-trap-react": "10.0.2",
|
"focus-trap-react": "10.0.2",
|
||||||
"folds": "2.6.1",
|
"folds": "2.6.2",
|
||||||
"html-dom-parser": "4.0.0",
|
"html-dom-parser": "4.0.0",
|
||||||
"html-react-parser": "4.2.0",
|
"html-react-parser": "4.2.0",
|
||||||
"i18next": "23.12.2",
|
"i18next": "23.12.2",
|
||||||
@@ -71,10 +71,10 @@
|
|||||||
"react-range": "1.8.14",
|
"react-range": "1.8.14",
|
||||||
"react-router-dom": "6.30.3",
|
"react-router-dom": "6.30.3",
|
||||||
"sanitize-html": "2.12.1",
|
"sanitize-html": "2.12.1",
|
||||||
"slate": "0.112.0",
|
"slate": "0.123.0",
|
||||||
"slate-dom": "0.112.2",
|
"slate-dom": "0.123.0",
|
||||||
"slate-history": "0.110.3",
|
"slate-history": "0.113.1",
|
||||||
"slate-react": "0.112.1",
|
"slate-react": "0.123.0",
|
||||||
"ua-parser-js": "1.0.35"
|
"ua-parser-js": "1.0.35"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -389,6 +389,8 @@ export function MLocation({ content }: MLocationProps) {
|
|||||||
const geoUri = content.geo_uri;
|
const geoUri = content.geo_uri;
|
||||||
if (typeof geoUri !== 'string') return <BrokenContent />;
|
if (typeof geoUri !== 'string') return <BrokenContent />;
|
||||||
const location = parseGeoUri(geoUri);
|
const location = parseGeoUri(geoUri);
|
||||||
|
if (!location) return <BrokenContent />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box direction="Column" alignItems="Start" gap="100">
|
<Box direction="Column" alignItems="Start" gap="100">
|
||||||
<Text size="T400">{geoUri}</Text>
|
<Text size="T400">{geoUri}</Text>
|
||||||
|
|||||||
@@ -20,6 +20,16 @@ export type AutoDiscoveryInfo = Record<string, unknown> & {
|
|||||||
'm.identity_server'?: {
|
'm.identity_server'?: {
|
||||||
base_url: string;
|
base_url: string;
|
||||||
};
|
};
|
||||||
|
'org.matrix.msc2965.authentication'?: {
|
||||||
|
account?: string;
|
||||||
|
issuer?: string;
|
||||||
|
};
|
||||||
|
'org.matrix.msc4143.rtc_foci'?: [
|
||||||
|
{
|
||||||
|
livekit_service_url: string;
|
||||||
|
type: 'livekit';
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const autoDiscovery = async (
|
export const autoDiscovery = async (
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import { Box, Chip, Icon, IconButton, Icons, Spinner, Text, Tooltip, TooltipProvider } from 'folds';
|
import { Box, Chip, Icon, IconButton, Icons, Spinner, Text, Tooltip, TooltipProvider } from 'folds';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
|
import { useSetAtom } from 'jotai';
|
||||||
import { StatusDivider } from './components';
|
import { StatusDivider } from './components';
|
||||||
import { CallEmbed, useCallControlState } from '../../plugins/call';
|
import { CallEmbed, useCallControlState } from '../../plugins/call';
|
||||||
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
||||||
|
import { callEmbedAtom } from '../../state/callEmbed';
|
||||||
|
|
||||||
type MicrophoneButtonProps = {
|
type MicrophoneButtonProps = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
onToggle: () => Promise<unknown>;
|
onToggle: () => Promise<unknown>;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
function MicrophoneButton({ enabled, onToggle }: MicrophoneButtonProps) {
|
function MicrophoneButton({ enabled, onToggle, disabled }: MicrophoneButtonProps) {
|
||||||
return (
|
return (
|
||||||
<TooltipProvider
|
<TooltipProvider
|
||||||
position="Top"
|
position="Top"
|
||||||
@@ -27,6 +30,7 @@ function MicrophoneButton({ enabled, onToggle }: MicrophoneButtonProps) {
|
|||||||
size="300"
|
size="300"
|
||||||
onClick={() => onToggle()}
|
onClick={() => onToggle()}
|
||||||
outlined
|
outlined
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<Icon size="100" src={enabled ? Icons.Mic : Icons.MicMute} filled={!enabled} />
|
<Icon size="100" src={enabled ? Icons.Mic : Icons.MicMute} filled={!enabled} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@@ -38,8 +42,9 @@ function MicrophoneButton({ enabled, onToggle }: MicrophoneButtonProps) {
|
|||||||
type SoundButtonProps = {
|
type SoundButtonProps = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
onToggle: () => void;
|
onToggle: () => void;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
function SoundButton({ enabled, onToggle }: SoundButtonProps) {
|
function SoundButton({ enabled, onToggle, disabled }: SoundButtonProps) {
|
||||||
return (
|
return (
|
||||||
<TooltipProvider
|
<TooltipProvider
|
||||||
position="Top"
|
position="Top"
|
||||||
@@ -58,6 +63,7 @@ function SoundButton({ enabled, onToggle }: SoundButtonProps) {
|
|||||||
size="300"
|
size="300"
|
||||||
onClick={() => onToggle()}
|
onClick={() => onToggle()}
|
||||||
outlined
|
outlined
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
size="100"
|
size="100"
|
||||||
@@ -73,8 +79,9 @@ function SoundButton({ enabled, onToggle }: SoundButtonProps) {
|
|||||||
type VideoButtonProps = {
|
type VideoButtonProps = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
onToggle: () => Promise<unknown>;
|
onToggle: () => Promise<unknown>;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
function VideoButton({ enabled, onToggle }: VideoButtonProps) {
|
function VideoButton({ enabled, onToggle, disabled }: VideoButtonProps) {
|
||||||
return (
|
return (
|
||||||
<TooltipProvider
|
<TooltipProvider
|
||||||
position="Top"
|
position="Top"
|
||||||
@@ -93,6 +100,7 @@ function VideoButton({ enabled, onToggle }: VideoButtonProps) {
|
|||||||
size="300"
|
size="300"
|
||||||
onClick={() => onToggle()}
|
onClick={() => onToggle()}
|
||||||
outlined
|
outlined
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
size="100"
|
size="100"
|
||||||
@@ -108,8 +116,9 @@ function VideoButton({ enabled, onToggle }: VideoButtonProps) {
|
|||||||
type ScreenShareButtonProps = {
|
type ScreenShareButtonProps = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
onToggle: () => void;
|
onToggle: () => void;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
function ScreenShareButton({ enabled, onToggle }: ScreenShareButtonProps) {
|
function ScreenShareButton({ enabled, onToggle, disabled }: ScreenShareButtonProps) {
|
||||||
return (
|
return (
|
||||||
<TooltipProvider
|
<TooltipProvider
|
||||||
position="Top"
|
position="Top"
|
||||||
@@ -128,6 +137,7 @@ function ScreenShareButton({ enabled, onToggle }: ScreenShareButtonProps) {
|
|||||||
size="300"
|
size="300"
|
||||||
onClick={onToggle}
|
onClick={onToggle}
|
||||||
outlined
|
outlined
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<Icon size="100" src={Icons.ScreenShare} filled={enabled} />
|
<Icon size="100" src={Icons.ScreenShare} filled={enabled} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@@ -136,8 +146,17 @@ function ScreenShareButton({ enabled, onToggle }: ScreenShareButtonProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CallControl({ callEmbed, compact }: { callEmbed: CallEmbed; compact: boolean }) {
|
export function CallControl({
|
||||||
|
callEmbed,
|
||||||
|
compact,
|
||||||
|
callJoined,
|
||||||
|
}: {
|
||||||
|
callEmbed: CallEmbed;
|
||||||
|
compact: boolean;
|
||||||
|
callJoined: boolean;
|
||||||
|
}) {
|
||||||
const { microphone, video, sound, screenshare } = useCallControlState(callEmbed.control);
|
const { microphone, video, sound, screenshare } = useCallControlState(callEmbed.control);
|
||||||
|
const setCallEmbed = useSetAtom(callEmbedAtom);
|
||||||
|
|
||||||
const [hangupState, hangup] = useAsyncCallback(
|
const [hangupState, hangup] = useAsyncCallback(
|
||||||
useCallback(() => callEmbed.hangup(), [callEmbed])
|
useCallback(() => callEmbed.hangup(), [callEmbed])
|
||||||
@@ -145,20 +164,38 @@ export function CallControl({ callEmbed, compact }: { callEmbed: CallEmbed; comp
|
|||||||
const exiting =
|
const exiting =
|
||||||
hangupState.status === AsyncStatus.Loading || hangupState.status === AsyncStatus.Success;
|
hangupState.status === AsyncStatus.Loading || hangupState.status === AsyncStatus.Success;
|
||||||
|
|
||||||
|
const handleHangup = () => {
|
||||||
|
if (!callJoined) {
|
||||||
|
setCallEmbed(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hangup();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box shrink="No" alignItems="Center" gap="300">
|
<Box shrink="No" alignItems="Center" gap="300">
|
||||||
<Box alignItems="Inherit" gap="200">
|
<Box alignItems="Inherit" gap="200">
|
||||||
<MicrophoneButton
|
<MicrophoneButton
|
||||||
enabled={microphone}
|
enabled={microphone}
|
||||||
onToggle={() => callEmbed.control.toggleMicrophone()}
|
onToggle={() => callEmbed.control.toggleMicrophone()}
|
||||||
|
disabled={!callJoined}
|
||||||
|
/>
|
||||||
|
<SoundButton
|
||||||
|
enabled={sound}
|
||||||
|
onToggle={() => callEmbed.control.toggleSound()}
|
||||||
|
disabled={!callJoined}
|
||||||
/>
|
/>
|
||||||
<SoundButton enabled={sound} onToggle={() => callEmbed.control.toggleSound()} />
|
|
||||||
{!compact && <StatusDivider />}
|
{!compact && <StatusDivider />}
|
||||||
<VideoButton enabled={video} onToggle={() => callEmbed.control.toggleVideo()} />
|
<VideoButton
|
||||||
|
enabled={video}
|
||||||
|
onToggle={() => callEmbed.control.toggleVideo()}
|
||||||
|
disabled={!callJoined}
|
||||||
|
/>
|
||||||
{!compact && (
|
{!compact && (
|
||||||
<ScreenShareButton
|
<ScreenShareButton
|
||||||
enabled={screenshare}
|
enabled={screenshare}
|
||||||
onToggle={() => callEmbed.control.toggleScreenshare()}
|
onToggle={() => callEmbed.control.toggleScreenshare()}
|
||||||
|
disabled={!callJoined}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -176,7 +213,7 @@ export function CallControl({ callEmbed, compact }: { callEmbed: CallEmbed; comp
|
|||||||
}
|
}
|
||||||
disabled={exiting}
|
disabled={exiting}
|
||||||
outlined
|
outlined
|
||||||
onClick={hangup}
|
onClick={handleHangup}
|
||||||
>
|
>
|
||||||
{!compact && (
|
{!compact && (
|
||||||
<Text as="span" size="L400">
|
<Text as="span" size="L400">
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export function CallStatus({ callEmbed }: CallStatusProps) {
|
|||||||
<CallRoomName room={room} />
|
<CallRoomName room={room} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<CallControl compact={compact} callEmbed={callEmbed} />
|
<CallControl callJoined={callJoined} compact={compact} callEmbed={callEmbed} />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,10 +13,29 @@ import { useCallMembers, useCallSession } from '../../hooks/useCall';
|
|||||||
import { CallMemberRenderer } from './CallMemberCard';
|
import { CallMemberRenderer } from './CallMemberCard';
|
||||||
import * as css from './styles.css';
|
import * as css from './styles.css';
|
||||||
import { CallControls } from './CallControls';
|
import { CallControls } from './CallControls';
|
||||||
|
import { useLivekitSupport } from '../../hooks/useLivekitSupport';
|
||||||
|
|
||||||
function JoinMessage({ hasParticipant }: { hasParticipant?: boolean }) {
|
function LivekitServerMissingMessage() {
|
||||||
|
return (
|
||||||
|
<Text style={{ margin: 'auto', color: color.Critical.Main }} size="L400" align="Center">
|
||||||
|
Your homeserver does not support calling. But you can still join call started by others.
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function JoinMessage({
|
||||||
|
hasParticipant,
|
||||||
|
livekitSupported,
|
||||||
|
}: {
|
||||||
|
hasParticipant?: boolean;
|
||||||
|
livekitSupported?: boolean;
|
||||||
|
}) {
|
||||||
if (hasParticipant) return null;
|
if (hasParticipant) return null;
|
||||||
|
|
||||||
|
if (livekitSupported === false) {
|
||||||
|
return <LivekitServerMissingMessage />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text style={{ margin: 'auto' }} size="L400" align="Center">
|
<Text style={{ margin: 'auto' }} size="L400" align="Center">
|
||||||
Voice chat’s empty — Be the first to hop in!
|
Voice chat’s empty — Be the first to hop in!
|
||||||
@@ -43,12 +62,13 @@ function AlreadyInCallMessage() {
|
|||||||
function CallPrescreen() {
|
function CallPrescreen() {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const room = useRoom();
|
const room = useRoom();
|
||||||
|
const livekitSupported = useLivekitSupport();
|
||||||
|
|
||||||
const powerLevels = usePowerLevelsContext();
|
const powerLevels = usePowerLevelsContext();
|
||||||
const creators = useRoomCreators(room);
|
const creators = useRoomCreators(room);
|
||||||
|
|
||||||
const permissions = useRoomPermissions(creators, powerLevels);
|
const permissions = useRoomPermissions(creators, powerLevels);
|
||||||
const canJoin = permissions.event(StateEvent.GroupCallMemberPrefix, mx.getSafeUserId());
|
const hasPermission = permissions.event(StateEvent.GroupCallMemberPrefix, mx.getSafeUserId());
|
||||||
|
|
||||||
const callSession = useCallSession(room);
|
const callSession = useCallSession(room);
|
||||||
const callMembers = useCallMembers(room, callSession);
|
const callMembers = useCallMembers(room, callSession);
|
||||||
@@ -57,6 +77,8 @@ function CallPrescreen() {
|
|||||||
const callEmbed = useCallEmbed();
|
const callEmbed = useCallEmbed();
|
||||||
const inOtherCall = callEmbed && callEmbed.roomId !== room.roomId;
|
const inOtherCall = callEmbed && callEmbed.roomId !== room.roomId;
|
||||||
|
|
||||||
|
const canJoin = hasPermission && (livekitSupported || hasParticipant);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Scroll variant="Surface" hideTrack>
|
<Scroll variant="Surface" hideTrack>
|
||||||
<Box className={css.CallViewContent} alignItems="Center" justifyContent="Center">
|
<Box className={css.CallViewContent} alignItems="Center" justifyContent="Center">
|
||||||
@@ -75,11 +97,15 @@ function CallPrescreen() {
|
|||||||
)}
|
)}
|
||||||
<CallMemberRenderer members={callMembers} />
|
<CallMemberRenderer members={callMembers} />
|
||||||
<PrescreenControls canJoin={canJoin} />
|
<PrescreenControls canJoin={canJoin} />
|
||||||
<Header size="300">
|
<Box className={css.PrescreenMessage} alignItems="Center">
|
||||||
{!inOtherCall &&
|
{!inOtherCall &&
|
||||||
(canJoin ? <JoinMessage hasParticipant={hasParticipant} /> : <NoPermissionMessage />)}
|
(hasPermission ? (
|
||||||
|
<JoinMessage hasParticipant={hasParticipant} livekitSupported={livekitSupported} />
|
||||||
|
) : (
|
||||||
|
<NoPermissionMessage />
|
||||||
|
))}
|
||||||
{inOtherCall && <AlreadyInCallMessage />}
|
{inOtherCall && <AlreadyInCallMessage />}
|
||||||
</Header>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Scroll>
|
</Scroll>
|
||||||
|
|||||||
@@ -22,3 +22,7 @@ export const CallMemberCard = style({
|
|||||||
export const CallControlContainer = style({
|
export const CallControlContainer = style({
|
||||||
padding: config.space.S400,
|
padding: config.space.S400,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const PrescreenMessage = style({
|
||||||
|
padding: config.space.S200,
|
||||||
|
});
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ import { useCallMembers, useCallSession } from '../../hooks/useCall';
|
|||||||
import { useCallEmbed, useCallStart } from '../../hooks/useCallEmbed';
|
import { useCallEmbed, useCallStart } from '../../hooks/useCallEmbed';
|
||||||
import { callChatAtom } from '../../state/callEmbed';
|
import { callChatAtom } from '../../state/callEmbed';
|
||||||
import { useCallPreferencesAtom } from '../../state/hooks/callPreferences';
|
import { useCallPreferencesAtom } from '../../state/hooks/callPreferences';
|
||||||
|
import { useAutoDiscoveryInfo } from '../../hooks/useAutoDiscoveryInfo';
|
||||||
|
import { livekitSupport } from '../../hooks/useLivekitSupport';
|
||||||
|
|
||||||
type RoomNavItemMenuProps = {
|
type RoomNavItemMenuProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
@@ -282,8 +284,14 @@ export function RoomNavItem({
|
|||||||
const startCall = useCallStart(direct);
|
const startCall = useCallStart(direct);
|
||||||
const callEmbed = useCallEmbed();
|
const callEmbed = useCallEmbed();
|
||||||
const callPref = useAtomValue(useCallPreferencesAtom());
|
const callPref = useAtomValue(useCallPreferencesAtom());
|
||||||
|
const autoDiscoveryInfo = useAutoDiscoveryInfo();
|
||||||
|
|
||||||
const handleStartCall: MouseEventHandler<HTMLAnchorElement> = (evt) => {
|
const handleStartCall: MouseEventHandler<HTMLAnchorElement> = (evt) => {
|
||||||
|
// Do not join if no livekit support or call is not started by others
|
||||||
|
if (!livekitSupport(autoDiscoveryInfo) && callMembers.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Do not join if already in call
|
// Do not join if already in call
|
||||||
if (callEmbed) {
|
if (callEmbed) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
|||||||
const isComposing = useComposingCheck();
|
const isComposing = useComposingCheck();
|
||||||
|
|
||||||
useElementSizeObserver(
|
useElementSizeObserver(
|
||||||
useCallback(() => document.body, []),
|
useCallback(() => fileDropContainerRef.current, [fileDropContainerRef]),
|
||||||
useCallback((width) => setHideStickerBtn(width < 500), [])
|
useCallback((width) => setHideStickerBtn(width < 500), [])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1475,7 +1475,13 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
|
|||||||
const senderId = mEvent.getSender() ?? '';
|
const senderId = mEvent.getSender() ?? '';
|
||||||
const senderName = getMemberDisplayName(room, senderId) || getMxIdLocalPart(senderId);
|
const senderName = getMemberDisplayName(room, senderId) || getMxIdLocalPart(senderId);
|
||||||
|
|
||||||
const callJoined = mEvent.getContent<SessionMembershipData>().application;
|
const content = mEvent.getContent<SessionMembershipData>();
|
||||||
|
const prevContent = mEvent.getPrevContent();
|
||||||
|
|
||||||
|
const callJoined = content.application;
|
||||||
|
if (callJoined && 'application' in prevContent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const timeJSX = (
|
const timeJSX = (
|
||||||
<Time
|
<Time
|
||||||
|
|||||||
@@ -492,7 +492,11 @@ export function RoomViewHeader({ callView }: { callView?: boolean }) {
|
|||||||
offset={4}
|
offset={4}
|
||||||
tooltip={
|
tooltip={
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<Text>{peopleDrawer ? 'Hide Members' : 'Show Members'}</Text>
|
{callView ? (
|
||||||
|
<Text>Members</Text>
|
||||||
|
) : (
|
||||||
|
<Text>{peopleDrawer ? 'Hide Members' : 'Show Members'}</Text>
|
||||||
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export function About({ requestClose }: AboutProps) {
|
|||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Box gap="100" alignItems="End">
|
<Box gap="100" alignItems="End">
|
||||||
<Text size="H3">Cinny</Text>
|
<Text size="H3">Cinny</Text>
|
||||||
<Text size="T200">v4.10.5</Text>
|
<Text size="T200">v4.11.1</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Text>Yet another matrix client.</Text>
|
<Text>Yet another matrix client.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { AutoDiscoveryInfo } from '../cs-api';
|
||||||
|
import { useAutoDiscoveryInfo } from './useAutoDiscoveryInfo';
|
||||||
|
|
||||||
|
export const livekitSupport = (autoDiscoveryInfo: AutoDiscoveryInfo): boolean => {
|
||||||
|
const rtcFoci = autoDiscoveryInfo['org.matrix.msc4143.rtc_foci'];
|
||||||
|
|
||||||
|
return (
|
||||||
|
Array.isArray(rtcFoci) && rtcFoci.some((info) => typeof info.livekit_service_url === 'string')
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useLivekitSupport = (): boolean => {
|
||||||
|
const autoDiscoveryInfo = useAutoDiscoveryInfo();
|
||||||
|
|
||||||
|
return livekitSupport(autoDiscoveryInfo);
|
||||||
|
};
|
||||||
@@ -15,7 +15,7 @@ export function AuthFooter() {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
v4.10.5
|
v4.11.1
|
||||||
</Text>
|
</Text>
|
||||||
<Text as="a" size="T300" href="https://twitter.com/cinnyapp" target="_blank" rel="noreferrer">
|
<Text as="a" size="T300" href="https://twitter.com/cinnyapp" target="_blank" rel="noreferrer">
|
||||||
Twitter
|
Twitter
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import React, { ReactNode, useCallback, useMemo } from 'react';
|
||||||
|
import { AutoDiscoveryInfoProvider } from '../../hooks/useAutoDiscoveryInfo';
|
||||||
|
import { AsyncStatus, useAsyncCallbackValue } from '../../hooks/useAsyncCallback';
|
||||||
|
import { autoDiscovery, AutoDiscoveryInfo } from '../../cs-api';
|
||||||
|
import { getMxIdServer } from '../../utils/matrix';
|
||||||
|
|
||||||
|
type AutoDiscoveryProps = {
|
||||||
|
userId: string;
|
||||||
|
baseUrl: string;
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
export function AutoDiscovery({ userId, baseUrl, children }: AutoDiscoveryProps) {
|
||||||
|
const [state] = useAsyncCallbackValue(
|
||||||
|
useCallback(async () => {
|
||||||
|
const server = getMxIdServer(userId);
|
||||||
|
return autoDiscovery(fetch, server ?? userId);
|
||||||
|
}, [userId])
|
||||||
|
);
|
||||||
|
|
||||||
|
const [, info] = state.status === AsyncStatus.Success ? state.data : [];
|
||||||
|
|
||||||
|
const fallback: AutoDiscoveryInfo = useMemo(
|
||||||
|
() => ({
|
||||||
|
'm.homeserver': {
|
||||||
|
base_url: baseUrl,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[baseUrl]
|
||||||
|
);
|
||||||
|
|
||||||
|
return <AutoDiscoveryInfoProvider value={info ?? fallback}>{children}</AutoDiscoveryInfoProvider>;
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ import { stopPropagation } from '../../utils/keyboard';
|
|||||||
import { SyncStatus } from './SyncStatus';
|
import { SyncStatus } from './SyncStatus';
|
||||||
import { AuthMetadataProvider } from '../../hooks/useAuthMetadata';
|
import { AuthMetadataProvider } from '../../hooks/useAuthMetadata';
|
||||||
import { getFallbackSession } from '../../state/sessions';
|
import { getFallbackSession } from '../../state/sessions';
|
||||||
|
import { AutoDiscovery } from './AutoDiscovery';
|
||||||
|
|
||||||
function ClientRootLoading() {
|
function ClientRootLoading() {
|
||||||
return (
|
return (
|
||||||
@@ -143,7 +144,7 @@ type ClientRootProps = {
|
|||||||
};
|
};
|
||||||
export function ClientRoot({ children }: ClientRootProps) {
|
export function ClientRoot({ children }: ClientRootProps) {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const { baseUrl } = getFallbackSession() ?? {};
|
const { baseUrl, userId } = getFallbackSession() ?? {};
|
||||||
|
|
||||||
const [loadState, loadMatrix] = useAsyncCallback<MatrixClient, Error, []>(
|
const [loadState, loadMatrix] = useAsyncCallback<MatrixClient, Error, []>(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
@@ -183,47 +184,55 @@ export function ClientRoot({ children }: ClientRootProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SpecVersions baseUrl={baseUrl!}>
|
<AutoDiscovery userId={userId!} baseUrl={baseUrl!}>
|
||||||
{mx && <SyncStatus mx={mx} />}
|
<SpecVersions baseUrl={baseUrl!}>
|
||||||
{loading && <ClientRootOptions mx={mx} />}
|
{mx && <SyncStatus mx={mx} />}
|
||||||
{(loadState.status === AsyncStatus.Error || startState.status === AsyncStatus.Error) && (
|
{loading && <ClientRootOptions mx={mx} />}
|
||||||
<SplashScreen>
|
{(loadState.status === AsyncStatus.Error || startState.status === AsyncStatus.Error) && (
|
||||||
<Box direction="Column" grow="Yes" alignItems="Center" justifyContent="Center" gap="400">
|
<SplashScreen>
|
||||||
<Dialog>
|
<Box
|
||||||
<Box direction="Column" gap="400" style={{ padding: config.space.S400 }}>
|
direction="Column"
|
||||||
{loadState.status === AsyncStatus.Error && (
|
grow="Yes"
|
||||||
<Text>{`Failed to load. ${loadState.error.message}`}</Text>
|
alignItems="Center"
|
||||||
)}
|
justifyContent="Center"
|
||||||
{startState.status === AsyncStatus.Error && (
|
gap="400"
|
||||||
<Text>{`Failed to start. ${startState.error.message}`}</Text>
|
>
|
||||||
)}
|
<Dialog>
|
||||||
<Button variant="Critical" onClick={mx ? () => startMatrix(mx) : loadMatrix}>
|
<Box direction="Column" gap="400" style={{ padding: config.space.S400 }}>
|
||||||
<Text as="span" size="B400">
|
{loadState.status === AsyncStatus.Error && (
|
||||||
Retry
|
<Text>{`Failed to load. ${loadState.error.message}`}</Text>
|
||||||
</Text>
|
)}
|
||||||
</Button>
|
{startState.status === AsyncStatus.Error && (
|
||||||
</Box>
|
<Text>{`Failed to start. ${startState.error.message}`}</Text>
|
||||||
</Dialog>
|
)}
|
||||||
</Box>
|
<Button variant="Critical" onClick={mx ? () => startMatrix(mx) : loadMatrix}>
|
||||||
</SplashScreen>
|
<Text as="span" size="B400">
|
||||||
)}
|
Retry
|
||||||
{loading || !mx ? (
|
</Text>
|
||||||
<ClientRootLoading />
|
</Button>
|
||||||
) : (
|
</Box>
|
||||||
<MatrixClientProvider value={mx}>
|
</Dialog>
|
||||||
<ServerConfigsLoader>
|
</Box>
|
||||||
{(serverConfigs) => (
|
</SplashScreen>
|
||||||
<CapabilitiesProvider value={serverConfigs.capabilities ?? {}}>
|
)}
|
||||||
<MediaConfigProvider value={serverConfigs.mediaConfig ?? {}}>
|
{loading || !mx ? (
|
||||||
<AuthMetadataProvider value={serverConfigs.authMetadata}>
|
<ClientRootLoading />
|
||||||
{children}
|
) : (
|
||||||
</AuthMetadataProvider>
|
<MatrixClientProvider value={mx}>
|
||||||
</MediaConfigProvider>
|
<ServerConfigsLoader>
|
||||||
</CapabilitiesProvider>
|
{(serverConfigs) => (
|
||||||
)}
|
<CapabilitiesProvider value={serverConfigs.capabilities ?? {}}>
|
||||||
</ServerConfigsLoader>
|
<MediaConfigProvider value={serverConfigs.mediaConfig ?? {}}>
|
||||||
</MatrixClientProvider>
|
<AuthMetadataProvider value={serverConfigs.authMetadata}>
|
||||||
)}
|
{children}
|
||||||
</SpecVersions>
|
</AuthMetadataProvider>
|
||||||
|
</MediaConfigProvider>
|
||||||
|
</CapabilitiesProvider>
|
||||||
|
)}
|
||||||
|
</ServerConfigsLoader>
|
||||||
|
</MatrixClientProvider>
|
||||||
|
)}
|
||||||
|
</SpecVersions>
|
||||||
|
</AutoDiscovery>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function WelcomePage() {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
>
|
>
|
||||||
v4.10.5
|
v4.11.1
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ export class CallEmbed {
|
|||||||
|
|
||||||
let initialMediaEvent = true;
|
let initialMediaEvent = true;
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this.listenEvent<ElementMediaStateDetail>(ElementWidgetActions.DeviceMute, (evt) => {
|
this.listenAction<ElementMediaStateDetail>(ElementWidgetActions.DeviceMute, (evt) => {
|
||||||
if (initialMediaEvent) {
|
if (initialMediaEvent) {
|
||||||
initialMediaEvent = false;
|
initialMediaEvent = false;
|
||||||
this.control.applyState();
|
this.control.applyState();
|
||||||
@@ -177,18 +177,27 @@ export class CallEmbed {
|
|||||||
return this.call.transport.send(ElementWidgetActions.HangupCall, {});
|
return this.call.transport.send(ElementWidgetActions.HangupCall, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
public listenEvent<T>(type: string, callback: (event: CustomEvent<T>) => void) {
|
public onPreparing(callback: () => void) {
|
||||||
this.call.on(`action:${type}`, callback);
|
return this.listenEvent('preparing', callback);
|
||||||
return () => {
|
}
|
||||||
this.call.off(`action:${type}`, callback);
|
|
||||||
};
|
public onPreparingError(callback: (error: any) => void) {
|
||||||
|
return this.listenEvent('error:preparing', callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onReady(callback: () => void) {
|
||||||
|
return this.listenEvent('ready', callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onCapabilitiesNotified(callback: () => void) {
|
||||||
|
return this.listenEvent('capabilitiesNotified', callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private start() {
|
private start() {
|
||||||
// Room widgets get locked to the room they were added in
|
// Room widgets get locked to the room they were added in
|
||||||
this.call.setViewedRoomId(this.roomId);
|
this.call.setViewedRoomId(this.roomId);
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this.listenEvent(ElementWidgetActions.JoinCall, this.onCallJoined.bind(this))
|
this.listenAction(ElementWidgetActions.JoinCall, this.onCallJoined.bind(this))
|
||||||
);
|
);
|
||||||
|
|
||||||
// Populate the map of "read up to" events for this widget with the current event in every room.
|
// Populate the map of "read up to" events for this widget with the current event in every room.
|
||||||
@@ -375,4 +384,15 @@ export class CallEmbed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public listenAction<T>(type: string, callback: (event: CustomEvent<T>) => void) {
|
||||||
|
return this.listenEvent(`action:${type}`, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public listenEvent<T>(type: string, callback: (event: T) => void) {
|
||||||
|
this.call.on(type, callback);
|
||||||
|
return () => {
|
||||||
|
this.call.off(type, callback);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,11 @@ export const getRecentEmojis = (mx: MatrixClient, limit?: number): IEmoji[] => {
|
|||||||
|
|
||||||
export function addRecentEmoji(mx: MatrixClient, unicode: string) {
|
export function addRecentEmoji(mx: MatrixClient, unicode: string) {
|
||||||
const recentEmojiEvent = getAccountData(mx, AccountDataEvent.ElementRecentEmoji);
|
const recentEmojiEvent = getAccountData(mx, AccountDataEvent.ElementRecentEmoji);
|
||||||
const recentEmoji = recentEmojiEvent?.getContent<IRecentEmojiContent>().recent_emoji ?? [];
|
const recentEmojiContent = recentEmojiEvent?.getContent<IRecentEmojiContent>();
|
||||||
|
const recentEmoji =
|
||||||
|
recentEmojiContent && Array.isArray(recentEmojiContent.recent_emoji)
|
||||||
|
? structuredClone(recentEmojiContent.recent_emoji)
|
||||||
|
: [];
|
||||||
|
|
||||||
const emojiIndex = recentEmoji.findIndex(([u]) => u === unicode);
|
const emojiIndex = recentEmoji.findIndex(([u]) => u === unicode);
|
||||||
let entry: [EmojiUnicode, EmojiUsageCount];
|
let entry: [EmojiUnicode, EmojiUsageCount];
|
||||||
|
|||||||
+15
-7
@@ -87,13 +87,21 @@ export const scaleYDimension = (x: number, scaledX: number, y: number): number =
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const parseGeoUri = (location: string) => {
|
export const parseGeoUri = (location: string) => {
|
||||||
const [, data] = location.split(':');
|
try {
|
||||||
const [cords] = data.split(';');
|
const [, data] = location.split(':');
|
||||||
const [latitude, longitude] = cords.split(',');
|
const [cords] = data.split(';');
|
||||||
return {
|
const [latitude, longitude] = cords.split(',');
|
||||||
latitude,
|
|
||||||
longitude,
|
if (typeof latitude === 'string' && typeof longitude === 'string') {
|
||||||
};
|
return {
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const START_SLASHES_REG = /^\/+/g;
|
const START_SLASHES_REG = /^\/+/g;
|
||||||
|
|||||||
Reference in New Issue
Block a user