feat: open convo a session sogs when joined from the UI

pull/2410/head
Audric Ackermann 3 years ago
parent 444282e2ec
commit b9cccfc2a8

@ -999,6 +999,8 @@ input {
}
.contact-selection-list {
display: flex;
flex-direction: column;
width: 20vw;
}

@ -25,6 +25,7 @@ const StyledSessionMemberItem = styled.button<{
}>`
cursor: pointer;
flex-shrink: 0;
flex-grow: 1;
font-family: var(--font-default);
padding: 0px var(--margins-sm);
height: ${props => (props.inMentions ? '40px' : '50px')};

@ -6,11 +6,10 @@ type PillContainerProps = {
margin?: string;
padding?: string;
onClick?: () => void;
onMouseEnter?: () => void;
onMouseLeave?: () => void;
disableHover?: boolean;
};
const StyledPillContainerHoverable = styled.div<PillContainerProps>`
export const StyledPillContainerHoverable = styled.div<PillContainerProps>`
background: none;
position: relative;
@ -26,7 +25,6 @@ const StyledPillContainerHoverable = styled.div<PillContainerProps>`
`;
const StyledPillInner = styled.div<PillContainerProps>`
background: green;
background: none;
display: flex;
@ -42,18 +40,18 @@ const StyledPillInner = styled.div<PillContainerProps>`
padding: ${props => props.padding || ''};
margin: ${props => props.margin || ''};
border-radius: 300px;
cursor: pointer;
cursor: ${props => (props.disableHover ? 'unset' : 'pointer')};
border: 1px solid var(--color-pill-divider);
transition: var(--default-duration);
&:hover {
background: var(--color-clickable-hovered);
background: ${props => (props.disableHover ? 'none' : 'var(--color-clickable-hovered)')};
}
`;
export const PillTooltipWrapper = (props: PillContainerProps) => {
return <StyledPillContainerHoverable {...props}>{props.children}</StyledPillContainerHoverable>;
};
export const PillContainerHoverable = (props: PillContainerProps) => {
return <StyledPillInner {...props}>{props.children}</StyledPillInner>;
export const PillContainerHoverable = (props: Omit<PillContainerProps, 'disableHover'>) => {
return (
<StyledPillInner {...props} disableHover={!props.onClick}>
{props.children}
</StyledPillInner>
);
};

@ -52,6 +52,7 @@ export type ReactionProps = {
handlePopupReaction?: (emoji: string) => void;
handlePopupClick?: () => void;
};
// tslint:disable-next-line: use-simple-attributes
export const Reaction = (props: ReactionProps): ReactElement => {
const {

@ -7,12 +7,17 @@ import { SessionButton, SessionButtonColor, SessionButtonType } from '../../basi
import { SessionIdEditable } from '../../basic/SessionIdEditable';
import { SessionSpinner } from '../../basic/SessionSpinner';
import { OverlayHeader } from './OverlayHeader';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { resetOverlayMode } from '../../../state/ducks/section';
import { joinOpenGroupV2WithUIEvents } from '../../../session/apis/open_group_api/opengroupV2/JoinOpenGroupV2';
import {
joinOpenGroupV2WithUIEvents,
JoinSogsRoomUICallbackArgs,
} from '../../../session/apis/open_group_api/opengroupV2/JoinOpenGroupV2';
import { openGroupV2CompleteURLRegex } from '../../../session/apis/open_group_api/utils/OpenGroupUtils';
import { ToastUtils } from '../../../session/utils';
import useKey from 'react-use/lib/useKey';
import { getOverlayMode } from '../../../state/selectors/section';
import { openConversationWithMessages } from '../../../state/ducks/conversations';
async function joinOpenGroup(serverUrl: string) {
// guess if this is an open
@ -31,6 +36,8 @@ export const OverlayCommunity = () => {
const [loading, setLoading] = useState(false);
const [groupUrl, setGroupUrl] = useState('');
const overlayModeIsCommunity = useSelector(getOverlayMode) === 'open-group';
function closeOverlay() {
dispatch(resetOverlayMode());
}
@ -52,6 +59,15 @@ export const OverlayCommunity = () => {
}
}
function onJoinSessionSogsRoom(args: JoinSogsRoomUICallbackArgs) {
setLoading(args.loadingState === 'started');
if (args.loadingState === 'finished' && overlayModeIsCommunity && args.conversationKey) {
closeOverlay();
void openConversationWithMessages({ conversationKey: args.conversationKey, messageId: null }); // open to last unread for a session run sogs
}
}
useKey('Escape', closeOverlay);
const title = window.i18n('joinOpenGroup');
@ -84,7 +100,10 @@ export const OverlayCommunity = () => {
/>
<SessionSpinner loading={loading} />
<SessionJoinableRooms onRoomClicked={closeOverlay} />
<SessionJoinableRooms
onJoinSessionSogsRoom={onJoinSessionSogsRoom}
alreadyJoining={loading}
/>
</div>
);
};

@ -1,9 +1,10 @@
import React, { useCallback, useEffect } from 'react';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import {
joinOpenGroupV2WithUIEvents,
JoinSogsRoomUICallbackArgs,
parseOpenGroupV2,
} from '../../../session/apis/open_group_api/opengroupV2/JoinOpenGroupV2';
import { sogsV3FetchPreviewBase64 } from '../../../session/apis/open_group_api/sogsv3/sogsV3FetchFile';
@ -11,7 +12,7 @@ import { updateDefaultBase64RoomData } from '../../../state/ducks/defaultRooms';
import { StateType } from '../../../state/reducer';
import { Avatar, AvatarSize } from '../../avatar/Avatar';
import { Flex } from '../../basic/Flex';
import { PillContainerHoverable, PillTooltipWrapper } from '../../basic/PillContainer';
import { PillContainerHoverable, StyledPillContainerHoverable } from '../../basic/PillContainer';
import { SessionSpinner } from '../../basic/SessionSpinner';
import { H3 } from '../../basic/Text';
// tslint:disable: no-void-expression
@ -21,7 +22,7 @@ export type JoinableRoomProps = {
name: string;
roomId: string;
imageId?: string;
onClick: (completeUrl: string) => void;
onClick?: (completeUrl: string) => void;
base64Data?: string;
};
@ -77,7 +78,7 @@ const SessionJoinableRoomAvatar = (props: JoinableRoomProps) => {
base64Data={props.base64Data}
{...props}
pubkey=""
onAvatarClick={() => props.onClick(props.completeUrl)}
onAvatarClick={() => props.onClick?.(props.completeUrl)}
/>
);
};
@ -94,64 +95,74 @@ const SessionJoinableRoomName = (props: JoinableRoomProps) => {
};
const SessionJoinableRoomRow = (props: JoinableRoomProps) => {
const { onClick, completeUrl } = props;
const onClickWithUrl = onClick
? () => {
onClick?.(completeUrl);
}
: undefined;
return (
<PillTooltipWrapper>
<PillContainerHoverable
onClick={() => {
props.onClick(props.completeUrl);
}}
margin="5px"
padding="5px"
>
<StyledPillContainerHoverable>
<PillContainerHoverable onClick={onClickWithUrl} margin="5px" padding="5px">
<SessionJoinableRoomAvatar {...props} />
<SessionJoinableRoomName {...props} />
</PillContainerHoverable>
</PillTooltipWrapper>
</StyledPillContainerHoverable>
);
};
export const SessionJoinableRooms = (props: { onRoomClicked: () => void }) => {
const JoinableRooms = (props: {
onJoinSessionSogsRoom: (args: JoinSogsRoomUICallbackArgs) => void;
alreadyJoining: boolean;
}) => {
const joinableRooms = useSelector((state: StateType) => state.defaultRooms);
const onRoomClicked = useCallback(
(loading: boolean) => {
if (loading) {
props.onRoomClicked();
}
},
[props.onRoomClicked]
const onClick = props.alreadyJoining
? undefined
: (completeUrl: string) => {
void joinOpenGroupV2WithUIEvents(completeUrl, true, false, props.onJoinSessionSogsRoom);
};
return (
<>
{joinableRooms.rooms.map(r => {
return (
<SessionJoinableRoomRow
key={r.id}
completeUrl={r.completeUrl}
name={r.name}
roomId={r.id}
imageId={r.imageId}
base64Data={r.base64Data}
onClick={onClick}
/>
);
})}
</>
);
};
export const SessionJoinableRooms = (props: {
onJoinSessionSogsRoom: (args: JoinSogsRoomUICallbackArgs) => void;
alreadyJoining: boolean;
}) => {
const joinableRooms = useSelector((state: StateType) => state.defaultRooms);
if (!joinableRooms.inProgress && !joinableRooms.rooms?.length) {
window?.log?.info('no default joinable rooms yet and not in progress');
return null;
}
const componentToRender = joinableRooms.inProgress ? (
<SessionSpinner loading={true} />
) : (
joinableRooms.rooms.map(r => {
return (
<SessionJoinableRoomRow
key={r.id}
completeUrl={r.completeUrl}
name={r.name}
roomId={r.id}
imageId={r.imageId}
base64Data={r.base64Data}
onClick={completeUrl => {
void joinOpenGroupV2WithUIEvents(completeUrl, true, false, onRoomClicked);
}}
/>
);
})
);
return (
<Flex container={true} flexGrow={1} flexDirection="column" width="93%">
<H3 text={window.i18n('orJoinOneOfThese')} />
<Flex container={true} flexGrow={0} flexWrap="wrap" justifyContent="center">
{componentToRender}
{joinableRooms.inProgress ? (
<SessionSpinner loading={true} />
) : (
<JoinableRooms {...props} />
)}
</Flex>
</Flex>
);

@ -113,7 +113,9 @@ const ContactsTitle = () => {
return null;
}
return <StyledChooseActionTitle>{window.i18n('contactsHeader')}</StyledChooseActionTitle>;
return (
<StyledChooseActionTitle tabIndex={0}>{window.i18n('contactsHeader')}</StyledChooseActionTitle>
);
};
export const ContactsListWithBreaks = () => {

@ -39,7 +39,7 @@ export type OpenGroupV2InfoJoinable = OpenGroupV2Info & {
// tslint:disable: no-http-string
const legacyDefaultServerIP = '116.203.70.33';
const defaultServer = 'https://open.getsession.org';
export const defaultServer = 'https://open.getsession.org';
const defaultServerHost = new window.URL(defaultServer).host;
/**

@ -2,6 +2,8 @@ import _ from 'lodash';
import { OpenGroupV2Room } from '../../../../data/opengroups';
import { getConversationController } from '../../../conversations';
import { PromiseUtils, ToastUtils } from '../../../utils';
import { getEventSessionSogsFirstPoll } from '../../../utils/GlobalEvents';
import { sleepFor, waitForTask } from '../../../utils/Promise';
import { forceSyncConfigurationNowIfNeeded } from '../../../utils/syncUtils';
import {
@ -103,6 +105,11 @@ async function joinOpenGroupV2(room: OpenGroupV2Room, fromConfigMessage: boolean
}
}
export type JoinSogsRoomUICallbackArgs = {
loadingState: 'started' | 'finished' | 'failed';
conversationKey: string | null;
};
/**
* This function does not throw
* This function can be used to join an opengroupv2 server, from a user initiated click or from a syncMessage.
@ -121,7 +128,7 @@ export async function joinOpenGroupV2WithUIEvents(
completeUrl: string,
showToasts: boolean,
fromConfigMessage: boolean,
uiCallback?: (loading: boolean) => void
uiCallback?: (args: JoinSogsRoomUICallbackArgs) => void
): Promise<boolean> {
try {
const parsedRoom = parseOpenGroupV2(completeUrl);
@ -142,11 +149,23 @@ export async function joinOpenGroupV2WithUIEvents(
if (showToasts) {
ToastUtils.pushToastInfo('connectingToServer', window.i18n('connectingToServer'));
}
if (uiCallback) {
uiCallback(true);
}
uiCallback?.({ loadingState: 'started', conversationKey: conversationID });
await joinOpenGroupV2(parsedRoom, fromConfigMessage);
// this is very hacky but is made so we wait for the poller to receive the first messages related to that room.
// once the poller added all the messages to the queue of jobs to be run, we still wait a bit for them to be processed.
// This won't age well, but I am not too sure how we can design better.
await waitForTask(done => {
const eventToWait = getEventSessionSogsFirstPoll(parsedRoom.roomId);
window.Whisper.events.on(eventToWait, async () => {
window.Whisper.events.off(eventToWait);
await sleepFor(5000);
done(0);
});
}, 25000);
const isConvoCreated = getConversationController().get(conversationID);
if (isConvoCreated) {
if (showToasts) {
@ -155,21 +174,21 @@ export async function joinOpenGroupV2WithUIEvents(
window.i18n('connectToServerSuccess')
);
}
uiCallback?.({ loadingState: 'finished', conversationKey: conversationID });
return true;
} else {
if (showToasts) {
ToastUtils.pushToastError('connectToServerFail', window.i18n('connectToServerFail'));
}
}
uiCallback?.({ loadingState: 'failed', conversationKey: conversationID });
} catch (error) {
window?.log?.warn('got error while joining open group:', error.message);
if (showToasts) {
ToastUtils.pushToastError('connectToServerFail', window.i18n('connectToServerFail'));
}
} finally {
if (uiCallback) {
uiCallback(false);
}
uiCallback?.({ loadingState: 'failed', conversationKey: null });
}
return false;
}

@ -1,6 +1,6 @@
import { AbortController } from 'abort-controller';
import { getOpenGroupV2ConversationId } from '../utils/OpenGroupUtils';
import { OpenGroupRequestCommonType } from './ApiUtil';
import { defaultServer, OpenGroupRequestCommonType } from './ApiUtil';
import _, { isNumber, isObject } from 'lodash';
import { OpenGroupData } from '../../../../data/opengroups';
@ -20,6 +20,7 @@ import {
roomHasBlindEnabled,
} from '../sogsv3/sogsV3Capabilities';
import { OpenGroupReaction } from '../../../../types/Reaction';
import { getEventSessionSogsFirstPoll } from '../../../utils/GlobalEvents';
export type OpenGroupMessageV4 = {
/** AFAIK: indicates the number of the message in the group. e.g. 2nd message will be 1 or 2 */
@ -317,6 +318,14 @@ export class OpenGroupServerPoller {
subrequestOptions,
this.roomIdsToPoll
);
if (this.serverUrl === defaultServer) {
for (const room of subrequestOptions) {
if (room.type === 'messages' && !room.messages?.sinceSeqNo && room.messages?.roomId) {
window.Whisper.events.trigger(getEventSessionSogsFirstPoll(room.messages.roomId));
}
}
}
} catch (e) {
window?.log?.warn('Got error while compact fetch:', e.message);
} finally {

@ -0,0 +1,3 @@
export function getEventSessionSogsFirstPoll(roomId: string) {
return `first-poll-session-sogs:${roomId}`;
}

4
ts/window.d.ts vendored

@ -21,11 +21,7 @@ declare global {
CONSTANTS: any;
Events: any;
Lodash: any;
SessionSnodeAPI: any;
Session: any;
StubAppDotNetApi: any;
StringView: any;
StubMessageAPI: any;
Whisper: any;
clearLocalData: any;
clipboard: any;

Loading…
Cancel
Save