diff --git a/ts/components/session/ActionsPanel.tsx b/ts/components/session/ActionsPanel.tsx
index b2efb92c5..6d750d921 100644
--- a/ts/components/session/ActionsPanel.tsx
+++ b/ts/components/session/ActionsPanel.tsx
@@ -46,6 +46,7 @@ import { loadDefaultRooms } from '../../opengroup/opengroupV2/ApiUtil';
import { ActionPanelOnionStatusLight } from '../dialog/OnionStatusPathDialog';
import { switchHtmlToDarkTheme, switchHtmlToLightTheme } from '../../state/ducks/SessionTheme';
+import { CallContainer } from './calling/CallContainer';
const Section = (props: { type: SectionType; avatarPath?: string | null }) => {
const ourNumber = useSelector(getOurNumber);
const unreadMessageCount = useSelector(getUnreadMessageCount);
@@ -286,6 +287,9 @@ export const ActionsPanel = () => {
return (
<>
+
+
+
diff --git a/ts/components/session/calling/CallContainer.tsx b/ts/components/session/calling/CallContainer.tsx
new file mode 100644
index 000000000..3f2f8871d
--- /dev/null
+++ b/ts/components/session/calling/CallContainer.tsx
@@ -0,0 +1,168 @@
+import React, { useState } from 'react';
+import styled from 'styled-components';
+import _ from 'underscore';
+import { ConversationModel } from '../../../models/conversation';
+// tslint:disable-next-line: no-submodule-imports
+import { getConversationController } from '../../../session/conversations/ConversationController';
+import { SessionButton, SessionButtonColor } from '../SessionButton';
+import { SessionWrapperModal } from '../SessionWrapperModal';
+
+export const CallWindow = styled.div`
+ position: absolute;
+ z-index: 9;
+ padding: 2rem;
+ top: 50vh;
+ left: 50vw;
+ transform: translate(-50%, -50%);
+ display: flex;
+ flex-direction: column;
+`;
+
+// similar styling to modal header
+const CallWindowHeader = styled.div`
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+
+ padding: $session-margin-lg;
+
+ font-family: $session-font-default;
+ text-align: center;
+ line-height: 18px;
+ font-size: $session-font-md;
+ font-weight: 700;
+`;
+
+// TODO: Add proper styling for this
+const VideoContainer = styled.div`
+ width: 200px;
+ height: 200px;
+`;
+
+const CallWindowInner = styled.div`
+ position: relative;
+ background-color: pink;
+ border: 1px solid #d3d3d3;
+ text-align: center;
+ padding: 2rem;
+ display: flex;
+ flex-direction: column;
+`;
+
+const CallWindowControls = styled.div`
+ position: absolute;
+ top: 100%;
+ left: 0;
+ width: 100%;
+ /* background: green; */
+ padding: 5px;
+ transform: translateY(-100%);
+`;
+
+// type WindowPositionType = {
+// top: string;
+// left: string;
+// } | null;
+
+type CallStateType = 'connecting' | 'ongoing' | 'incoming' | null
+
+export const CallContainer = () => {
+ const conversations = getConversationController().getConversations();
+
+ // TODO:
+ /**
+ * Add mute input, deafen, end call, possibly add person to call
+ * duration - look at how duration calculated for recording.
+ */
+
+ const [connectionState, setConnectionState] = useState
('incoming');
+ // const [callWindowPosition, setCallWindowPosition] = useState(null)
+
+ // picking a conversation at random to test with
+ let randConvo = _.sample(conversations) as ConversationModel;
+ randConvo.callState = 'incoming';
+ console.warn({ randConvo });
+
+ const firstCallingConvo = _.first(conversations.filter(convo => convo.callState !== undefined));
+
+ //#region input handlers
+ const handleAcceptIncomingCall = () => {
+ console.warn('accept call');
+
+ if (firstCallingConvo) {
+ setConnectionState('connecting');
+ firstCallingConvo.callState = 'connecting';
+
+ // some delay
+ setConnectionState('ongoing');
+ firstCallingConvo.callState = 'ongoing';
+ }
+ // set conversationState = setting up
+ }
+
+ const handleDeclineIncomingCall = () => {
+ // set conversation.callState = null or undefined
+ // close the modal
+ if (firstCallingConvo) {
+ firstCallingConvo.callState = undefined;
+ }
+ console.warn('declined call');
+ }
+
+ const handleEndCall = () => {
+ // call method to end call connection
+ console.warn("ending the call");
+ }
+
+ const handleMouseDown = () => {
+ // reposition call window
+ }
+ //#endregion
+
+ return (
+ <>
+ {connectionState === 'connecting' ?
+ 'connecting...'
+ : null
+ }
+ {connectionState === 'ongoing' ?
+
+
+
+ { firstCallingConvo ? firstCallingConvo.getName() : 'Group name not found'}
+
+
+
+
+
+
+
+
+ : null
+ }
+
+ {!connectionState ?
+
+ 'none'
+
+ : null
+ }
+
+ {connectionState === 'incoming' ?
+
+
+
+
+
+
+ : null
+ }
+ >
+ );
+};
+
diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts
index dfc68c9df..ac79ac978 100644
--- a/ts/models/conversation.ts
+++ b/ts/models/conversation.ts
@@ -184,6 +184,8 @@ export class ConversationModel extends Backbone.Model {
public markRead: (newestUnreadDate: number, providedOptions?: any) => Promise;
public initialPromise: any;
+ public callState: 'incoming' | 'connecting' | 'ongoing' | 'none' | undefined;
+
private typingRefreshTimer?: NodeJS.Timeout | null;
private typingPauseTimer?: NodeJS.Timeout | null;
private typingTimer?: NodeJS.Timeout | null;