Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ Prerequisites:
## 1. Fetching dependencies and configurations

1. Run `yarn`

- This will install all dependencies and fetch a [configuration](https://github.com/wireapp/wire-web-config-wire/) for the application.

## 2. Build & run
Expand Down
8 changes: 4 additions & 4 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
"main": "dist/index.js",
"license": "GPL-3.0",
"dependencies": {
"@wireapp/commons": "5.4.9",
"@wireapp/commons": "5.4.10",
"dotenv": "16.5.0",
"dotenv-extended": "2.9.0",
"express": "4.22.0",
"express-sitemap-xml": "3.1.0",
"express-useragent": "1.0.15",
"fs-extra": "11.3.1",
"express-useragent": "2.0.2",
"fs-extra": "11.3.2",
"geolite2": "1.3.0",
"hbs": "4.2.0",
"helmet": "8.1.0",
Expand All @@ -31,7 +31,7 @@
"@types/hbs": "4.0.5",
"@types/jest": "^29.5.14",
"@types/node": "22.5.5",
"browserslist": "^4.28.0",
"browserslist": "^4.28.1",
"jest": "29.7.0",
"rimraf": "6.1.2",
"typescript": "5.6.3"
Expand Down
89 changes: 76 additions & 13 deletions server/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1123,15 +1123,15 @@ __metadata:
languageName: node
linkType: hard

"@wireapp/commons@npm:5.4.9":
version: 5.4.9
resolution: "@wireapp/commons@npm:5.4.9"
"@wireapp/commons@npm:5.4.10":
version: 5.4.10
resolution: "@wireapp/commons@npm:5.4.10"
dependencies:
ansi-regex: "npm:5.0.1"
fs-extra: "npm:11.3.1"
logdown: "npm:3.3.1"
platform: "npm:1.3.6"
checksum: 10/10804e146e7dbe3f87ad2473a066eafe17faf0e2f8c2e89e378b844e2ff72777b8cb640aa76adb52f17d8ad46bcb37cfb4cffb8311fdea3102d653ff1e49bfa0
checksum: 10/35832d8da7eeb07dae12ed3489780f302b861841683505ba07ccb10eed3e139af56ccf6245ed4c6a8ee16bddfbc374b145634ea37ff6257d86ed944bd3dc06c6
languageName: node
linkType: hard

Expand Down Expand Up @@ -1462,6 +1462,15 @@ __metadata:
languageName: node
linkType: hard

"baseline-browser-mapping@npm:^2.9.0":
version: 2.9.4
resolution: "baseline-browser-mapping@npm:2.9.4"
bin:
baseline-browser-mapping: dist/cli.js
checksum: 10/71cf80f822e74e0f0109a9ed69d87fdb128d01bf06670a2ef91166a3eb636034e0a013d76cd9915a9d38594f649848c8c1ef6cbe39ed417f38314ff5bd22e393
languageName: node
linkType: hard

"basic-ftp@npm:^5.0.2":
version: 5.0.3
resolution: "basic-ftp@npm:5.0.3"
Expand Down Expand Up @@ -1522,7 +1531,7 @@ __metadata:
languageName: node
linkType: hard

"browserslist@npm:^4.21.9, browserslist@npm:^4.28.0":
"browserslist@npm:^4.21.9":
version: 4.28.0
resolution: "browserslist@npm:4.28.0"
dependencies:
Expand All @@ -1537,6 +1546,21 @@ __metadata:
languageName: node
linkType: hard

"browserslist@npm:^4.28.1":
version: 4.28.1
resolution: "browserslist@npm:4.28.1"
dependencies:
baseline-browser-mapping: "npm:^2.9.0"
caniuse-lite: "npm:^1.0.30001759"
electron-to-chromium: "npm:^1.5.263"
node-releases: "npm:^2.0.27"
update-browserslist-db: "npm:^1.2.0"
bin:
browserslist: cli.js
checksum: 10/64f2a97de4bce8473c0e5ae0af8d76d1ead07a5b05fc6bc87b848678bb9c3a91ae787b27aa98cdd33fc00779607e6c156000bed58fefb9cf8e4c5a183b994cdb
languageName: node
linkType: hard

"bser@npm:2.1.1":
version: 2.1.1
resolution: "bser@npm:2.1.1"
Expand Down Expand Up @@ -1628,6 +1652,13 @@ __metadata:
languageName: node
linkType: hard

"caniuse-lite@npm:^1.0.30001759":
version: 1.0.30001759
resolution: "caniuse-lite@npm:1.0.30001759"
checksum: 10/da0ec28dd993dffa99402914903426b9466d2798d41c1dc9341fcb7dd10f58fdd148122e2c65001246c030ba1c939645b7b4597f6321e3246dc792323bb11541
languageName: node
linkType: hard

"chalk@npm:3.0.0, chalk@npm:~3.0.0":
version: 3.0.0
resolution: "chalk@npm:3.0.0"
Expand Down Expand Up @@ -2083,6 +2114,13 @@ __metadata:
languageName: node
linkType: hard

"electron-to-chromium@npm:^1.5.263":
version: 1.5.266
resolution: "electron-to-chromium@npm:1.5.266"
checksum: 10/2c7e05d1df189013e01b9fa19f5794dc249b80f330ab87f78674fa7416df153e2d32738d16914eee1112b5d8878b6181336e502215a34c63c255da078de5209d
languageName: node
linkType: hard

"emittery@npm:^0.13.1":
version: 0.13.1
resolution: "emittery@npm:0.13.1"
Expand Down Expand Up @@ -2341,10 +2379,10 @@ __metadata:
languageName: node
linkType: hard

"express-useragent@npm:1.0.15":
version: 1.0.15
resolution: "express-useragent@npm:1.0.15"
checksum: 10/5427b00a19b80d8bc2cee6a5c79c17e3d544b19a105d3a8b9d15e10d782b53f0d3c4522ca2bb04673528cd7adbc3c4e26604b58316a0ec086fb28b7064401b92
"express-useragent@npm:2.0.2":
version: 2.0.2
resolution: "express-useragent@npm:2.0.2"
checksum: 10/39ed1a15ff5fb865a1386e45958c167f60b7fc90a0ea7f7971bde7fa0f2fd79667f08abb29cffd352afe15172b5442cef5809ea491438762bd4cd511675f02d8
languageName: node
linkType: hard

Expand Down Expand Up @@ -2512,6 +2550,17 @@ __metadata:
languageName: node
linkType: hard

"fs-extra@npm:11.3.2":
version: 11.3.2
resolution: "fs-extra@npm:11.3.2"
dependencies:
graceful-fs: "npm:^4.2.0"
jsonfile: "npm:^6.0.1"
universalify: "npm:^2.0.0"
checksum: 10/d559545c73fda69c75aa786f345c2f738b623b42aea850200b1582e006a35278f63787179e3194ba19413c26a280441758952b0c7e88dd96762d497e365a6c3e
languageName: node
linkType: hard

"fs-extra@npm:^8.1.0":
version: 8.1.0
resolution: "fs-extra@npm:8.1.0"
Expand Down Expand Up @@ -5547,6 +5596,20 @@ __metadata:
languageName: node
linkType: hard

"update-browserslist-db@npm:^1.2.0":
version: 1.2.2
resolution: "update-browserslist-db@npm:1.2.2"
dependencies:
escalade: "npm:^3.2.0"
picocolors: "npm:^1.1.1"
peerDependencies:
browserslist: ">= 4.21.0"
bin:
update-browserslist-db: cli.js
checksum: 10/ae2102d3c83fca35e9deb012d82bfde6f734998ced937e34a3bf239a4b67577108fdd144283aafc0e5e3cf38ca1aecd7714906ba6f562896c762d2f2fa391026
languageName: node
linkType: hard

"util-deprecate@npm:^1.0.1":
version: 1.0.2
resolution: "util-deprecate@npm:1.0.2"
Expand Down Expand Up @@ -5641,14 +5704,14 @@ __metadata:
"@types/hbs": "npm:4.0.5"
"@types/jest": "npm:^29.5.14"
"@types/node": "npm:22.5.5"
"@wireapp/commons": "npm:5.4.9"
browserslist: "npm:^4.28.0"
"@wireapp/commons": "npm:5.4.10"
browserslist: "npm:^4.28.1"
dotenv: "npm:16.5.0"
dotenv-extended: "npm:2.9.0"
express: "npm:4.22.0"
express-sitemap-xml: "npm:3.1.0"
express-useragent: "npm:1.0.15"
fs-extra: "npm:11.3.1"
express-useragent: "npm:2.0.2"
fs-extra: "npm:11.3.2"
geolite2: "npm:1.3.0"
hbs: "npm:4.2.0"
helmet: "npm:8.1.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {ChannelAvatar} from 'Components/Avatar/ChannelAvatar';
import {UserBlockedBadge} from 'Components/Badge';
import {CellDescription} from 'Components/ConversationListCell/components/CellDescription';
import {UserInfo} from 'Components/UserInfo';
import {useConversationCall} from 'Hooks/useConversationCall';
import {useNoInternetCallGuard} from 'Hooks/useNoInternetCallGuard/useNoInternetCallGuard';
import type {Conversation} from 'Repositories/entity/Conversation';
import {MediaType} from 'Repositories/media/MediaType';
Expand All @@ -45,7 +46,7 @@ interface ConversationListCellProps {
dataUieName: string;
isSelected?: (conversation: Conversation) => boolean;
onClick: (event: ReactMouseEvent<HTMLDivElement, MouseEvent> | ReactKeyBoardEvent<HTMLDivElement>) => void;
onJoinCall: (conversation: Conversation, mediaType: MediaType) => void;
onJoinCall: (conversation: Conversation, mediaType: MediaType) => Promise<void>;
rightClick: (conversation: Conversation, event: MouseEvent | React.MouseEvent<Element, MouseEvent>) => void;
showJoinButton: boolean;
handleArrowKeyDown: (e: React.KeyboardEvent) => void;
Expand Down Expand Up @@ -93,6 +94,7 @@ export const ConversationListCell = ({
]);

const guardCall = useNoInternetCallGuard();
const {isCallConnecting} = useConversationCall(conversation);

const {isChannelsEnabled} = useChannelsFeatureFlag();
const isActive = isSelected(conversation);
Expand All @@ -103,17 +105,38 @@ export const ConversationListCell = ({
const [isContextMenuOpen, setContextMenuOpen] = useState(false);
const contextMenuKeyboardShortcut = `keyboard-shortcut-${conversation.id}`;

// Ref for immediate synchronous protection from multiple clicks
const isJoiningCallRef = useRef(false);

// Button is disabled if either local state or call state indicates joining
const isButtonDisabled = isJoiningCallRef.current || isCallConnecting;

const openContextMenu = (event: MouseEvent | React.MouseEvent<Element, MouseEvent>) => {
event.stopPropagation();
event.preventDefault();
rightClick(conversation, event);
};

const onClickJoinCall = (event: React.MouseEvent) => {
const handleJoinCall = async (event: React.MouseEvent) => {
event.preventDefault();
guardCall(() => {
onJoinCall(conversation, MediaType.AUDIO);
});

// Check ref first for immediate synchronous protection
if (isJoiningCallRef.current || isButtonDisabled) {
return;
}

// Immediately disable synchronously
isJoiningCallRef.current = true;

try {
await guardCall(async () => {
await onJoinCall(conversation, MediaType.AUDIO);
isJoiningCallRef.current = false;
});
} catch (error) {
// Re-enable on error
isJoiningCallRef.current = false;
}
};

const handleDivKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
Expand Down Expand Up @@ -241,10 +264,11 @@ export const ConversationListCell = ({

{showJoinButton && (
<button
onClick={onClickJoinCall}
onClick={handleJoinCall}
type="button"
className="call-ui__button call-ui__button--green call-ui__button--join"
data-uie-name="do-call-controls-call-join"
disabled={isButtonDisabled}
>
{t('callJoin')}
</button>
Expand Down
35 changes: 30 additions & 5 deletions src/script/components/TitleBar/TitleBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {ConversationVerificationBadges} from 'Components/Badge';
import {useCallAlertState} from 'Components/calling/useCallAlertState';
import * as Icon from 'Components/Icon';
import {LegalHoldDot} from 'Components/LegalHoldDot';
import {useConversationCall} from 'Hooks/useConversationCall';
import {useNoInternetCallGuard} from 'Hooks/useNoInternetCallGuard/useNoInternetCallGuard';
import {CallState} from 'Repositories/calling/CallState';
import {ConversationFilter} from 'Repositories/conversation/ConversationFilter';
Expand Down Expand Up @@ -103,12 +104,24 @@ export const TitleBar = ({
]);

const guardCall = useNoInternetCallGuard();
const {isCallConnecting, isCallActive} = useConversationCall(conversation);

const {isActivatedAccount} = useKoSubscribableChildren(selfUser, ['isActivatedAccount']);
const {joinedCall, activeCalls} = useKoSubscribableChildren(callState, ['joinedCall', 'activeCalls']);

const currentFocusedElementRef = useRef<HTMLButtonElement | null>(null);

// using ref for immediate double-click protection
const isStartingCallRef = useRef(false);

// Reset local state when a call becomes active or cleared
if (isStartingCallRef && (isCallActive || activeCalls.length === 0)) {
isStartingCallRef.current = false;
}

// Button is disabled if starting, connecting, or already active
const isCallButtonDisabled = isReadOnlyConversation || isStartingCallRef.current || isCallConnecting || isCallActive;

const badgeLabelCopy = useMemo(() => {
if (is1to1 && isRequest) {
return '';
Expand Down Expand Up @@ -200,9 +213,21 @@ export const TitleBar = ({
const onClickDetails = () => showDetails(false);

const startCallAndShowAlert = () => {
guardCall(() => {
callActions.startAudio(conversation);
showStartedCallAlert(isGroupOrChannel);
if (isStartingCallRef.current || isCallButtonDisabled) {
return;
}

isStartingCallRef.current = true;

guardCall(async () => {
try {
await callActions.startAudio(conversation);
isStartingCallRef.current = false;
showStartedCallAlert(isGroupOrChannel);
} catch (error) {
// Re-enable on error
isStartingCallRef.current = false;
}
});
};

Expand Down Expand Up @@ -306,7 +331,7 @@ export const TitleBar = ({
startCallAndShowAlert();
}}
data-uie-name="do-call"
disabled={isReadOnlyConversation}
disabled={isCallButtonDisabled}
>
<CallIcon />
</button>
Expand All @@ -331,7 +356,7 @@ export const TitleBar = ({
css={{marginBottom: 0}}
onClick={onClickStartAudio}
data-uie-name="do-call"
disabled={isReadOnlyConversation}
disabled={isCallButtonDisabled}
>
<CallIcon />
</IconButton>
Expand Down
Loading
Loading