add joinable rooms on opengroupv2 joining screen

pull/1576/head
Audric Ackermann 4 years ago
parent 6aa699ad23
commit 5289d4c2aa
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -7,7 +7,7 @@ import is from '@sindresorhus/is';
import * as GoogleChrome from '../util/GoogleChrome';
import * as MIME from '../types/MIME';
import { SessionIconButton, SessionIconSize, SessionIconType } from './session/icon';
import { Flex } from './session/Flex';
import { Flex } from './basic/Flex';
import { DefaultTheme } from 'styled-components';
// useCss has some issues on our setup. so import it directly
// tslint:disable-next-line: no-submodule-imports

@ -0,0 +1,29 @@
import React from 'react';
import styled from 'styled-components';
type PillContainerProps = {
children: React.ReactNode;
margin?: string;
padding?: string;
onClick?: () => void;
};
const StyledPillContainer = styled.div<PillContainerProps>`
display: flex;
background: none;
flex-direction: 'row';
flex-grow: 1;
align-items: center;
padding: ${props => props.padding || ''};
margin: ${props => props.margin || ''};
border-radius: 300px;
border: 1px solid ${props => props.theme.colors.pillDividerColor};
transition: ${props => props.theme.common.animations.defaultDuration};
&:hover {
background: ${props => props.theme.colors.clickableHovered};
}
`;
export const PillContainer = (props: PillContainerProps) => {
return <StyledPillContainer {...props}>{props.children}</StyledPillContainer>;
};

@ -0,0 +1,23 @@
import React from 'react';
import styled from 'styled-components';
type TextProps = {
text: string;
subtle?: boolean;
opposite?: boolean;
};
const StyledDefaultText = styled.div<TextProps>`
transition: ${props => props.theme.common.animations.defaultDuration};
font-family: ${props => props.theme.common.fonts.sessionFontDefault};
color: ${props =>
props.opposite
? props.theme.colors.textColorOpposite
: props.subtle
? props.theme.colors.textColorSubtle
: props.theme.colors.textColor};
`;
export const Text = (props: TextProps) => {
return <StyledDefaultText {...props}>{props.text}</StyledDefaultText>;
};

@ -1,7 +1,7 @@
import React from 'react';
import classNames from 'classnames';
import { Spinner } from '../Spinner';
import { Spinner } from '../basic/Spinner';
import { LocalizerType } from '../../types/Util';
import { AttachmentType } from '../../types/Attachment';
import { useEncryptedFileFetch } from '../../hooks/useEncryptedFileFetch';

@ -2,7 +2,7 @@ import React from 'react';
import classNames from 'classnames';
import { Avatar, AvatarSize } from '../Avatar';
import { Spinner } from '../Spinner';
import { Spinner } from '../basic/Spinner';
import { MessageBody } from './MessageBody';
import { ImageGrid } from './ImageGrid';
import { Image } from './Image';

@ -5,7 +5,7 @@ import { ToastUtils } from '../../session/utils';
import { SessionModal } from '../session/SessionModal';
import { DefaultTheme } from 'styled-components';
import { SessionSpinner } from '../session/SessionSpinner';
import { Flex } from '../session/Flex';
import { Flex } from '../basic/Flex';
import { ConversationModel } from '../../models/conversation';
interface Props {
convo: ConversationModel;

@ -5,7 +5,7 @@ import { ApiV2 } from '../../opengroup/opengroupV2';
import { ConversationController } from '../../session/conversations';
import { PubKey } from '../../session/types';
import { ToastUtils } from '../../session/utils';
import { Flex } from '../session/Flex';
import { Flex } from '../basic/Flex';
import { SessionButton, SessionButtonColor, SessionButtonType } from '../session/SessionButton';
import { ContactType, SessionMemberListItem } from '../session/SessionMemberListItem';
import { SessionModal } from '../session/SessionModal';

@ -3,7 +3,7 @@ import classNames from 'classnames';
import { MessageSendingErrorText, MetadataSpacer } from './MetadataUtilComponent';
import { OutgoingMessageStatus } from './OutgoingMessageStatus';
import { Spinner } from '../../Spinner';
import { Spinner } from '../../basic/Spinner';
import { MetadataBadges } from './MetadataBadge';
import { Timestamp } from '../Timestamp';
import { ExpireTimer } from '../ExpireTimer';

@ -31,6 +31,7 @@ import { showLeftPaneSection } from '../../state/ducks/section';
import { cleanUpOldDecryptedMedias } from '../../session/crypto/DecryptedAttachmentsManager';
import { OpenGroupManagerV2 } from '../../opengroup/opengroupV2/OpenGroupManagerV2';
import { loadDefaultRoomsIfNeeded } from '../../opengroup/opengroupV2/ApiUtil';
// tslint:disable-next-line: no-import-side-effect no-submodule-imports
export enum SectionType {
@ -175,11 +176,10 @@ export const ActionsPanel = () => {
};
void generateAttachmentKeyIfEmpty();
// trigger a sync message if needed for our other devices
// 'http://sessionopengroup.com/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231b'
// 'https://sog.ibolpap.finance/main?public_key=b464aa186530c97d6bcf663a3a3b7465a5f782beaa67c83bee99468824b4aa10'
// 'https://opengroup.bilb.us/main?public_key=1352534ba73d4265973280431dbc72e097a3e43275d1ada984f9805b4943047d'
void OpenGroupManagerV2.getInstance().startPolling();
void syncConfiguration();
void loadDefaultRoomsIfNeeded();
}, []);
// wait for cleanUpMediasInterval and then start cleaning up medias

@ -12,6 +12,7 @@ import { PillDivider } from './PillDivider';
import { DefaultTheme } from 'styled-components';
import { UserUtils } from '../../session/utils';
import { ConversationTypeEnum } from '../../models/conversation';
import { SessionJoinableRooms } from './SessionJoinableDefaultRooms';
export enum SessionClosableOverlayType {
Message = 'message',
@ -220,6 +221,7 @@ export class SessionClosableOverlay extends React.Component<Props, State> {
{descriptionLong && <div className="session-description-long">{descriptionLong}</div>}
{isMessageView && false && <h4>{window.i18n('or')}</h4>}
{/* FIXME enable back those two items when they are working */}
{isOpenGroupView && <SessionJoinableRooms />}
{isMessageView && false && (
<UserSearchDropdown
searchTerm={searchTerm || ''}

@ -0,0 +1,70 @@
import React, { useReducer } from 'react';
import { useSelector } from 'react-redux';
import { joinOpenGroupV2WithUIEvents } from '../../opengroup/opengroupV2/JoinOpenGroupV2';
import { StateType } from '../../state/reducer';
import { Avatar, AvatarSize } from '../Avatar';
import { Flex } from '../basic/Flex';
import { PillContainer } from '../basic/PillContainer';
// tslint:disable: no-void-expression
export type JoinableRoomProps = {
completeUrl: string;
name: string;
imageId?: string;
onClick: (completeUrl: string) => void;
};
const SessionJoinableRoomAvatar = (props: JoinableRoomProps) => {
return (
<Avatar
size={AvatarSize.XS}
{...props}
onAvatarClick={() => props.onClick(props.completeUrl)}
/>
);
};
const SessionJoinableRoomName = (props: JoinableRoomProps) => {
return <Flex padding="0 10px">{props.name}</Flex>;
};
const SessionJoinableRoomRow = (props: JoinableRoomProps) => {
return (
<PillContainer
onClick={() => {
props.onClick(props.completeUrl);
}}
margin="5px"
padding="5px"
>
<SessionJoinableRoomAvatar {...props} />
<SessionJoinableRoomName {...props} />
</PillContainer>
);
};
export const SessionJoinableRooms = () => {
const joinableRooms = useSelector((state: StateType) => state.defaultRooms);
if (!joinableRooms?.length) {
console.warn('no default joinable rooms yet');
return <></>;
}
return (
<Flex container={true} flexGrow={1} flexWrap="wrap">
{joinableRooms.map(r => {
return (
<SessionJoinableRoomRow
key={r.id}
completeUrl={r.completeUrl}
name={r.name}
onClick={completeUrl => {
void joinOpenGroupV2WithUIEvents(completeUrl, true);
}}
/>
);
})}
</Flex>
);
};

@ -1,5 +1,5 @@
import React from 'react';
import { Flex } from './Flex';
import { Flex } from '../basic/Flex';
// tslint:disable: react-unused-props-and-state
interface Props {

@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import { SessionIcon, SessionIconSize, SessionIconType } from './icon/';
import { Flex } from './Flex';
import { Flex } from '../basic/Flex';
import styled, { ThemeContext } from 'styled-components';
import { noop } from 'lodash';

@ -13,7 +13,7 @@ import { SignalService } from '../../../protobuf';
import { Constants } from '../../../session';
import { toArray } from 'react-emoji-render';
import { Flex } from '../Flex';
import { Flex } from '../../basic/Flex';
import { AttachmentList } from '../../conversation/AttachmentList';
import { ToastUtils } from '../../../session/utils';
import { AttachmentUtil } from '../../../util';

@ -1,6 +1,6 @@
import React, { useContext } from 'react';
import styled, { ThemeContext } from 'styled-components';
import { Flex } from '../Flex';
import { Flex } from '../../basic/Flex';
import { SessionIcon, SessionIconSize, SessionIconType } from '../icon';
// padding-inline-end: ${props => props.theme.common.margins.md};

@ -1,5 +1,5 @@
import React, { useContext } from 'react';
import { Flex } from '../Flex';
import { Flex } from '../../basic/Flex';
import { SessionIconButton, SessionIconSize, SessionIconType } from '../icon';
import { ReplyingToMessageProps } from './SessionCompositionBox';
import styled, { DefaultTheme, ThemeContext } from 'styled-components';

@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Flex } from '../Flex';
import { Flex } from '../../basic/Flex';
import { SessionButton, SessionButtonColor, SessionButtonType } from '../SessionButton';
import { SessionSpinner } from '../SessionSpinner';
import { signInWithLinking, signInWithRecovery, validatePassword } from './RegistrationTabs';

@ -1,6 +1,11 @@
import _ from 'underscore';
import { PubKey } from '../../session/types';
import { allowOnlyOneAtATime } from '../../session/utils/Promise';
import { fromBase64ToArrayBuffer, fromHex } from '../../session/utils/String';
import { updateDefaultRooms } from '../../state/ducks/defaultRooms';
import { getCompleteUrlFromRoom } from '../utils/OpenGroupUtils';
import { parseOpenGroupV2 } from './JoinOpenGroupV2';
import { getAllRoomInfos } from './OpenGroupAPIV2';
import { OpenGroupMessageV2 } from './OpenGroupMessageV2';
export const defaultServer = 'https://sessionopengroup.com';
@ -36,6 +41,13 @@ export type OpenGroupV2Info = {
imageId?: string;
};
export type OpenGroupV2InfoJoinable = {
id: string;
name: string;
completeUrl: string;
imageId?: string;
};
/**
* Try to build an full url and check it for validity.
* @returns null if the check failed. the built URL otherwise
@ -97,3 +109,53 @@ export const parseMessages = async (
);
return _.compact(messages);
};
// 'http://sessionopengroup.com/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231b'
// FIXME audric change this to sessionopengroup.com once http is fixed
const defaultRoom =
'https://opengroup.bilb.us/main?public_key=1352534ba73d4265973280431dbc72e097a3e43275d1ada984f9805b4943047d';
const loadDefaultRoomsSingle = () =>
allowOnlyOneAtATime(
'loadDefaultRoomsSingle',
async (): Promise<Array<OpenGroupV2InfoJoinable>> => {
const roomInfos = parseOpenGroupV2(defaultRoom);
if (roomInfos) {
try {
const roomsGot = await getAllRoomInfos(roomInfos);
if (!roomsGot) {
return [];
}
return roomsGot.map(room => {
return {
...room,
completeUrl: getCompleteUrlFromRoom({
serverUrl: roomInfos.serverUrl,
serverPublicKey: roomInfos.serverPublicKey,
roomId: room.id,
}),
};
});
} catch (e) {
window.log.warn('loadDefaultRoomsIfNeeded failed', e);
}
return [];
}
return [];
}
);
/**
* Load to the cache all the details of the room of the default opengroupv2 server
* This call will only run once at a time.
*/
export const loadDefaultRoomsIfNeeded = async () => {
// FIXME audric do the UI and refresh this list from time to time
const allRooms: Array<OpenGroupV2InfoJoinable> = await loadDefaultRoomsSingle();
if (allRooms !== undefined) {
window.inboxStore?.dispatch(updateDefaultRooms(allRooms));
}
};

@ -27,6 +27,7 @@ export function parseOpenGroupV2(urlWithPubkey: string): OpenGroupV2Room | undef
if (!openGroupV2CompleteURLRegex.test(lowerCased)) {
throw new Error('regex fail');
}
// prefix the URL if it does not have a prefix
const prefixedUrl = prefixify(lowerCased);
// new URL fails if the protocol is not explicit
@ -44,7 +45,7 @@ export function parseOpenGroupV2(urlWithPubkey: string): OpenGroupV2Room | undef
};
return room;
} catch (e) {
window.log.error('Invalid Opengroup v2 join URL:', lowerCased);
window.log.error('Invalid Opengroup v2 join URL:', lowerCased, e);
}
return undefined;
}
@ -127,14 +128,14 @@ export async function joinOpenGroupV2WithUIEvents(
showToasts: boolean,
uiCallback?: (loading: boolean) => void
): Promise<boolean> {
const parsedRoom = parseOpenGroupV2(completeUrl);
if (!parsedRoom) {
if (showToasts) {
ToastUtils.pushToastError('connectToServer', window.i18n('invalidOpenGroupUrl'));
}
return false;
}
try {
const parsedRoom = parseOpenGroupV2(completeUrl);
if (!parsedRoom) {
if (showToasts) {
ToastUtils.pushToastError('connectToServer', window.i18n('invalidOpenGroupUrl'));
}
return false;
}
const conversationID = getOpenGroupV2ConversationId(parsedRoom.serverUrl, parsedRoom.roomId);
if (ConversationController.getInstance().get(conversationID)) {
if (showToasts) {

@ -20,9 +20,15 @@ export const openGroupV2ServerUrlRegex = new RegExp(
`${protocolRegex.source}${hostnameRegex.source}${portRegex}`
);
/**
* Regex to use to check if a string is a v2open completeURL with pubkey.
* Be aware that the /g flag is not set as .test() will otherwise return alternating result
*
* @see https://stackoverflow.com/a/9275499/1680951
*/
export const openGroupV2CompleteURLRegex = new RegExp(
`^${openGroupV2ServerUrlRegex.source}\/${roomIdV2Regex}${qMark}${publicKeyParam}${publicKeyRegex}$`,
'gm'
`${openGroupV2ServerUrlRegex.source}\/${roomIdV2Regex}${qMark}${publicKeyParam}${publicKeyRegex}`,
'm'
);
/**

@ -6,13 +6,15 @@ import { SessionToast, SessionToastType } from '../../components/session/Session
// if you push a toast manually with toast...() be sure to set the type attribute of the SessionToast component
export function pushToastError(id: string, title: string, description?: string) {
toast.error(
<SessionToast title={title} description={description} type={SessionToastType.Error} />
<SessionToast title={title} description={description} type={SessionToastType.Error} />,
{ toastId: id, updateId: id }
);
}
export function pushToastWarning(id: string, title: string, description?: string) {
toast.warning(
<SessionToast title={title} description={description} type={SessionToastType.Warning} />
<SessionToast title={title} description={description} type={SessionToastType.Warning} />,
{ toastId: id, updateId: id }
);
}
@ -28,7 +30,8 @@ export function pushToastInfo(
description={description}
type={SessionToastType.Info}
onToastClick={onToastClick}
/>
/>,
{ toastId: id, updateId: id }
);
}
@ -44,7 +47,8 @@ export function pushToastSuccess(
description={description}
type={SessionToastType.Success}
icon={icon}
/>
/>,
{ toastId: id, updateId: id }
);
}

@ -0,0 +1,24 @@
import { createSlice } from '@reduxjs/toolkit';
import { OpenGroupV2InfoJoinable } from '../../opengroup/opengroupV2/ApiUtil';
export type DefaultRoomsState = Array<OpenGroupV2InfoJoinable>;
const initialState: DefaultRoomsState = [];
/**
* This slice is the one holding the default joinable rooms fetched once in a while from the default opengroup v2 server.
*/
const defaultRoomsSlice = createSlice({
name: 'defaultRooms',
initialState,
reducers: {
updateDefaultRooms(state, action) {
window.log.warn('updating default rooms', action.payload);
return action.payload as DefaultRoomsState;
},
},
});
const { actions, reducer } = defaultRoomsSlice;
export const { updateDefaultRooms } = actions;
export const defaultRoomReducer = reducer;

@ -5,6 +5,7 @@ import { ConversationsStateType, reducer as conversations } from './ducks/conver
import { reducer as user, UserStateType } from './ducks/user';
import { reducer as theme, ThemeStateType } from './ducks/theme';
import { reducer as section, SectionStateType } from './ducks/section';
import { defaultRoomReducer as defaultRooms, DefaultRoomsState } from './ducks/defaultRooms';
export type StateType = {
search: SearchStateType;
@ -13,6 +14,7 @@ export type StateType = {
conversations: ConversationsStateType;
theme: ThemeStateType;
section: SectionStateType;
defaultRooms: DefaultRoomsState;
};
export const reducers = {
@ -24,6 +26,7 @@ export const reducers = {
user,
theme,
section,
defaultRooms,
};
// Making this work would require that our reducer signature supported AnyAction, not

Loading…
Cancel
Save