Merge branch 'ui-redesigns' into theming

pull/2521/head
William Grant 3 years ago
commit 6cd81e06f2

@ -155,9 +155,6 @@
"oceanLightThemeTitle": "Ocean Light",
"pruneSettingTitle": "Trim Communities",
"pruneSettingDescription": "Delete messages from Communities older than 6 months, and where there are over 2,000 messages.",
"pruningOpengroupDialogTitle": "Community pruning",
"pruningOpengroupDialogMessage": "Pruning old communities messages improves performance. Enable pruning for communities messages older than 6 months?",
"pruningOpengroupDialogSubMessage": "You can change this setting in the Session settings menu",
"enable": "Enable",
"keepDisabled": "Keep disabled",
"notificationSettingsDialog": "The information shown in notifications.",
@ -165,6 +162,10 @@
"noNameOrMessage": "No name or content",
"nameOnly": "Name Only",
"newMessage": "New Message",
"createConversationNewContact": "Create a conversation with a new contact",
"createConversationNewGroup": "Create a group with existing contacts",
"joinACommunity": "Join a community",
"chooseAnAction": "Choose an action to start a conversation",
"newMessages": "New Messages",
"notificationMostRecentFrom": "Most recent from: $name$",
"notificationFrom": "From:",
@ -172,6 +173,7 @@
"sendFailed": "Send Failed",
"mediaMessage": "Media message",
"messageBodyMissing": "Please enter a message body.",
"messageBody": "Message body",
"unblockToSend": "Unblock this contact to send a message.",
"unblockGroupToSend": "This group is blocked. Unlock it if you would like to send a message.",
"youChangedTheTimer": "You set the disappearing message timer to $time$",
@ -297,7 +299,7 @@
"changePasswordToastDescription": "Your password has been changed. Please keep it safe.",
"removePasswordToastDescription": "You have removed your password.",
"publicChatExists": "You are already connected to this community",
"connectToServerFail": "Couldn't join group",
"connectToServerFail": "Couldn't join community",
"connectingToServer": "Connecting...",
"connectToServerSuccess": "Successfully connected to community",
"setPasswordFail": "Failed to set password",
@ -367,7 +369,7 @@
"invalidGroupNameTooShort": "Please enter a group name",
"invalidGroupNameTooLong": "Please enter a shorter group name",
"pickClosedGroupMember": "Please pick at least 1 group member",
"closedGroupMaxSize": "A closed group cannot have more than 100 members",
"closedGroupMaxSize": "A group cannot have more than 100 members",
"noBlockedContacts": "No blocked contacts",
"userAddedToModerators": "User added to admin list",
"userRemovedFromModerators": "User removed from admin list",
@ -380,7 +382,7 @@
"closedGroupInviteOkText": "Retry invitations",
"closedGroupInviteSuccessTitlePlural": "Group Invitations Completed",
"closedGroupInviteSuccessTitle": "Group Invitation Succeeded",
"closedGroupInviteSuccessMessage": "Successfully invited closed group members",
"closedGroupInviteSuccessMessage": "Successfully invited group members",
"notificationForConvo": "Notifications",
"notificationForConvo_all": "All",
"notificationForConvo_disabled": "Disabled",
@ -420,7 +422,7 @@
"recoveryPhraseRevealMessage": "Secure your account by saving your recovery phrase. Reveal your recovery phrase then store it safely to secure it.",
"recoveryPhraseRevealButtonText": "Reveal Recovery Phrase",
"notificationSubtitle": "Notifications - $setting$",
"surveyTitle": "We'd love your Feedback",
"surveyTitle": "We'd Love Your Feedback",
"faq": "FAQ",
"support": "Support",
"clearAll": "Clear All",

@ -29,6 +29,7 @@ window.sessionFeatureFlags = {
useTestNet: Boolean(
process.env.NODE_APP_INSTANCE && process.env.NODE_APP_INSTANCE.includes('testnet')
),
useSettingsThemeSwitcher: false,
debug: {
debugFileServerRequests: false,
debugNonSnodeRequests: false,

@ -19,20 +19,20 @@ body {
// scrollbars
::-webkit-scrollbar {
width: 9px;
height: 9px;
width: 6px;
height: 6px;
}
::-webkit-scrollbar-thumb {
background: var(--color-scroll-bar-thumb);
border: solid 2px var(--color-text-opposite);
border-radius: 20px;
&:hover {
background: var(--color-light-gray-color);
}
}
::-webkit-scrollbar-track {
background: var(--color-scroll-bar-track);
background: none;
}
audio {

@ -94,7 +94,7 @@ textarea {
background-color: var(--color-transparent-color);
&.disabled {
cursor: default;
cursor: not-allowed;
}
&.default,
@ -106,18 +106,6 @@ textarea {
background: var(--color-clickable-hovered);
}
&.green,
&.white,
&.primary,
&.secondary,
&.success,
&.danger,
&.warning {
&.disabled {
filter: brightness(60%);
}
}
&.green {
background-color: var(--color-accent-button);
color: var(--color-text-opposite);
@ -182,10 +170,10 @@ textarea {
&.white,
&.green {
&.disabled {
filter: brightness(60%);
@include transparent-background(var(--color-text-subtle));
&:hover {
filter: brightness(60%);
@include transparent-background(var(--color-text-subtle));
}
}
}
@ -823,6 +811,8 @@ input {
}
.contact-selection-list {
display: flex;
flex-direction: column;
width: 20vw;
}

@ -152,9 +152,6 @@ $session-subtle-factor: 0.6;
// ////////////////// Sizing ////////////////////
// //////////////////////////////////////////////
// Default Components
$session-left-pane-width: 300px;
// Various Components
$session-modal-size-sm: 220px;
$session-modal-size-md: 400px;

@ -372,38 +372,6 @@
}
}
&--status {
display: flex;
justify-content: center;
position: absolute;
left: 0;
z-index: 2;
right: 0;
bottom: $composition-container-height + var(--margins-md);
.session-button {
display: flex;
justify-content: center;
align-items: center;
width: 173px;
font-weight: 300;
font-family: $session-font-default;
&.primary {
cursor: default;
user-select: none;
&:hover {
filter: brightness(100%);
border: 2px solid #161819;
}
background-color: var(--color-darkest-gray-color);
border: 2px solid #161819;
}
}
}
&--timer {
display: inline-flex;
align-items: center;

@ -71,7 +71,6 @@ $session-compose-margin: 20px;
}
.module-left-pane {
width: $session-left-pane-width;
position: relative;
height: 100vh;
flex-shrink: 0;

@ -162,7 +162,6 @@
white-space: nowrap;
font-weight: bold;
text-decoration: none;
color: var(--color-text-subtle);
transition: var(--default-duration);
@ -179,8 +178,8 @@
&-description-long {
padding-top: 0;
padding-bottom: 20px;
color: var(--color-text-subtle);
// TODO Theming needs to be updated
color: rgba(black, 0.6);
text-align: center;
font-size: 12px;
line-height: 20px;

@ -18,39 +18,44 @@ const AvatarItem = (props: { memberPubkey: string; isAdmin: boolean }) => {
);
};
const StyledSessionMemberItem = styled.div<{
const StyledSessionMemberItem = styled.button<{
inMentions?: boolean;
zombie?: boolean;
selected?: boolean;
}>`
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
flex-grow: 1;
font-family: var(--font-default);
padding: 0px var(--margins-sm);
height: ${props => (props.inMentions ? '40px' : '50px')};
display: flex;
justify-content: space-between;
transition: var(--default-duration);
transition: var(--default-duration);
opacity: ${props => (props.zombie ? 0.5 : 1)};
background-color: ${props =>
props.selected && 'var(--color-conversation-item-selected) !important'};
:not(:last-child) {
border-bottom: var(--border-session);
}
background-color: ${props =>
props.selected ? 'var(--color-conversation-item-selected) !important' : null};
`;
const StyledInfo = styled.div`
display: flex;
align-items: center;
min-width: 0;
`;
const StyledName = styled.span`
font-weight: bold;
margin-inline-start: var(--margins-md);
margin-inline-end: var(--margins-md);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
const StyledCheckContainer = styled.div`
@ -97,7 +102,6 @@ export const MemberListItem = (props: {
}
: {}
}
role="button"
data-testid={dataTestId}
zombie={isZombie}
inMentions={inMentions}
@ -105,7 +109,6 @@ export const MemberListItem = (props: {
>
<StyledInfo>
<AvatarItem memberPubkey={pubkey} isAdmin={isAdmin || false} />
<StyledName>{memberName}</StyledName>
</StyledInfo>

@ -6,13 +6,11 @@ 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;
flex-direction: 'row';
flex-shrink: 0;
@ -26,7 +24,6 @@ const StyledPillContainerHoverable = styled.div<PillContainerProps>`
`;
const StyledPillInner = styled.div<PillContainerProps>`
background: green;
background: none;
display: flex;
@ -42,18 +39,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>
);
};

@ -1,5 +1,6 @@
import React, { ChangeEvent } from 'react';
import styled from 'styled-components';
import { black } from '../../state/ducks/SessionTheme';
import { Flex } from '../basic/Flex';
// tslint:disable: react-unused-props-and-state
@ -46,7 +47,7 @@ const StyledLabel = styled.label<{
outline: var(--color-text) solid 1px;
border: none;
outline-offset: ${props => props.outlineOffset}px;
margin: ${props => props.beforeMargins || ''};
${props => props.beforeMargins && `margin: ${props.beforeMargins};`};
}
`;
@ -86,6 +87,7 @@ export const SessionRadio = (props: Props) => {
filledSize={filledSize}
outlineOffset={outlineOffset}
beforeMargins={beforeMargins}
aria-label={label}
>
{label}
</StyledLabel>
@ -105,7 +107,7 @@ const StyledInputOutlineSelected = styled(StyledInput)`
const StyledLabelOutlineSelected = styled(StyledLabel)<{ selectedColor: string }>`
:before {
background: ${props => props.selectedColor};
outline: #0000 solid 1px;
outline: ${black} solid 1px;
}
`;

@ -2,6 +2,7 @@ import React from 'react';
import { updateConfirmModal } from '../../state/ducks/modalDialog';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
import { white } from '../../state/ducks/SessionTheme';
const StyledKnob = styled.div<{ active: boolean }>`
position: absolute;
@ -10,7 +11,7 @@ const StyledKnob = styled.div<{ active: boolean }>`
height: 21px;
width: 21px;
border-radius: 28px;
background-color: white;
background-color: ${white};
box-shadow: ${props =>
props.active ? '-2px 1px 3px rgba(0, 0, 0, 0.15)' : '2px 1px 3px rgba(0, 0, 0, 0.15);'};
@ -22,7 +23,7 @@ const StyledKnob = styled.div<{ active: boolean }>`
const StyledSessionToggle = styled.div<{ active: boolean }>`
width: 51px;
height: 25px;
border: 1px solid #e5e5ea;
border: 1px solid #e5e5ea; // TODO Theming update
border-radius: 16px;
position: relative;

@ -1,5 +1,5 @@
import React from 'react';
import styled from 'styled-components';
import styled, { CSSProperties } from 'styled-components';
type TextProps = {
text: string;
@ -28,6 +28,7 @@ export const Text = (props: TextProps) => {
type SpacerProps = {
size: 'lg' | 'md' | 'sm' | 'xs';
style?: CSSProperties;
};
const SpacerStyled = styled.div<SpacerProps>`
@ -54,19 +55,19 @@ const Spacer = (props: SpacerProps) => {
return <SpacerStyled {...props} />;
};
export const SpacerLG = () => {
return <Spacer size="lg" />;
export const SpacerLG = (props: { style?: CSSProperties }) => {
return <Spacer size="lg" style={props.style} />;
};
export const SpacerMD = () => {
return <Spacer size="md" />;
export const SpacerMD = (props: { style?: CSSProperties }) => {
return <Spacer size="md" style={props.style} />;
};
export const SpacerSM = () => {
return <Spacer size="sm" />;
export const SpacerSM = (props: { style?: CSSProperties }) => {
return <Spacer size="sm" style={props.style} />;
};
export const SpacerXS = () => {
return <Spacer size="xs" />;
export const SpacerXS = (props: { style?: CSSProperties }) => {
return <Spacer size="xs" style={props.style} />;
};
type H3Props = {

@ -35,23 +35,21 @@ export const YourSessionIDPill = () => {
};
const StyledYourSessionIDSelectable = styled.p`
user-select: text;
user-select: none;
text-align: center;
word-break: break-all;
padding: 0px var(--margins-lg);
font-weight: 300;
color: var(--color-text);
font-size: var(--font-size-sm);
padding: 0px var(--margins-md);
`;
export const YourSessionIDSelectable = () => {
const ourSessionID = UserUtils.getOurPubKeyStrFromCache();
return (
<StyledYourSessionIDSelectable data-testid="your-session-id">
{ourSessionID}
{ourSessionID.slice(0, 33)}
<br />
{ourSessionID.slice(33)}
</StyledYourSessionIDSelectable>
);
};

@ -6,7 +6,7 @@ import { getOverlayMode } from '../../state/selectors/section';
import { SessionIcon } from '../icon';
// tslint:disable: use-simple-attributes
const StyledMenuButton = styled.label`
const StyledMenuButton = styled.button`
position: relative;
display: inline-block;
@ -20,17 +20,13 @@ const StyledMenuButton = styled.label`
height: 33px;
cursor: pointer;
transition: var(--default-duration);
:hover {
background: var(--hover-bg-color);
}
`;
const StyledMenuInput = styled.input`
opacity: 0;
width: 0;
height: 0;
`;
/**
* This is the Session Menu Botton. i.e. the button on top of the conversation list to start a new conversation.
* It has two state: selected or not and so we use an checkbox input to keep the state in sync.
@ -54,13 +50,14 @@ export const MenuButton = () => {
'--fg-color': 'white',
} as CSSProperties
}
onClick={onClickFn}
>
<StyledMenuInput type="checkbox" checked={isToggled} onClick={onClickFn} />
<SessionIcon
iconSize="small"
iconType="plusFat"
iconColor="var(--fg-color)"
iconRotation={isToggled ? 45 : 0}
aria-label={window.i18n('chooseAnAction')}
/>
</StyledMenuButton>
);

@ -54,6 +54,8 @@ import { ConversationMessageRequestButtons } from './ConversationRequestButtons'
import { ConversationRequestinfo } from './ConversationRequestInfo';
import { getCurrentRecoveryPhrase } from '../../util/storage';
import loadImage from 'blueimp-load-image';
import { SessionSpinner } from '../basic/SessionSpinner';
import styled from 'styled-components';
// tslint:disable: jsx-curly-spacing
interface State {
@ -78,8 +80,25 @@ interface Props {
lightBoxOptions?: LightBoxOptions;
stagedAttachments: Array<StagedAttachmentType>;
isSelectedConvoInitialLoadingInProgress: boolean;
}
const StyledSpinnerContainer = styled.div`
display: flex;
justify-content: center;
width: 100%;
height: 100%;
align-items: center;
`;
const ConvoLoadingSpinner = () => {
return (
<StyledSpinnerContainer>
<SessionSpinner loading={true} />
</StyledSpinnerContainer>
);
};
export class SessionConversation extends React.Component<Props, State> {
private readonly messageContainerRef: React.RefObject<HTMLDivElement>;
private dragCounter: number;
@ -219,6 +238,7 @@ export class SessionConversation extends React.Component<Props, State> {
selectedMessages,
isRightPanelShowing,
lightBoxOptions,
isSelectedConvoInitialLoadingInProgress,
} = this.props;
if (!selectedConversation || !messagesProps) {
@ -233,46 +253,55 @@ export class SessionConversation extends React.Component<Props, State> {
<div className="conversation-header">
<ConversationHeaderWithDetails />
</div>
<div
// if you change the classname, also update it on onKeyDown
className={classNames('conversation-content', selectionMode && 'selection-mode')}
tabIndex={0}
onKeyDown={this.onKeyDown}
role="navigation"
>
<div className={classNames('conversation-info-panel', showMessageDetails && 'show')}>
<MessageDetail />
</div>
{lightBoxOptions?.media && this.renderLightBox(lightBoxOptions)}
<div className="conversation-messages">
<ConversationMessageRequestButtons />
<SplitViewContainer
top={<InConversationCallContainer />}
bottom={
<SessionMessagesListContainer
messageContainerRef={this.messageContainerRef}
scrollToNow={this.scrollToNow}
{isSelectedConvoInitialLoadingInProgress ? (
<ConvoLoadingSpinner />
) : (
<>
<div
// if you change the classname, also update it on onKeyDown
className={classNames('conversation-content', selectionMode && 'selection-mode')}
tabIndex={0}
onKeyDown={this.onKeyDown}
role="navigation"
>
<div className={classNames('conversation-info-panel', showMessageDetails && 'show')}>
<MessageDetail />
</div>
{lightBoxOptions?.media && this.renderLightBox(lightBoxOptions)}
<div className="conversation-messages">
<ConversationMessageRequestButtons />
<SplitViewContainer
top={<InConversationCallContainer />}
bottom={
<SessionMessagesListContainer
messageContainerRef={this.messageContainerRef}
scrollToNow={this.scrollToNow}
/>
}
disableTop={!this.props.hasOngoingCallWithFocusedConvo}
/>
}
disableTop={!this.props.hasOngoingCallWithFocusedConvo}
/>
{isDraggingFile && <SessionFileDropzone />}
</div>
<ConversationRequestinfo />
<CompositionBox
sendMessage={this.sendMessageFn}
stagedAttachments={this.props.stagedAttachments}
onChoseAttachments={this.onChoseAttachments}
/>
</div>
<div
className={classNames('conversation-item__options-pane', isRightPanelShowing && 'show')}
>
<SessionRightPanelWithDetails />
</div>
{isDraggingFile && <SessionFileDropzone />}
</div>
<ConversationRequestinfo />
<CompositionBox
sendMessage={this.sendMessageFn}
stagedAttachments={this.props.stagedAttachments}
onChoseAttachments={this.onChoseAttachments}
/>
</div>
<div
className={classNames(
'conversation-item__options-pane',
isRightPanelShowing && 'show'
)}
>
<SessionRightPanelWithDetails />
</div>
</>
)}
</SessionTheme>
);
}

@ -14,7 +14,7 @@ const StyledTypingContainer = styled.div`
const StyledTypingDot = styled.div<{ index: number }>`
border-radius: 50%;
background-color: var(--color-text-subtle);
background-color: var(--color-text-subtle); // TODO Theming update
height: 6px;
width: 6px;

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

@ -47,6 +47,25 @@ const StyledOnionDescription = styled.p`
width: 0;
`;
const StyledVerticalLine = styled.div`
background: rgba(#7a7a7a, 0.6);
position: absolute;
height: calc(100% - 2 * 15px);
margin: 15px calc(100% / 2 - 1px);
width: 1px;
`;
const StyledLightsContainer = styled.div`
position: relative;
`;
const StyledGrowingIcon = styled.div`
flex-grow: 1;
display: flex;
align-items: center;
`;
const OnionCountryDisplay = ({ labelText, snodeIp }: { snodeIp?: string; labelText: string }) => {
const element = (hovered: boolean) => (
<StyledCountry>{hovered && snodeIp ? snodeIp : labelText}</StyledCountry>
@ -120,25 +139,6 @@ const OnionPathModalInner = () => {
);
};
const StyledVerticalLine = styled.div`
background: rgba(#7a7a7a, 0.6);
position: absolute;
height: calc(100% - 2 * 15px);
margin: 15px calc(100% / 2 - 1px);
width: 1px;
`;
const StyledLightsContainer = styled.div`
position: relative;
`;
const StyledGrowingIcon = styled.div`
flex-grow: 1;
display: flex;
align-items: center;
`;
export type OnionNodeStatusLightType = {
glowStartDelay: number;
glowDuration: number;

@ -131,6 +131,7 @@ const Svg = React.memo(styled.svg<StyledSvgProps>`
border-radius: ${props => (props.borderRadius ? props.borderRadius : '')};
filter: ${props => (props.noScale ? `drop-shadow(0px 0px 4px ${props.iconColor})` : '')};
padding: ${props => (props.iconPadding ? props.iconPadding : '')};
transition: inherit;
`);
//tslint:enable no-unnecessary-callback-wrapper

@ -1,6 +1,7 @@
import React from 'react';
import { useSelector } from 'react-redux';
import styled from 'styled-components';
import { SectionType } from '../../state/ducks/section';
import { SessionTheme } from '../../themes/SessionTheme';
import { getLeftPaneLists } from '../../state/selectors/conversations';
@ -11,6 +12,11 @@ import { ActionsPanel } from './ActionsPanel';
import { LeftPaneMessageSection } from './LeftPaneMessageSection';
import { LeftPaneSettingSection } from './LeftPaneSettingSection';
export const leftPaneListWidth = 300;
const StyledLeftPane = styled.div`
width: ${leftPaneListWidth}px;
`;
const InnerLeftPaneMessageSection = () => {
const showSearch = useSelector(isSearching);
@ -51,9 +57,9 @@ export const LeftPane = () => {
<div className="module-left-pane-session">
<ActionsPanel />
<div className="module-left-pane">
<StyledLeftPane className="module-left-pane">
<LeftPaneSection />
</div>
</StyledLeftPane>
</div>
</SessionTheme>
);

@ -42,6 +42,13 @@ const StyledConversationListContent = styled.div`
background: var(--color-conversation-list);
`;
const StyledLeftPaneContent = styled.div`
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
`;
const ClosableOverlay = () => {
const overlayMode = useSelector(getOverlayMode);
@ -146,10 +153,3 @@ export class LeftPaneMessageSection extends React.Component<Props> {
);
}
}
const StyledLeftPaneContent = styled.div`
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
`;

@ -18,6 +18,7 @@ import styled from 'styled-components';
import { SessionSearchInput } from '../../SessionSearchInput';
import { getSearchResults, isSearching } from '../../../state/selectors/search';
import { useSet } from '../../../hooks/useSet';
import { VALIDATION } from '../../../session/constants';
const StyledMemberListNoContacts = styled.div`
font-family: var(--font-font-mono);
@ -90,6 +91,8 @@ export const OverlayClosedGroup = () => {
}
const contactsToRender = isSearch ? sharedWithResults : privateContactsPubkeys;
const disableCreateButton = !selectedMemberIds.length && !groupName.length;
return (
<div className="module-left-pane-overlay">
<OverlayHeader title={title} subtitle={subtitle} />
@ -99,7 +102,7 @@ export const OverlayClosedGroup = () => {
placeholder={placeholder}
value={groupName}
isGroup={true}
maxLength={100}
maxLength={VALIDATION.MAX_GROUP_NAME_LENGTH}
onChange={setGroupName}
onPressEnter={onEnterPressed}
dataTestId="new-closed-group-name"
@ -129,13 +132,13 @@ export const OverlayClosedGroup = () => {
)}
</StyledGroupMemberListContainer>
<SpacerLG />
<SpacerLG style={{ flexShrink: 0 }} />
<SessionButton
buttonColor={SessionButtonColor.Green}
buttonType={SessionButtonType.BrandOutline}
text={buttonText}
disabled={noContactsForClosedGroup}
disabled={disableCreateButton}
onClick={onEnterPressed}
dataTestId="next-button"
margin="auto 0 var(--margins-lg) 0 " // just to keep that button at the bottom of the overlay (even with an empty list)

@ -7,17 +7,28 @@ 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 {
markConversationInitialLoadingInProgress,
openConversationWithMessages,
} from '../../../state/ducks/conversations';
async function joinOpenGroup(serverUrl: string) {
async function joinOpenGroup(
serverUrl: string,
uiCallback?: (args: JoinSogsRoomUICallbackArgs) => void
) {
// guess if this is an open
if (serverUrl.match(openGroupV2CompleteURLRegex)) {
const groupCreated = await joinOpenGroupV2WithUIEvents(serverUrl, true, false);
const groupCreated = await joinOpenGroupV2WithUIEvents(serverUrl, true, false, uiCallback);
return groupCreated;
} else {
ToastUtils.pushToastError('invalidOpenGroupUrl', window.i18n('invalidOpenGroupUrl'));
@ -31,20 +42,18 @@ export const OverlayCommunity = () => {
const [loading, setLoading] = useState(false);
const [groupUrl, setGroupUrl] = useState('');
const overlayModeIsCommunity = useSelector(getOverlayMode) === 'open-group';
function closeOverlay() {
dispatch(resetOverlayMode());
}
async function onEnterPressed() {
async function onTryJoinRoom(completeUrl?: string) {
try {
if (loading) {
return;
}
setLoading(true);
const groupCreated = await joinOpenGroup(groupUrl);
if (groupCreated) {
closeOverlay();
}
await joinOpenGroup(completeUrl || groupUrl, joinSogsUICallback);
} catch (e) {
window.log.warn(e);
} finally {
@ -52,7 +61,22 @@ export const OverlayCommunity = () => {
}
}
// FIXME autofocus inputref on mount
function joinSogsUICallback(args: JoinSogsRoomUICallbackArgs) {
setLoading(args.loadingState === 'started');
if (args.conversationKey) {
dispatch(
markConversationInitialLoadingInProgress({
conversationKey: args.conversationKey,
isInitialFetchingInProgress: true,
})
);
}
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');
@ -72,20 +96,20 @@ export const OverlayCommunity = () => {
isGroup={true}
maxLength={300}
onChange={setGroupUrl}
onPressEnter={onEnterPressed}
onPressEnter={onTryJoinRoom}
/>
</div>
<SessionButton
buttonColor={SessionButtonColor.Green}
buttonType={SessionButtonType.BrandOutline}
text={buttonText}
disabled={!groupUrl}
onClick={onTryJoinRoom}
/>
<SessionSpinner loading={loading} />
<SessionJoinableRooms onRoomClicked={closeOverlay} />
{groupUrl && (
<SessionButton
buttonColor={SessionButtonColor.Green}
buttonType={SessionButtonType.BrandOutline}
text={buttonText}
onClick={onEnterPressed}
/>
)}
<SessionJoinableRooms onJoinClick={onTryJoinRoom} alreadyJoining={loading} />
</div>
);
};

@ -45,13 +45,15 @@ export const OverlayMessage = () => {
const subtitle = window.i18n('enterSessionIDOrONSName');
const placeholder = window.i18n('enterSessionIDOfRecipient');
const disableNextButton = !pubkeyOrOns || loading;
async function openConvoOnceResolved(resolvedSessionID: string) {
const convo = await getConversationController().getOrCreateAndWait(
resolvedSessionID,
ConversationTypeEnum.PRIVATE
);
// we now want to show a conversation we just started on the leftpane, even if we did not sent a message to it yet
// we now want to show a conversation we just started on the leftpane, even if we did not send a message to it yet
if (!convo.isActive() || !convo.isApproved()) {
convo.set({ active_at: Date.now(), isApproved: true });
await convo.commit();
@ -105,6 +107,7 @@ export const OverlayMessage = () => {
placeholder={placeholder}
onChange={setPubkeyOrOns}
dataTestId="new-session-conversation"
onPressEnter={handleMessageButtonClick}
/>
<SessionSpinner loading={loading} />
@ -118,7 +121,8 @@ export const OverlayMessage = () => {
container={true}
justifyContent="space-between"
alignItems="center"
padding="0 15px 0 0 " // YourSessionIDSelectable already has a left margin of 15px
width="100%"
padding="0 var(--margins-md)" // YourSessionIDSelectable already has a left margin of 15px
>
<YourSessionIDSelectable />
<SessionIconButton iconSize="small" iconType="copy" onClick={copyOurSessionID} />
@ -127,7 +131,7 @@ export const OverlayMessage = () => {
buttonColor={SessionButtonColor.Green}
buttonType={SessionButtonType.BrandOutline}
text={buttonText}
disabled={false}
disabled={disableNextButton}
onClick={handleMessageButtonClick}
dataTestId="next-new-conversation-button"
/>

@ -1,17 +1,14 @@
import React, { useCallback, useEffect } from 'react';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import {
joinOpenGroupV2WithUIEvents,
parseOpenGroupV2,
} from '../../../session/apis/open_group_api/opengroupV2/JoinOpenGroupV2';
import { parseOpenGroupV2 } from '../../../session/apis/open_group_api/opengroupV2/JoinOpenGroupV2';
import { sogsV3FetchPreviewBase64 } from '../../../session/apis/open_group_api/sogsv3/sogsV3FetchFile';
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 +18,7 @@ export type JoinableRoomProps = {
name: string;
roomId: string;
imageId?: string;
onClick: (completeUrl: string) => void;
onClick?: (completeUrl: string) => void;
base64Data?: string;
};
@ -77,7 +74,7 @@ const SessionJoinableRoomAvatar = (props: JoinableRoomProps) => {
base64Data={props.base64Data}
{...props}
pubkey=""
onAvatarClick={() => props.onClick(props.completeUrl)}
onAvatarClick={() => props.onClick?.(props.completeUrl)}
/>
);
};
@ -94,64 +91,68 @@ 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: {
alreadyJoining: boolean;
onJoinClick?: (completeUrl: string) => void;
}) => {
const joinableRooms = useSelector((state: StateType) => state.defaultRooms);
const onRoomClicked = useCallback(
(loading: boolean) => {
if (loading) {
props.onRoomClicked();
}
},
[props.onRoomClicked]
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={props.onJoinClick}
/>
);
})}
</>
);
};
export const SessionJoinableRooms = (props: {
onJoinClick?: (completeUrl: string) => 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>
);

@ -41,7 +41,7 @@ const StyledContactRowName = styled.div`
font-size: var(--font-size-lg);
`;
const StyledRowContainer = styled.div`
const StyledRowContainer = styled.button`
display: flex;
align-items: center;
padding: 0 var(--margins-lg);
@ -64,7 +64,7 @@ const StyledBreak = styled.div`
padding: 0 var(--margins-lg);
color: var(--color-text-subtle);
font-size: var(--font-size-md);
height: 25px; // should also be changed in rowHeight
height: 30px; // should also be changed in rowHeight
border-bottom: 1px var(--color-session-border) solid;
`;

@ -8,6 +8,7 @@ import {
getDirectContactsByName,
getDirectContactsCount,
} from '../../../../state/selectors/conversations';
import { leftPaneListWidth } from '../../LeftPane';
import { StyledLeftPaneList } from '../../LeftPaneList';
import { ContactRow, ContactRowBreak } from './ContactRow';
import { StyledChooseActionTitle } from './OverlayChooseAction';
@ -93,11 +94,11 @@ const ContactListItemSection = () => {
rowCount={length}
rowHeight={
(params: Index) =>
isString(directContactsByNameWithBreaks[params.index]) ? 25 : 64 // should also be changed in `ContactRowBreak`
isString(directContactsByNameWithBreaks[params.index]) ? 30 : 64 // should also be changed in `ContactRowBreak`
}
directContactsByNameWithBreaks={directContactsByNameWithBreaks}
rowRenderer={renderRow}
width={300} // the same as session-left-pane-width
width={leftPaneListWidth}
autoHeight={false}
/>
);
@ -113,7 +114,9 @@ const ContactsTitle = () => {
return null;
}
return <StyledChooseActionTitle>{window.i18n('contactsHeader')}</StyledChooseActionTitle>;
return (
<StyledChooseActionTitle tabIndex={0}>{window.i18n('contactsHeader')}</StyledChooseActionTitle>
);
};
export const ContactsListWithBreaks = () => {

@ -66,15 +66,27 @@ export const OverlayChooseAction = () => {
return (
<div className="module-left-pane-overlay">
<StyledActionRow onClick={openNewMessage} data-testid="chooser-new-conversation-button">
<StyledActionRow
onClick={openNewMessage}
data-testid="chooser-new-conversation-button"
aria-label={window.i18n('createConversationNewContact')}
>
<IconOnActionRow iconType="chatBubble" />
<StyledChooseActionTitle>{window.i18n('newMessage')}</StyledChooseActionTitle>
</StyledActionRow>
<StyledActionRow onClick={openCreateGroup} data-testid="chooser-new-group">
<StyledActionRow
onClick={openCreateGroup}
data-testid="chooser-new-group"
aria-label={window.i18n('createConversationNewGroup')}
>
<IconOnActionRow iconType="group" />
<StyledChooseActionTitle>{window.i18n('createGroup')}</StyledChooseActionTitle>
</StyledActionRow>
<StyledActionRow onClick={openJoinCommunity} data-testid="chooser-new-community">
<StyledActionRow
onClick={openJoinCommunity}
data-testid="chooser-new-community"
aria-label={window.i18n('joinACommunity')}
>
<IconOnActionRow iconType="communities" />
<StyledChooseActionTitle>{window.i18n('joinOpenGroup')}</StyledChooseActionTitle>
</StyledActionRow>

@ -2,7 +2,7 @@ import React, { useState } from 'react';
// tslint:disable-next-line: no-submodule-imports
import useUpdate from 'react-use/lib/useUpdate';
import styled, { CSSProperties } from 'styled-components';
import styled from 'styled-components';
import { useSet } from '../../hooks/useSet';
import { ToastUtils } from '../../session/utils';
import { BlockedNumberController } from '../../util';
@ -18,14 +18,14 @@ const BlockedEntriesContainer = styled.div`
overflow: auto;
min-height: 40px;
max-height: 100%;
background: var(--blocked-contact-list-bg);
background: var(--color-input-background); // TODO theming update
`;
const BlockedEntriesRoundedContainer = styled.div`
overflow: hidden;
border-radius: 16px;
padding: var(--margins-lg);
background: var(--blocked-contact-list-bg);
background: var(--color-input-background); // TODO theming update
`;
const BlockedContactsSection = styled.div`
@ -119,9 +119,7 @@ export const BlockedContactsList = () => {
}
return (
<BlockedContactsSection
style={{ '--blocked-contact-list-bg': 'var(--color-input-background)' } as CSSProperties}
>
<BlockedContactsSection>
<StyledBlockedSettingItem clickable={!noBlockedNumbers}>
<BlockedContactListTitle onClick={toggleUnblockList}>
<SettingsTitleAndDescription title={window.i18n('blockedSettingsTitle')} />

@ -57,7 +57,10 @@ export const SessionNotificationGroupSettings = (props: { hasPassword: boolean |
Notifications.addNotification(
{
conversationId: `preview-notification-${Date.now()}`,
message: items.find(m => m.value === initialItem)?.label || 'Message body',
message:
items.find(m => m.value === initialItem)?.label ||
window?.i18n?.('messageBody') ||
'Message body',
title: window.i18n('notificationPreview'),
iconUrl: null,
isExpiringMessage: false,

@ -163,7 +163,7 @@ const SettingInCategory = (props: {
case SessionSettingCategory.Permissions:
return <SettingsCategoryPermissions hasPassword={hasPassword} />;
// those three down there have no options, they are just a button
// these three down there have no options, they are just a button
case SessionSettingCategory.ClearData:
case SessionSettingCategory.MessageRequests:
case SessionSettingCategory.RecoveryPhrase:

@ -1,6 +1,5 @@
import React from 'react';
import styled from 'styled-components';
import { LocalizerKeys } from '../../types/LocalizerKeys';
import { missingCaseError } from '../../util';
import { SessionSettingCategory, SettingsViewProps } from './SessionSettings';
@ -26,32 +25,30 @@ const StyledHeaderTittle = styled.div`
export const SettingsHeader = (props: Props) => {
const { category } = props;
let categoryLocalized: LocalizerKeys | null = null;
let categoryTitle: string | null = null;
switch (category) {
case SessionSettingCategory.Appearance:
categoryLocalized = 'appearanceSettingsTitle';
categoryTitle = window.i18n('appearanceSettingsTitle');
break;
case SessionSettingCategory.Conversations:
categoryLocalized = 'conversationsSettingsTitle';
categoryTitle = window.i18n('conversationsSettingsTitle');
break;
case SessionSettingCategory.Notifications:
categoryLocalized = 'notificationsSettingsTitle';
categoryTitle = window.i18n('notificationsSettingsTitle');
break;
case SessionSettingCategory.Help:
categoryLocalized = 'helpSettingsTitle';
categoryTitle = window.i18n('helpSettingsTitle');
break;
case SessionSettingCategory.Permissions:
categoryLocalized = 'permissionsSettingsTitle';
categoryTitle = window.i18n('permissionsSettingsTitle');
break;
case SessionSettingCategory.Privacy:
categoryLocalized = 'privacySettingsTitle';
categoryTitle = window.i18n('privacySettingsTitle');
break;
default:
throw missingCaseError('SettingsHeader' as never);
}
const categoryTitle = window.i18n(categoryLocalized);
return (
<StyledSettingsHeader>
<StyledHeaderTittle>{categoryTitle}</StyledHeaderTittle>

@ -159,7 +159,7 @@ const Themes = () => {
};
export const SettingsThemeSwitcher = () => {
//FIXME store that value somewhere in the theme object
//TODO Theming
const [selectedAccent, setSelectedAccent] = useState<PrimaryColorIds | undefined>(undefined);
return (

@ -19,7 +19,7 @@ export const SettingsCategoryAppearance = (props: { hasPassword: boolean | null
return (
<>
<SettingsThemeSwitcher />
{window.sessionFeatureFlags.useSettingsThemeSwitcher && <SettingsThemeSwitcher />}
<ZoomingSessionSlider />
{isHideMenuBarSupported() && (
<SessionToggleWithDescription

@ -84,8 +84,6 @@ let newVersion = false;
window.document.title = window.getTitle();
// Whisper.events =
// window.Whisper.events = WhisperEvents ?
const WhisperEvents = _.clone(Backbone.Events);
window.Whisper = window.Whisper || {};
window.Whisper.events = WhisperEvents;

@ -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;
/**

@ -103,6 +103,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 +126,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,9 +147,9 @@ 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);
const isConvoCreated = getConversationController().get(conversationID);
@ -155,21 +160,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;
}

@ -20,6 +20,10 @@ import {
roomHasBlindEnabled,
} from '../sogsv3/sogsV3Capabilities';
import { OpenGroupReaction } from '../../../../types/Reaction';
import {
markConversationInitialLoadingInProgress,
openConversationWithMessages,
} from '../../../../state/ducks/conversations';
export type OpenGroupMessageV4 = {
/** AFAIK: indicates the number of the message in the group. e.g. 2nd message will be 1 or 2 */
@ -317,6 +321,36 @@ export class OpenGroupServerPoller {
// ==> At this point all those results need to trigger conversation updates, so update what we have to update
await handleBatchPollResults(this.serverUrl, batchPollResults, subrequestOptions);
for (const room of subrequestOptions) {
if (room.type === 'messages' && !room.messages?.sinceSeqNo && room.messages?.roomId) {
const conversationKey = getOpenGroupV2ConversationId(
this.serverUrl,
room.messages.roomId
);
global.setTimeout(() => {
const stateConversations = window.inboxStore?.getState().conversations;
if (
stateConversations.conversationLookup?.[conversationKey]?.isInitialFetchingInProgress
) {
if (
stateConversations.selectedConversation &&
conversationKey === stateConversations.selectedConversation
) {
void openConversationWithMessages({ conversationKey, messageId: null }).then(() => {
window.inboxStore?.dispatch(
markConversationInitialLoadingInProgress({
conversationKey,
isInitialFetchingInProgress: false,
})
);
});
}
}
}, 5000);
}
}
} catch (e) {
window?.log?.warn('Got error while compact fetch:', e.message);
} finally {

@ -45,7 +45,7 @@ export const CONVERSATION = {
export const MAX_ATTACHMENT_FILESIZE_BYTES = 6 * 1000 * 1000; // 6MB
export const VALIDATION = {
MAX_GROUP_NAME_LENGTH: 64,
MAX_GROUP_NAME_LENGTH: 30,
CLOSED_GROUP_SIZE_LIMIT: 100,
};

@ -264,6 +264,7 @@ export interface ReduxConversationType {
currentNotificationSetting?: ConversationNotificationSettingType;
isPinned?: boolean;
isInitialFetchingInProgress?: boolean;
isApproved?: boolean;
didApproveMe?: boolean;
@ -717,10 +718,6 @@ const conversationsSlice = createSlice({
initialMessages: Array<MessageModelPropsWithoutConvoProps>;
}>
) {
if (state.selectedConversation === action.payload.conversationKey) {
return state;
}
// this is quite hacky, but we don't want to show the showScrollButton if we have only a small amount of messages,
// or if the first unread message is not far from the most recent one.
// this is because when a new message get added, we do not add it to redux depending on the showScrollButton state.
@ -835,6 +832,20 @@ const conversationsSlice = createSlice({
state.mentionMembers = action.payload;
return state;
},
markConversationInitialLoadingInProgress(
state: ConversationsStateType,
action: PayloadAction<{ conversationKey: string; isInitialFetchingInProgress: boolean }>
) {
window?.log?.info(
`mark conversation initialLoading ${action.payload.conversationKey}: ${action.payload.isInitialFetchingInProgress}`
);
if (state.conversationLookup[action.payload.conversationKey]) {
state.conversationLookup[action.payload.conversationKey].isInitialFetchingInProgress =
action.payload.isInitialFetchingInProgress;
}
return state;
},
},
extraReducers: (builder: any) => {
// Add reducers for additional action types here, and handle loading state as needed
@ -945,7 +956,7 @@ function applyConversationChanged(
selectedConversation,
conversationLookup: {
...conversationLookup,
[id]: data,
[id]: { ...data, isInitialFetchingInProgress: existing.isInitialFetchingInProgress },
},
};
}
@ -981,6 +992,7 @@ export const {
setNextMessageToPlayId,
updateMentionsMembers,
resetConversationExternal,
markConversationInitialLoadingInProgress,
} = actions;
export async function openConversationWithMessages(args: {

@ -92,6 +92,7 @@ export const actions = {
export const initialSectionState: SectionStateType = {
focusedSection: SectionType.Message,
focusedSettingsSection: undefined,
isAppFocused: false,
overlayMode: undefined,
};

@ -1172,3 +1172,8 @@ export const getOldBottomMessageId = createSelector(
getConversations,
(state: ConversationsStateType): string | null => state.oldBottomMessageId || null
);
export const getIsSelectedConvoInitialLoadingInProgress = createSelector(
getSelectedConversation,
(convo: ReduxConversationType | undefined): boolean => Boolean(convo?.isInitialFetchingInProgress)
);

@ -3,6 +3,7 @@ import { mapDispatchToProps } from '../actions';
import { StateType } from '../reducer';
import { getTheme } from '../selectors/theme';
import {
getIsSelectedConvoInitialLoadingInProgress,
getLightBoxOptions,
getSelectedConversation,
getSelectedConversationKey,
@ -29,6 +30,7 @@ const mapStateToProps = (state: StateType) => {
lightBoxOptions: getLightBoxOptions(state),
stagedAttachments: getStagedAttachmentsForCurrentConversation(state),
hasOngoingCallWithFocusedConvo: getHasOngoingCallWithFocusedConvo(state),
isSelectedConvoInitialLoadingInProgress: getIsSelectedConvoInitialLoadingInProgress(state),
};
};

@ -74,7 +74,6 @@ const darkFilterSessionText = 'none';
const darkUnreadBorder = `4px solid ${accentDarkTheme}`;
const darkScrollbarThumb = darkGrayColor;
const darkScrollbarTrack = '#1b1b1b';
const darkFakeChatBubbleBg = '#212121';
const darkInboxBackground = '#171717';
@ -150,7 +149,6 @@ export const switchHtmlToDarkTheme = () => {
document.documentElement.style.setProperty('--border-unread', darkUnreadBorder);
document.documentElement.style.setProperty('--color-scroll-bar-thumb', darkScrollbarThumb);
document.documentElement.style.setProperty('--color-scroll-bar-track', darkScrollbarTrack);
document.documentElement.style.setProperty(
'--color-fake-chat-bubble-background',
darkFakeChatBubbleBg
@ -225,7 +223,6 @@ const lightFilterSessionText = 'brightness(0) saturate(100%)';
const lightUnreadBorder = `4px solid ${accentLightTheme}`;
const lightScrollbarThumb = darkGrayColor;
const lightScrollbarTrack = '#fcfcfc';
const lightFakeChatBubbleBg = '#f5f5f5';
const lightInboxBackground = whiteColor;
@ -307,7 +304,6 @@ export const switchHtmlToLightTheme = () => {
document.documentElement.style.setProperty('--border-unread', lightUnreadBorder);
document.documentElement.style.setProperty('--color-scroll-bar-thumb', lightScrollbarThumb);
document.documentElement.style.setProperty('--color-scroll-bar-track', lightScrollbarTrack);
document.documentElement.style.setProperty(
'--color-fake-chat-bubble-background',
lightFakeChatBubbleBg
@ -445,7 +441,6 @@ export const SessionGlobalStyles = createGlobalStyle`
--color-pill-divider-text: ${lightColorPillDividerText};
--color-input-background: ${lightInputBackground};
--color-scroll-bar-thumb: ${lightScrollbarThumb};
--color-scroll-bar-track: ${lightScrollbarTrack};
--color-fake-chat-bubble-background: ${lightFakeChatBubbleBg};
--color-inbox-background: ${lightInboxBackground};
--color-left-pane-overlay-background: ${lightLeftPaneOverlayBg};

@ -67,6 +67,7 @@ export type LocalizerKeys =
| 'timerOption_1_hour'
| 'youGotKickedFromGroup'
| 'cannotRemoveCreatorFromGroupDesc'
| 'contactAvatarAlt'
| 'incomingError'
| 'notificationsSettingsTitle'
| 'conversationsSettingsTitle'
@ -75,6 +76,7 @@ export type LocalizerKeys =
| 'from'
| 'requestsSubtitle'
| 'thisMonth'
| 'chooseAnAction'
| 'next'
| 'addModerators'
| 'sessionMessenger'
@ -122,7 +124,6 @@ export type LocalizerKeys =
| 'deleteJustForMe'
| 'changeAccountPasswordTitle'
| 'onionPathIndicatorDescription'
| 'pruningOpengroupDialogSubMessage'
| 'mediaPermissionsTitle'
| 'replyingToMessage'
| 'welcomeToYourSession'
@ -176,6 +177,7 @@ export type LocalizerKeys =
| 'copyOpenGroupURL'
| 'setPasswordInvalid'
| 'timerOption_30_seconds_abbreviated'
| 'createConversationNewContact'
| 'removeResidueMembers'
| 'areYouSureDeleteEntireAccount'
| 'noGivenPassword'
@ -256,7 +258,6 @@ export type LocalizerKeys =
| 'goToSupportPage'
| 'passwordsDoNotMatch'
| 'createClosedGroupNamePrompt'
| 'pruningOpengroupDialogMessage'
| 'audioMessageAutoplayDescription'
| 'leaveAndRemoveForEveryone'
| 'previewThumbnail'
@ -267,6 +268,7 @@ export type LocalizerKeys =
| 'reactionPopupOne'
| 'imageCaptionIconAlt'
| 'sendRecoveryPhraseTitle'
| 'joinACommunity'
| 'multipleJoinedTheGroup'
| 'messageRequestAcceptedOursNoName'
| 'databaseError'
@ -335,8 +337,7 @@ export type LocalizerKeys =
| 'respondingToRequestWarning'
| 'error'
| 'clearAllData'
| 'pruningOpengroupDialogTitle'
| 'contactAvatarAlt'
| 'createConversationNewGroup'
| 'disappearingMessages'
| 'autoUpdateNewVersionTitle'
| 'linkPreviewDescription'
@ -390,6 +391,7 @@ export type LocalizerKeys =
| 'changeAccountPasswordDescription'
| 'notificationSettingsDialog'
| 'invalidOldPassword'
| 'messageBody'
| 'audioMessageAutoplayTitle'
| 'removePasswordInvalid'
| 'password'

@ -88,7 +88,7 @@ export class BlockedNumberController {
}
/**
* Unblock all thope users.
* Unblock all these users.
* This will only unblock the primary device of the user.
*
* @param user The user to unblock.

6
ts/window.d.ts vendored

@ -6,6 +6,7 @@ import { Store } from 'redux';
import { ConversationCollection, ConversationModel } from './models/conversation';
import { ConversationType } from './state/ducks/conversations';
import { StateType } from './state/reducer';
export interface LibTextsecure {
messaging: boolean;
@ -21,11 +22,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;
@ -38,6 +35,7 @@ declare global {
sessionFeatureFlags: {
useOnionRequests: boolean;
useTestNet: boolean;
useSettingsThemeSwitcher: boolean;
debug: {
debugFileServerRequests: boolean;
debugNonSnodeRequests: boolean;

Loading…
Cancel
Save