refactor most of the components to outside of their Session folder (#2072)
* refactor most of the components to outside of their Session folder * finish moving overlay and memberListItem to react hook * fix bug with kicked member len >2 not being displayed also sort admins first in UpdateGroupMembers dialog * fix admin leaving text of groupNotification * add a useFocusMount hook to focus input fields on mount * make click avatar convo item open only user dialog * cleanup config default.json * make sure to use convoController to build sync message * disable showing pubkey on opengroups * add a pause on audio playback Fixes #2079
This commit is contained in:
parent
95e40c9509
commit
28c7445dce
10
Gruntfile.js
10
Gruntfile.js
|
@ -151,14 +151,6 @@ module.exports = grunt => {
|
|||
grunt.file.write(configPath, `${JSON.stringify(localConfig)}\n`);
|
||||
}
|
||||
|
||||
grunt.registerTask('getExpireTime', () => {
|
||||
grunt.task.requires('gitinfo');
|
||||
const gitinfo = grunt.config.get('gitinfo');
|
||||
const committed = gitinfo.local.branch.current.lastCommitTime;
|
||||
const time = Date.parse(committed) + 1000 * 60 * 60 * 24 * 90;
|
||||
updateLocalConfig({ buildExpiration: time });
|
||||
});
|
||||
|
||||
grunt.registerTask('getCommitHash', () => {
|
||||
grunt.task.requires('gitinfo');
|
||||
const gitinfo = grunt.config.get('gitinfo');
|
||||
|
@ -167,7 +159,7 @@ module.exports = grunt => {
|
|||
});
|
||||
|
||||
grunt.registerTask('dev', ['default', 'watch']);
|
||||
grunt.registerTask('date', ['gitinfo', 'getExpireTime']);
|
||||
grunt.registerTask('date', ['gitinfo']);
|
||||
grunt.registerTask('default', [
|
||||
'exec:build-protobuf',
|
||||
'exec:transpile',
|
||||
|
|
|
@ -327,7 +327,7 @@
|
|||
"addAsModerator": "Add as Moderator",
|
||||
"removeFromModerators": "Remove From Moderators",
|
||||
"add": "Add",
|
||||
"addingContacts": "Adding contacts to",
|
||||
"addingContacts": "Adding contacts to $name$",
|
||||
"noContactsToAdd": "No contacts to add",
|
||||
"noMembersInThisGroup": "No other members in this group",
|
||||
"noModeratorsToRemove": "no moderators to remove",
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
],
|
||||
"updatesEnabled": false,
|
||||
"openDevTools": false,
|
||||
"buildExpiration": 0,
|
||||
"commitHash": "",
|
||||
"import": false,
|
||||
"serverTrustRoot": "BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx"
|
||||
"import": false
|
||||
}
|
||||
|
|
|
@ -8,12 +8,14 @@ const OS = require('../../ts/OS');
|
|||
const Settings = require('./settings');
|
||||
const Util = require('../../ts/util');
|
||||
const LinkPreviews = require('./link_previews');
|
||||
const { Message } = require('../../ts/components/conversation/Message');
|
||||
const { Message } = require('../../ts/components/conversation/message/message-item/Message');
|
||||
|
||||
// Components
|
||||
const { SessionRegistrationView } = require('../../ts/components/session/SessionRegistrationView');
|
||||
const {
|
||||
SessionRegistrationView,
|
||||
} = require('../../ts/components/registration/SessionRegistrationView');
|
||||
|
||||
const { SessionInboxView } = require('../../ts/components/session/SessionInboxView');
|
||||
const { SessionInboxView } = require('../../ts/components/SessionInboxView');
|
||||
|
||||
// Types
|
||||
const AttachmentType = require('./types/attachment');
|
||||
|
|
2
main.js
2
main.js
|
@ -154,7 +154,6 @@ function prepareURL(pathSegments, moreKeys) {
|
|||
name: packageJson.productName,
|
||||
locale: locale.name,
|
||||
version: app.getVersion(),
|
||||
buildExpiration: config.get('buildExpiration'),
|
||||
commitHash: config.get('commitHash'),
|
||||
serverUrl: config.get('serverUrl'),
|
||||
localUrl: config.get('localUrl'),
|
||||
|
@ -167,7 +166,6 @@ function prepareURL(pathSegments, moreKeys) {
|
|||
appInstance: process.env.NODE_APP_INSTANCE,
|
||||
proxyUrl: process.env.HTTPS_PROXY || process.env.https_proxy,
|
||||
contentProxyUrl: config.contentProxyUrl,
|
||||
serverTrustRoot: config.get('serverTrustRoot'),
|
||||
appStartInitialSpellcheckSetting,
|
||||
...moreKeys,
|
||||
},
|
||||
|
|
|
@ -21,7 +21,7 @@ window.getEnvironment = () => config.environment;
|
|||
window.getVersion = () => config.version;
|
||||
window.getAppInstance = () => config.appInstance;
|
||||
|
||||
const { SessionPasswordPrompt } = require('./ts/components/session/SessionPasswordPrompt');
|
||||
const { SessionPasswordPrompt } = require('./ts/components/SessionPasswordPrompt');
|
||||
|
||||
window.Signal = {
|
||||
Components: {
|
||||
|
|
|
@ -30,11 +30,9 @@ window.getEnvironment = () => config.environment;
|
|||
window.getAppInstance = () => config.appInstance;
|
||||
window.getVersion = () => config.version;
|
||||
window.isDev = () => config.environment === 'development';
|
||||
window.getExpiration = () => config.buildExpiration;
|
||||
window.getCommitHash = () => config.commitHash;
|
||||
window.getNodeVersion = () => config.node_version;
|
||||
window.getHostName = () => config.hostname;
|
||||
window.getServerTrustRoot = () => config.serverTrustRoot;
|
||||
window.isBehindProxy = () => Boolean(config.proxyUrl);
|
||||
|
||||
window.lokiFeatureFlags = {
|
||||
|
@ -214,7 +212,7 @@ window.Signal = Signal.setup({
|
|||
logger: window.log,
|
||||
});
|
||||
|
||||
window.getSwarmPollingInstance = require('./ts/session/snode_api/').getSwarmPollingInstance;
|
||||
window.getSwarmPollingInstance = require('./ts/session/apis/snode_api/').getSwarmPollingInstance;
|
||||
|
||||
const WorkerInterface = require('./js/modules/util_worker_interface');
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ $borderAvatarColor: unquote(
|
|||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
|
@ -16,21 +17,6 @@ $borderAvatarColor: unquote(
|
|||
}
|
||||
}
|
||||
|
||||
.module-avatar__label {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-weight: 300;
|
||||
text-transform: uppercase;
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
.module-avatar__icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.module-avatar__icon--crown-wrapper {
|
||||
position: absolute;
|
||||
bottom: 0%;
|
||||
|
@ -129,7 +115,8 @@ $borderAvatarColor: unquote(
|
|||
|
||||
.module-avatar-clickable {
|
||||
transition: $session-transition-duration;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: $session-subtle-factor;
|
||||
filter: grayscale(0.7);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,12 @@
|
|||
padding: 1.1em;
|
||||
}
|
||||
|
||||
.session-modal {
|
||||
.contact-selection-list {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.create-group-dialog,
|
||||
.add-moderators-dialog,
|
||||
.remove-moderators-dialog,
|
||||
|
|
|
@ -494,6 +494,7 @@ label {
|
|||
|
||||
.group-member-list__selection {
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__centered {
|
||||
|
|
|
@ -158,10 +158,8 @@
|
|||
z-index: 1;
|
||||
|
||||
.session-icon-button {
|
||||
// & > .session-icon-button {
|
||||
margin-right: $session-margin-sm;
|
||||
}
|
||||
.session-icon-button {
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
@ -169,7 +167,7 @@
|
|||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
transform: scale(0.93);
|
||||
filter: brightness(0.9);
|
||||
transition: $session-transition-duration;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@ import * as GoogleChrome from '../util/GoogleChrome';
|
|||
|
||||
import { AttachmentType } from '../types/Attachment';
|
||||
|
||||
import { SessionInput } from './session/SessionInput';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from './session/SessionButton';
|
||||
import autoBind from 'auto-bind';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from './basic/SessionButton';
|
||||
import { SessionInput } from './basic/SessionInput';
|
||||
|
||||
interface Props {
|
||||
attachment: AttachmentType;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
|
||||
import { ContactType } from './session/SessionMemberListItem';
|
||||
import { ToastUtils } from '../session/utils';
|
||||
import { createClosedGroup as createClosedGroupV2 } from '../receiver/closedGroups';
|
||||
import { VALIDATION } from '../session/constants';
|
||||
|
@ -38,7 +37,7 @@ export class MessageView extends React.Component {
|
|||
*/
|
||||
async function createClosedGroup(
|
||||
groupName: string,
|
||||
groupMembers: Array<ContactType>
|
||||
groupMemberIds: Array<string>
|
||||
): Promise<boolean> {
|
||||
// Validate groupName and groupMembers length
|
||||
if (groupName.length === 0) {
|
||||
|
@ -53,16 +52,14 @@ async function createClosedGroup(
|
|||
// >= because we add ourself as a member AFTER this. so a 10 group is already invalid as it will be 11 with ourself
|
||||
// the same is valid with groups count < 1
|
||||
|
||||
if (groupMembers.length < 1) {
|
||||
if (groupMemberIds.length < 1) {
|
||||
ToastUtils.pushToastError('pickClosedGroupMember', window.i18n('pickClosedGroupMember'));
|
||||
return false;
|
||||
} else if (groupMembers.length >= VALIDATION.CLOSED_GROUP_SIZE_LIMIT) {
|
||||
} else if (groupMemberIds.length >= VALIDATION.CLOSED_GROUP_SIZE_LIMIT) {
|
||||
ToastUtils.pushToastError('closedGroupMaxSize', window.i18n('closedGroupMaxSize'));
|
||||
return false;
|
||||
}
|
||||
|
||||
const groupMemberIds = groupMembers.map(m => m.id);
|
||||
|
||||
await createClosedGroupV2(groupName, groupMemberIds);
|
||||
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Avatar, AvatarSize } from './avatar/Avatar';
|
||||
import { Constants } from '../session';
|
||||
import { SessionIcon } from './icon';
|
||||
import { useConversationUsernameOrShorten } from '../hooks/useParamSelector';
|
||||
|
||||
const AvatarItem = (props: { memberPubkey: string }) => {
|
||||
return <Avatar size={AvatarSize.XS} pubkey={props.memberPubkey} />;
|
||||
};
|
||||
|
||||
export const MemberListItem = (props: {
|
||||
pubkey: string;
|
||||
isSelected: boolean;
|
||||
// this bool is used to make a zombie appear with less opacity than a normal member
|
||||
isZombie?: boolean;
|
||||
onSelect?: (pubkey: string) => void;
|
||||
onUnselect?: (pubkey: string) => void;
|
||||
}) => {
|
||||
const { isSelected, pubkey, isZombie, onSelect, onUnselect } = props;
|
||||
|
||||
const memberName = useConversationUsernameOrShorten(pubkey);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('session-member-item', isSelected && 'selected', isZombie && 'zombie')}
|
||||
onClick={() => {
|
||||
isSelected ? onUnselect?.(pubkey) : onSelect?.(pubkey);
|
||||
}}
|
||||
role="button"
|
||||
>
|
||||
<div className="session-member-item__info">
|
||||
<span className="session-member-item__avatar">
|
||||
<AvatarItem memberPubkey={pubkey} />
|
||||
</span>
|
||||
<span className="session-member-item__name">{memberName}</span>
|
||||
</div>
|
||||
<span className={classNames('session-member-item__checkmark', isSelected && 'selected')}>
|
||||
<SessionIcon iconType="check" iconSize="medium" iconColor={Constants.UI.COLORS.GREEN} />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,32 +1,32 @@
|
|||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { getConversationController } from '../../session/conversations';
|
||||
import { UserUtils } from '../../session/utils';
|
||||
import { createStore } from '../../state/createStore';
|
||||
import {
|
||||
actions as conversationActions,
|
||||
getEmptyConversationState,
|
||||
openConversationWithMessages,
|
||||
} from '../../state/ducks/conversations';
|
||||
import { initialDefaultRoomState } from '../../state/ducks/defaultRooms';
|
||||
import { initialModalState } from '../../state/ducks/modalDialog';
|
||||
import { initialOnionPathState } from '../../state/ducks/onion';
|
||||
import { initialSearchState } from '../../state/ducks/search';
|
||||
import { initialSectionState } from '../../state/ducks/section';
|
||||
import { initialThemeState } from '../../state/ducks/theme';
|
||||
import { initialUserConfigState } from '../../state/ducks/userConfig';
|
||||
import { StateType } from '../../state/reducer';
|
||||
import { makeLookup } from '../../util';
|
||||
import { LeftPane } from '../LeftPane';
|
||||
import { SessionMainPanel } from '../SessionMainPanel';
|
||||
import { LeftPane } from './leftpane/LeftPane';
|
||||
|
||||
// tslint:disable-next-line: no-submodule-imports
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
import { persistStore } from 'redux-persist';
|
||||
import { TimerOptionsArray } from '../../state/ducks/timerOptions';
|
||||
import { getEmptyStagedAttachmentsState } from '../../state/ducks/stagedAttachments';
|
||||
import { initialCallState } from '../../state/ducks/call';
|
||||
import { getConversationController } from '../session/conversations';
|
||||
import { UserUtils } from '../session/utils';
|
||||
import { initialCallState } from '../state/ducks/call';
|
||||
import {
|
||||
actions as conversationActions,
|
||||
getEmptyConversationState,
|
||||
openConversationWithMessages,
|
||||
} from '../state/ducks/conversations';
|
||||
import { initialDefaultRoomState } from '../state/ducks/defaultRooms';
|
||||
import { initialModalState } from '../state/ducks/modalDialog';
|
||||
import { initialOnionPathState } from '../state/ducks/onion';
|
||||
import { initialSearchState } from '../state/ducks/search';
|
||||
import { initialSectionState } from '../state/ducks/section';
|
||||
import { getEmptyStagedAttachmentsState } from '../state/ducks/stagedAttachments';
|
||||
import { initialThemeState } from '../state/ducks/theme';
|
||||
import { TimerOptionsArray } from '../state/ducks/timerOptions';
|
||||
import { initialUserConfigState } from '../state/ducks/userConfig';
|
||||
import { StateType } from '../state/reducer';
|
||||
import { makeLookup } from '../util';
|
||||
import { SessionMainPanel } from './SessionMainPanel';
|
||||
import { createStore } from '../state/createStore';
|
||||
|
||||
// Workaround: A react component's required properties are filtering up through connect()
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363
|
|
@ -4,7 +4,7 @@ import { useAppIsFocused } from '../hooks/useAppFocused';
|
|||
import { getFocusedSettingsSection } from '../state/selectors/section';
|
||||
|
||||
import { SmartSessionConversation } from '../state/smart/SessionConversation';
|
||||
import { SessionSettingsView } from './session/settings/SessionSettings';
|
||||
import { SessionSettingsView } from './settings/SessionSettings';
|
||||
|
||||
const FilteredSettingsView = SessionSettingsView as any;
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@ import React from 'react';
|
|||
import classNames from 'classnames';
|
||||
|
||||
import { SessionIcon } from './icon';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from './SessionButton';
|
||||
import { Constants } from '../../session';
|
||||
import { withTheme } from 'styled-components';
|
||||
import autoBind from 'auto-bind';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from './basic/SessionButton';
|
||||
import { Constants } from '../session';
|
||||
|
||||
interface State {
|
||||
error: string;
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
import { getShowScrollButton } from '../../state/selectors/conversations';
|
||||
import { getShowScrollButton } from '../state/selectors/conversations';
|
||||
|
||||
import { SessionIconButton } from './icon';
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { getConversationsCount } from '../../state/selectors/conversations';
|
||||
import { getConversationsCount } from '../state/selectors/conversations';
|
||||
import { SessionIconButton } from './icon';
|
||||
|
||||
type Props = {
|
|
@ -2,10 +2,10 @@ import React, { useEffect, useRef } from 'react';
|
|||
import classNames from 'classnames';
|
||||
|
||||
import { SessionIconButton } from './icon/';
|
||||
import { SessionButton } from './SessionButton';
|
||||
|
||||
// tslint:disable-next-line: no-submodule-imports
|
||||
import useKey from 'react-use/lib/useKey';
|
||||
import { SessionButton } from './basic/SessionButton';
|
||||
|
||||
export type SessionWrapperModalType = {
|
||||
title?: string;
|
|
@ -1,15 +1,15 @@
|
|||
import React, { useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useEncryptedFileFetch } from '../hooks/useEncryptedFileFetch';
|
||||
import { useEncryptedFileFetch } from '../../hooks/useEncryptedFileFetch';
|
||||
import _ from 'underscore';
|
||||
import {
|
||||
useAvatarPath,
|
||||
useConversationUsername,
|
||||
useIsClosedGroup,
|
||||
} from '../hooks/useParamSelector';
|
||||
} from '../../hooks/useParamSelector';
|
||||
import { AvatarPlaceHolder } from './AvatarPlaceHolder/AvatarPlaceHolder';
|
||||
import { ClosedGroupAvatar } from './AvatarPlaceHolder/ClosedGroupAvatar';
|
||||
import { useDisableDrag } from '../hooks/useDisableDrag';
|
||||
import { useDisableDrag } from '../../hooks/useDisableDrag';
|
||||
|
||||
export enum AvatarSize {
|
||||
XS = 28,
|
||||
|
@ -111,10 +111,12 @@ const AvatarInner = (props: Props) => {
|
|||
hasImage ? 'module-avatar--with-image' : 'module-avatar--no-image',
|
||||
isClickable && 'module-avatar-clickable'
|
||||
)}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
props.onAvatarClick?.();
|
||||
onMouseDown={e => {
|
||||
if (props.onAvatarClick) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
props.onAvatarClick?.();
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
data-testid={dataTestId}
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { getInitials } from '../../util/getInitials';
|
||||
import { getInitials } from '../../../util/getInitials';
|
||||
|
||||
type Props = {
|
||||
diameter: number;
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { useMembersAvatars } from '../../hooks/useMembersAvatars';
|
||||
import { useMembersAvatars } from '../../../hooks/useMembersAvatars';
|
||||
import { Avatar, AvatarSize } from '../Avatar';
|
||||
|
||||
type Props = {
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState } from 'react';
|
||||
import { SessionIcon, SessionIconType } from '../icon';
|
||||
|
||||
import { SessionIcon, SessionIconType } from './icon/';
|
||||
import { SessionDropdownItem, SessionDropDownItemType } from './SessionDropdownItem';
|
||||
|
||||
// THIS IS DROPDOWN ACCORDIAN STYLE OPTIONS SELECTOR ELEMENT, NOT A CONTEXTMENU
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { SessionIcon, SessionIconType } from './icon/';
|
||||
import { SessionIcon, SessionIconType } from '../icon';
|
||||
|
||||
export enum SessionDropDownItemType {
|
||||
Default = 'default',
|
|
@ -20,6 +20,7 @@ export const SessionHtmlRenderer: React.SFC<Props> = ({ tag = 'div', key, html,
|
|||
return React.createElement(tag, {
|
||||
key,
|
||||
className,
|
||||
// tslint:disable-next-line: react-no-dangerous-html
|
||||
dangerouslySetInnerHTML: { __html: clean },
|
||||
});
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
import React, { ChangeEvent, KeyboardEvent, useRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useFocusMount } from '../../hooks/useFocusMount';
|
||||
|
||||
type Props = {
|
||||
placeholder?: string;
|
||||
value?: string;
|
||||
text?: string;
|
||||
editable?: boolean;
|
||||
onChange?: (value: string) => void;
|
||||
onPressEnter?: any;
|
||||
maxLength?: number;
|
||||
isGroup?: boolean;
|
||||
};
|
||||
|
||||
export const SessionIdEditable = (props: Props) => {
|
||||
const { placeholder, onPressEnter, onChange, editable, text, value, maxLength, isGroup } = props;
|
||||
const inputRef = useRef(null);
|
||||
|
||||
useFocusMount(inputRef, editable);
|
||||
function handleChange(e: ChangeEvent<HTMLTextAreaElement>) {
|
||||
if (editable && onChange) {
|
||||
const eventValue = e.target.value?.replace(/(\r\n|\n|\r)/gm, '');
|
||||
onChange(eventValue);
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeyDown(e: KeyboardEvent<HTMLTextAreaElement>) {
|
||||
if (editable && e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
// tslint:disable-next-line: no-unused-expression
|
||||
onPressEnter && onPressEnter();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames('session-id-editable', !editable && 'session-id-editable-disabled')}>
|
||||
<textarea
|
||||
className={classNames(
|
||||
isGroup ? 'group-id-editable-textarea' : 'session-id-editable-textarea'
|
||||
)}
|
||||
ref={inputRef}
|
||||
placeholder={placeholder}
|
||||
disabled={!editable}
|
||||
spellCheck={false}
|
||||
onKeyDown={handleKeyDown}
|
||||
onChange={handleChange}
|
||||
onBlur={handleChange}
|
||||
value={value || text}
|
||||
maxLength={maxLength}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { SessionIconButton } from './icon';
|
||||
import { SessionIconButton } from '../icon';
|
||||
|
||||
type Props = {
|
||||
label?: string;
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
|
||||
import { SessionIcon, SessionIconType } from './icon/';
|
||||
import { Flex } from '../basic/Flex';
|
||||
import styled from 'styled-components';
|
||||
import { noop } from 'lodash';
|
||||
import { SessionIcon, SessionIconType } from '../icon';
|
||||
|
||||
export enum SessionToastType {
|
||||
Info = 'info',
|
|
@ -1,11 +1,11 @@
|
|||
import { SessionIconButton } from '../icon';
|
||||
import { animation, contextMenu, Item, Menu } from 'react-contexify';
|
||||
import { InputItem } from '../../../session/utils/calling/CallManager';
|
||||
import { setFullScreenCall } from '../../../state/ducks/call';
|
||||
import { CallManager, ToastUtils } from '../../../session/utils';
|
||||
import { InputItem } from '../../session/utils/calling/CallManager';
|
||||
import { setFullScreenCall } from '../../state/ducks/call';
|
||||
import { CallManager, ToastUtils } from '../../session/utils';
|
||||
import React from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { getHasOngoingCallWithPubkey } from '../../../state/selectors/call';
|
||||
import { getHasOngoingCallWithPubkey } from '../../state/selectors/call';
|
||||
import { DropDownAndToggleButton } from '../icon/DropDownAndToggleButton';
|
||||
import styled from 'styled-components';
|
||||
|
|
@ -3,12 +3,12 @@ import { useDispatch, useSelector } from 'react-redux';
|
|||
// tslint:disable-next-line: no-submodule-imports
|
||||
import useKey from 'react-use/lib/useKey';
|
||||
import styled from 'styled-components';
|
||||
import { useVideoCallEventsListener } from '../../../hooks/useVideoEventListener';
|
||||
import { setFullScreenCall } from '../../../state/ducks/call';
|
||||
import { useVideoCallEventsListener } from '../../hooks/useVideoEventListener';
|
||||
import { setFullScreenCall } from '../../state/ducks/call';
|
||||
import {
|
||||
getCallIsInFullScreen,
|
||||
getHasOngoingCallWithFocusedConvo,
|
||||
} from '../../../state/selectors/call';
|
||||
} from '../../state/selectors/call';
|
||||
import { CallWindowControls } from './CallButtons';
|
||||
import { StyledVideoElement } from './DraggableCallContainer';
|
||||
|
|
@ -4,11 +4,11 @@ import Draggable, { DraggableData, DraggableEvent } from 'react-draggable';
|
|||
|
||||
import styled from 'styled-components';
|
||||
import _ from 'underscore';
|
||||
import { getSelectedConversationKey } from '../../../state/selectors/conversations';
|
||||
import { getHasOngoingCall, getHasOngoingCallWith } from '../../../state/selectors/call';
|
||||
import { openConversationWithMessages } from '../../../state/ducks/conversations';
|
||||
import { Avatar, AvatarSize } from '../../Avatar';
|
||||
import { useVideoCallEventsListener } from '../../../hooks/useVideoEventListener';
|
||||
import { getSelectedConversationKey } from '../../state/selectors/conversations';
|
||||
import { getHasOngoingCall, getHasOngoingCallWith } from '../../state/selectors/call';
|
||||
import { openConversationWithMessages } from '../../state/ducks/conversations';
|
||||
import { Avatar, AvatarSize } from '../avatar/Avatar';
|
||||
import { useVideoCallEventsListener } from '../../hooks/useVideoEventListener';
|
||||
import { VideoLoadingSpinner } from './InConversationCallContainer';
|
||||
|
||||
export const DraggableCallWindow = styled.div`
|
|
@ -3,7 +3,7 @@ import React, { useRef, useState } from 'react';
|
|||
|
||||
import styled from 'styled-components';
|
||||
import _ from 'underscore';
|
||||
import { CallManager, UserUtils } from '../../../session/utils';
|
||||
import { CallManager, UserUtils } from '../../session/utils';
|
||||
import {
|
||||
getCallIsInFullScreen,
|
||||
getCallWithFocusedConvoIsOffering,
|
||||
|
@ -11,18 +11,18 @@ import {
|
|||
getCallWithFocusedConvosIsConnecting,
|
||||
getHasOngoingCallWithFocusedConvo,
|
||||
getHasOngoingCallWithPubkey,
|
||||
} from '../../../state/selectors/call';
|
||||
} from '../../state/selectors/call';
|
||||
import { StyledVideoElement } from './DraggableCallContainer';
|
||||
import { Avatar, AvatarSize } from '../../Avatar';
|
||||
import { Avatar, AvatarSize } from '../avatar/Avatar';
|
||||
|
||||
import { useVideoCallEventsListener } from '../../../hooks/useVideoEventListener';
|
||||
import { useModuloWithTripleDots } from '../../../hooks/useModuloWithTripleDots';
|
||||
import { useVideoCallEventsListener } from '../../hooks/useVideoEventListener';
|
||||
import { useModuloWithTripleDots } from '../../hooks/useModuloWithTripleDots';
|
||||
import { CallWindowControls } from './CallButtons';
|
||||
import { SessionSpinner } from '../SessionSpinner';
|
||||
import { DEVICE_DISABLED_DEVICE_ID } from '../../../session/utils/calling/CallManager';
|
||||
import { DEVICE_DISABLED_DEVICE_ID } from '../../session/utils/calling/CallManager';
|
||||
// tslint:disable-next-line: no-submodule-imports
|
||||
import useInterval from 'react-use/lib/useInterval';
|
||||
import moment from 'moment';
|
||||
import { SessionSpinner } from '../basic/SessionSpinner';
|
||||
|
||||
const VideoContainer = styled.div`
|
||||
height: 100%;
|
|
@ -3,13 +3,13 @@ import { useSelector } from 'react-redux';
|
|||
|
||||
import styled from 'styled-components';
|
||||
import _ from 'underscore';
|
||||
import { useConversationUsername } from '../../../hooks/useParamSelector';
|
||||
import { ed25519Str } from '../../../session/onions/onionPath';
|
||||
import { CallManager } from '../../../session/utils';
|
||||
import { callTimeoutMs } from '../../../session/utils/calling/CallManager';
|
||||
import { getHasIncomingCall, getHasIncomingCallFrom } from '../../../state/selectors/call';
|
||||
import { Avatar, AvatarSize } from '../../Avatar';
|
||||
import { SessionButton, SessionButtonColor } from '../SessionButton';
|
||||
import { useConversationUsername } from '../../hooks/useParamSelector';
|
||||
import { ed25519Str } from '../../session/onions/onionPath';
|
||||
import { CallManager } from '../../session/utils';
|
||||
import { callTimeoutMs } from '../../session/utils/calling/CallManager';
|
||||
import { getHasIncomingCall, getHasIncomingCallFrom } from '../../state/selectors/call';
|
||||
import { Avatar, AvatarSize } from '../avatar/Avatar';
|
||||
import { SessionButton, SessionButtonColor } from '../basic/SessionButton';
|
||||
import { SessionWrapperModal } from '../SessionWrapperModal';
|
||||
|
||||
export const CallWindow = styled.div`
|
|
@ -1,11 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Avatar, AvatarSize } from '../Avatar';
|
||||
import { Avatar, AvatarSize } from '../avatar/Avatar';
|
||||
|
||||
import { SessionIconButton } from '../session/icon';
|
||||
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from '../session/SessionButton';
|
||||
import { MemoConversationHeaderMenu } from '../session/menu/ConversationHeaderMenu';
|
||||
import { contextMenu } from 'react-contexify';
|
||||
import styled from 'styled-components';
|
||||
import { ConversationNotificationSettingType } from '../../models/conversation';
|
||||
|
@ -38,6 +34,9 @@ import {
|
|||
import { callRecipient } from '../../interactions/conversationInteractions';
|
||||
import { getHasIncomingCall, getHasOngoingCall } from '../../state/selectors/call';
|
||||
import { useConversationUsername } from '../../hooks/useParamSelector';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
|
||||
import { SessionIconButton } from '../icon';
|
||||
import { MemoConversationHeaderMenu } from '../menu/ConversationHeaderMenu';
|
||||
|
||||
export interface TimerOption {
|
||||
name: string;
|
||||
|
|
|
@ -3,7 +3,7 @@ import React, { useCallback, useState } from 'react';
|
|||
import { getTimerBucketIcon } from '../../util/timer';
|
||||
import { useInterval } from '../../hooks/useInterval';
|
||||
import styled from 'styled-components';
|
||||
import { SessionIcon } from '../session/icon';
|
||||
import { SessionIcon } from '../icon/SessionIcon';
|
||||
|
||||
type Props = {
|
||||
expirationLength: number;
|
||||
|
|
|
@ -8,8 +8,8 @@ import {
|
|||
PropsForGroupUpdateType,
|
||||
} from '../../state/ducks/conversations';
|
||||
import _ from 'underscore';
|
||||
import { ReadableMessage } from './ReadableMessage';
|
||||
import { NotificationBubble } from './notification-bubble/NotificationBubble';
|
||||
import { NotificationBubble } from './message/message-item/notification-bubble/NotificationBubble';
|
||||
import { ReadableMessage } from './message/message-item/ReadableMessage';
|
||||
|
||||
// This component is used to display group updates in the conversation view.
|
||||
// This is a not a "notification" as the name suggests, but a message inside the conversation
|
||||
|
|
|
@ -9,8 +9,8 @@ import {
|
|||
getSortedMessagesOfSelectedConversation,
|
||||
} from '../../state/selectors/conversations';
|
||||
import { getAudioAutoplay } from '../../state/selectors/userConfig';
|
||||
import { SessionIcon } from '../session/icon';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from '../session/SessionButton';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
|
||||
import { SessionIcon } from '../icon';
|
||||
|
||||
export const AudioPlayerWithEncryptedFile = (props: {
|
||||
src: string;
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
} from '../../types/Attachment';
|
||||
|
||||
import { Image } from './Image';
|
||||
import { IsMessageVisibleContext } from './message/MessageContent';
|
||||
import { IsMessageVisibleContext } from './message/message-content/MessageContent';
|
||||
|
||||
type Props = {
|
||||
attachments: Array<AttachmentTypeWithPath>;
|
||||
|
|
|
@ -8,36 +8,38 @@ import {
|
|||
StagedAttachmentType,
|
||||
} from './composition/CompositionBox';
|
||||
|
||||
import { Constants } from '../../../session';
|
||||
import _ from 'lodash';
|
||||
import { AttachmentUtil, GoogleChrome } from '../../../util';
|
||||
import { ConversationHeaderWithDetails } from '../../conversation/ConversationHeader';
|
||||
import { SessionRightPanelWithDetails } from './SessionRightPanel';
|
||||
import { SessionTheme } from '../../../state/ducks/SessionTheme';
|
||||
import { SessionMessagesListContainer } from './SessionMessagesListContainer';
|
||||
import { LightboxGallery, MediaItemType } from '../../LightboxGallery';
|
||||
|
||||
import { AttachmentTypeWithPath } from '../../../types/Attachment';
|
||||
import { ToastUtils, UserUtils } from '../../../session/utils';
|
||||
import * as MIME from '../../../types/MIME';
|
||||
import { SessionMessagesListContainer } from './SessionMessagesListContainer';
|
||||
|
||||
import { SessionFileDropzone } from './SessionFileDropzone';
|
||||
|
||||
import autoBind from 'auto-bind';
|
||||
import { InConversationCallContainer } from '../calling/InConversationCallContainer';
|
||||
import { SplitViewContainer } from '../SplitViewContainer';
|
||||
import { LightboxGallery, MediaItemType } from '../lightbox/LightboxGallery';
|
||||
import { getPubkeysInPublicConversation } from '../../data/data';
|
||||
import { Constants } from '../../session';
|
||||
import { getConversationController } from '../../session/conversations';
|
||||
import { ToastUtils, UserUtils } from '../../session/utils';
|
||||
import {
|
||||
quoteMessage,
|
||||
ReduxConversationType,
|
||||
resetSelectedMessageIds,
|
||||
SortedMessageModelProps,
|
||||
updateMentionsMembers,
|
||||
} from '../../../state/ducks/conversations';
|
||||
import { MessageView } from '../../MainViewController';
|
||||
import { MessageDetail } from '../../conversation/MessageDetail';
|
||||
import { getConversationController } from '../../../session/conversations';
|
||||
import { getPubkeysInPublicConversation } from '../../../data/data';
|
||||
import autoBind from 'auto-bind';
|
||||
import { SessionButtonColor } from '../SessionButton';
|
||||
import { updateConfirmModal } from '../../../state/ducks/modalDialog';
|
||||
import { addStagedAttachmentsInConversation } from '../../../state/ducks/stagedAttachments';
|
||||
import { InConversationCallContainer } from '../calling/InConversationCallContainer';
|
||||
import { SplitViewContainer } from '../SplitViewContainer';
|
||||
} from '../../state/ducks/conversations';
|
||||
import { updateConfirmModal } from '../../state/ducks/modalDialog';
|
||||
import { SessionTheme } from '../../state/ducks/SessionTheme';
|
||||
import { addStagedAttachmentsInConversation } from '../../state/ducks/stagedAttachments';
|
||||
import { MIME } from '../../types';
|
||||
import { AttachmentTypeWithPath } from '../../types/Attachment';
|
||||
import { AttachmentUtil, GoogleChrome } from '../../util';
|
||||
import { SessionButtonColor } from '../basic/SessionButton';
|
||||
import { MessageView } from '../MainViewController';
|
||||
import { ConversationHeaderWithDetails } from './ConversationHeader';
|
||||
import { MessageDetail } from './message/message-item/MessageDetail';
|
||||
import { SessionRightPanelWithDetails } from './SessionRightPanel';
|
||||
// tslint:disable: jsx-curly-spacing
|
||||
|
||||
interface State {
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Picker } from 'emoji-mart';
|
||||
import { Constants } from '../../../session';
|
||||
import { Constants } from '../../session';
|
||||
|
||||
type Props = {
|
||||
onEmojiClicked: (emoji: any) => void;
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Flex } from '../../basic/Flex';
|
||||
import { Flex } from '../basic/Flex';
|
||||
import { SessionIcon } from '../icon';
|
||||
|
||||
const DropZoneContainer = styled.div`
|
|
@ -2,22 +2,23 @@ import React from 'react';
|
|||
import { useSelector } from 'react-redux';
|
||||
// tslint:disable-next-line: no-submodule-imports
|
||||
import useKey from 'react-use/lib/useKey';
|
||||
import { PropsForDataExtractionNotification, QuoteClickOptions } from '../../../models/messageType';
|
||||
import { PropsForDataExtractionNotification, QuoteClickOptions } from '../../models/messageType';
|
||||
import {
|
||||
PropsForCallNotification,
|
||||
PropsForExpirationTimer,
|
||||
PropsForGroupInvitation,
|
||||
PropsForGroupUpdate,
|
||||
} from '../../../state/ducks/conversations';
|
||||
import { getSortedMessagesTypesOfSelectedConversation } from '../../../state/selectors/conversations';
|
||||
import { CallNotification } from '../../conversation/notification-bubble/CallNotification';
|
||||
import { DataExtractionNotification } from '../../conversation/DataExtractionNotification';
|
||||
import { GroupInvitation } from '../../conversation/GroupInvitation';
|
||||
import { GroupNotification } from '../../conversation/GroupNotification';
|
||||
import { Message } from '../../conversation/Message';
|
||||
import { MessageDateBreak } from '../../conversation/message/DateBreak';
|
||||
import { TimerNotification } from '../../conversation/TimerNotification';
|
||||
} from '../../state/ducks/conversations';
|
||||
import { getSortedMessagesTypesOfSelectedConversation } from '../../state/selectors/conversations';
|
||||
import { GroupNotification } from './GroupNotification';
|
||||
import { DataExtractionNotification } from './message/message-item/DataExtractionNotification';
|
||||
import { MessageDateBreak } from './message/message-item/DateBreak';
|
||||
import { GroupInvitation } from './message/message-item/GroupInvitation';
|
||||
import { Message } from './message/message-item/Message';
|
||||
import { CallNotification } from './message/message-item/notification-bubble/CallNotification';
|
||||
|
||||
import { SessionLastSeenIndicator } from './SessionLastSeenIndicator';
|
||||
import { TimerNotification } from './TimerNotification';
|
||||
|
||||
export const SessionMessagesList = (props: {
|
||||
scrollToQuoteMessage: (options: QuoteClickOptions) => Promise<void>;
|
|
@ -2,23 +2,26 @@ import React from 'react';
|
|||
|
||||
import { SessionScrollButton } from '../SessionScrollButton';
|
||||
import { contextMenu } from 'react-contexify';
|
||||
|
||||
import { connect, useSelector } from 'react-redux';
|
||||
|
||||
import { SessionMessagesList } from './SessionMessagesList';
|
||||
import styled from 'styled-components';
|
||||
import autoBind from 'auto-bind';
|
||||
import { getMessagesBySentAt } from '../../data/data';
|
||||
import { ConversationTypeEnum } from '../../models/conversation';
|
||||
import { MessageModel } from '../../models/message';
|
||||
import { QuoteClickOptions } from '../../models/messageType';
|
||||
import { getConversationController } from '../../session/conversations';
|
||||
import { ToastUtils } from '../../session/utils';
|
||||
import {
|
||||
quotedMessageToAnimate,
|
||||
ReduxConversationType,
|
||||
showScrollToBottomButton,
|
||||
SortedMessageModelProps,
|
||||
updateHaveDoneFirstScroll,
|
||||
} from '../../../state/ducks/conversations';
|
||||
import { ToastUtils } from '../../../session/utils';
|
||||
import { TypingBubble } from '../../conversation/TypingBubble';
|
||||
import { getConversationController } from '../../../session/conversations';
|
||||
import { MessageModel } from '../../../models/message';
|
||||
import { QuoteClickOptions } from '../../../models/messageType';
|
||||
import { getMessagesBySentAt } from '../../../data/data';
|
||||
import autoBind from 'auto-bind';
|
||||
import { ConversationTypeEnum } from '../../../models/conversation';
|
||||
import { StateType } from '../../../state/reducer';
|
||||
import { connect, useSelector } from 'react-redux';
|
||||
} from '../../state/ducks/conversations';
|
||||
import { StateType } from '../../state/reducer';
|
||||
import {
|
||||
getFirstUnreadMessageId,
|
||||
getQuotedMessageToAnimate,
|
||||
|
@ -27,9 +30,8 @@ import {
|
|||
getShowScrollButton,
|
||||
getSortedMessagesOfSelectedConversation,
|
||||
isFirstUnreadMessageIdAbove,
|
||||
} from '../../../state/selectors/conversations';
|
||||
import { SessionMessagesList } from './SessionMessagesList';
|
||||
import styled from 'styled-components';
|
||||
} from '../../state/selectors/conversations';
|
||||
import { TypingBubble } from './TypingBubble';
|
||||
|
||||
export type SessionMessageListProps = {
|
||||
messageContainerRef: React.RefObject<HTMLDivElement>;
|
|
@ -1,13 +1,13 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { Flex } from '../../basic/Flex';
|
||||
import { SessionIcon, SessionIconButton } from '../icon';
|
||||
import styled from 'styled-components';
|
||||
import { getAlt, isAudio } from '../../../types/Attachment';
|
||||
import { Image } from '../../conversation/Image';
|
||||
import { AUDIO_MP3 } from '../../../types/MIME';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { getQuotedMessage } from '../../../state/selectors/conversations';
|
||||
import { quoteMessage } from '../../../state/ducks/conversations';
|
||||
import { quoteMessage } from '../../state/ducks/conversations';
|
||||
import { getQuotedMessage } from '../../state/selectors/conversations';
|
||||
import { getAlt, isAudio } from '../../types/Attachment';
|
||||
import { AUDIO_MP3 } from '../../types/MIME';
|
||||
import { Flex } from '../basic/Flex';
|
||||
import { Image } from '../../../ts/components/conversation/Image';
|
||||
|
||||
const QuotedMessageComposition = styled.div`
|
||||
width: 100%;
|
|
@ -3,11 +3,11 @@ import classNames from 'classnames';
|
|||
import moment from 'moment';
|
||||
|
||||
import { SessionIconButton } from '../icon';
|
||||
import { Constants } from '../../../session';
|
||||
import { ToastUtils } from '../../../session/utils';
|
||||
import autoBind from 'auto-bind';
|
||||
import MicRecorder from 'mic-recorder-to-mp3';
|
||||
import styled from 'styled-components';
|
||||
import { Constants } from '../../session';
|
||||
import { ToastUtils } from '../../session/utils';
|
||||
|
||||
interface Props {
|
||||
onExitVoiceNoteView: () => void;
|
||||
|
@ -124,7 +124,7 @@ export class SessionRecording extends React.Component<Props, State> {
|
|||
<StyledFlexWrapper marginHorizontal={Constants.UI.SPACING.marginXs}>
|
||||
{isRecording && (
|
||||
<SessionIconButton
|
||||
iconType="pause"
|
||||
iconType="stop"
|
||||
iconSize="medium"
|
||||
iconColor={Constants.UI.COLORS.DANGER_ALT}
|
||||
onClick={actionPauseFn}
|
|
@ -1,17 +1,13 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { SessionIconButton } from '../icon';
|
||||
import { Avatar, AvatarSize } from '../../Avatar';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from '../SessionButton';
|
||||
import { SessionDropdown } from '../SessionDropdown';
|
||||
import { MediaGallery } from '../../conversation/media-gallery/MediaGallery';
|
||||
import _ from 'lodash';
|
||||
import { Constants } from '../../../session';
|
||||
import { AttachmentTypeWithPath } from '../../../types/Attachment';
|
||||
// tslint:disable-next-line: no-submodule-imports
|
||||
import useInterval from 'react-use/lib/useInterval';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import {
|
||||
getMessagesWithFileAttachments,
|
||||
getMessagesWithVisualMediaAttachments,
|
||||
} from '../../../data/data';
|
||||
import { SpacerLG } from '../../basic/Text';
|
||||
} from '../../data/data';
|
||||
import {
|
||||
deleteAllMessagesByConvoIdWithConfirmation,
|
||||
setDisappearingMessagesByConvoId,
|
||||
|
@ -21,17 +17,18 @@ import {
|
|||
showRemoveModeratorsByConvoId,
|
||||
showUpdateGroupMembersByConvoId,
|
||||
showUpdateGroupNameByConvoId,
|
||||
} from '../../../interactions/conversationInteractions';
|
||||
import { MediaItemType } from '../../LightboxGallery';
|
||||
// tslint:disable-next-line: no-submodule-imports
|
||||
import useInterval from 'react-use/lib/useInterval';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { getTimerOptions } from '../../../state/selectors/timerOptions';
|
||||
import {
|
||||
getSelectedConversation,
|
||||
isRightPanelShowing,
|
||||
} from '../../../state/selectors/conversations';
|
||||
import { closeRightPanel } from '../../../state/ducks/conversations';
|
||||
} from '../../interactions/conversationInteractions';
|
||||
import { Constants } from '../../session';
|
||||
import { closeRightPanel } from '../../state/ducks/conversations';
|
||||
import { getSelectedConversation, isRightPanelShowing } from '../../state/selectors/conversations';
|
||||
import { getTimerOptions } from '../../state/selectors/timerOptions';
|
||||
import { AttachmentTypeWithPath } from '../../types/Attachment';
|
||||
import { Avatar, AvatarSize } from '../avatar/Avatar';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
|
||||
import { SessionDropdown } from '../basic/SessionDropdown';
|
||||
import { SpacerLG } from '../basic/Text';
|
||||
import { MediaItemType } from '../lightbox/LightboxGallery';
|
||||
import { MediaGallery } from './media-gallery/MediaGallery';
|
||||
|
||||
async function getMediaGalleryProps(
|
||||
conversationId: string
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react';
|
||||
import { arrayBufferFromFile } from '../../../types/Attachment';
|
||||
import { AttachmentUtil, LinkPreviewUtil } from '../../../util';
|
||||
import { StagedLinkPreviewData } from './composition/CompositionBox';
|
||||
import { default as insecureNodeFetch } from 'node-fetch';
|
||||
import { fetchLinkPreviewImage } from '../../../util/linkPreviewFetch';
|
||||
import { AbortSignal } from 'abort-controller';
|
||||
import { StagedLinkPreview } from '../../conversation/StagedLinkPreview';
|
||||
import { arrayBufferFromFile } from '../../types/Attachment';
|
||||
import { AttachmentUtil, LinkPreviewUtil } from '../../util';
|
||||
import { fetchLinkPreviewImage } from '../../util/linkPreviewFetch';
|
||||
import { StagedLinkPreview } from './StagedLinkPreview';
|
||||
|
||||
export interface StagedLinkPreviewProps extends StagedLinkPreviewData {
|
||||
onClose: (url: string) => void;
|
|
@ -4,7 +4,7 @@ import classNames from 'classnames';
|
|||
import { Image } from './Image';
|
||||
|
||||
import { AttachmentType, isImageAttachment } from '../../types/Attachment';
|
||||
import { SessionSpinner } from '../session/SessionSpinner';
|
||||
import { SessionSpinner } from '../basic/SessionSpinner';
|
||||
|
||||
type Props = {
|
||||
isLoaded: boolean;
|
||||
|
|
|
@ -2,8 +2,8 @@ import React from 'react';
|
|||
|
||||
import { missingCaseError } from '../../util/missingCaseError';
|
||||
import { PropsForExpirationTimer } from '../../state/ducks/conversations';
|
||||
import { ReadableMessage } from './ReadableMessage';
|
||||
import { NotificationBubble } from './notification-bubble/NotificationBubble';
|
||||
import { NotificationBubble } from './message/message-item/notification-bubble/NotificationBubble';
|
||||
import { ReadableMessage } from './message/message-item/ReadableMessage';
|
||||
|
||||
export const TimerNotification = (props: PropsForExpirationTimer) => {
|
||||
const { messageId, receivedAt, isUnread, pubkey, profileName, timespan, type, disabled } = props;
|
||||
|
|
|
@ -1,19 +1,12 @@
|
|||
import React from 'react';
|
||||
import _, { debounce } from 'lodash';
|
||||
|
||||
import { AttachmentType } from '../../../../types/Attachment';
|
||||
import * as MIME from '../../../../types/MIME';
|
||||
import * as MIME from '../../../types/MIME';
|
||||
|
||||
import { SessionEmojiPanel } from '../SessionEmojiPanel';
|
||||
import { SessionRecording } from '../SessionRecording';
|
||||
|
||||
import { Constants } from '../../../../session';
|
||||
|
||||
import { toArray } from 'react-emoji-render';
|
||||
import { Flex } from '../../../basic/Flex';
|
||||
import { StagedAttachmentList } from '../../../conversation/StagedAttachmentList';
|
||||
import { ToastUtils } from '../../../../session/utils';
|
||||
import { AttachmentUtil } from '../../../../util';
|
||||
import {
|
||||
getPreview,
|
||||
LINK_PREVIEW_TIMEOUT,
|
||||
|
@ -22,31 +15,37 @@ import {
|
|||
import { AbortController } from 'abort-controller';
|
||||
import { SessionQuotedMessageComposition } from '../SessionQuotedMessageComposition';
|
||||
import { Mention, MentionsInput } from 'react-mentions';
|
||||
import { CaptionEditor } from '../../../CaptionEditor';
|
||||
import { getConversationController } from '../../../../session/conversations';
|
||||
import { ReduxConversationType } from '../../../../state/ducks/conversations';
|
||||
import { SessionMemberListItem } from '../../SessionMemberListItem';
|
||||
import { MemberListItem } from '../../MemberListItem';
|
||||
import autoBind from 'auto-bind';
|
||||
import { getMediaPermissionsSettings } from '../../settings/SessionSettings';
|
||||
import {
|
||||
getIsTypingEnabled,
|
||||
getMentionsInput,
|
||||
getQuotedMessage,
|
||||
getSelectedConversation,
|
||||
getSelectedConversationKey,
|
||||
} from '../../../../state/selectors/conversations';
|
||||
import { connect } from 'react-redux';
|
||||
import { StateType } from '../../../../state/reducer';
|
||||
import { getTheme } from '../../../../state/selectors/theme';
|
||||
import { removeAllStagedAttachmentsInConversation } from '../../../../state/ducks/stagedAttachments';
|
||||
import { getDraftForConversation, updateDraftForConversation } from '../SessionConversationDrafts';
|
||||
import { showLinkSharingConfirmationModalDialog } from '../../../../interactions/conversationInteractions';
|
||||
import {
|
||||
AddStagedAttachmentButton,
|
||||
SendMessageButton,
|
||||
StartRecordingButton,
|
||||
ToggleEmojiButton,
|
||||
} from './CompositionButtons';
|
||||
import { AttachmentType } from '../../../types/Attachment';
|
||||
import { connect } from 'react-redux';
|
||||
import { showLinkSharingConfirmationModalDialog } from '../../../interactions/conversationInteractions';
|
||||
import { Constants } from '../../../session';
|
||||
import { getConversationController } from '../../../session/conversations';
|
||||
import { ToastUtils } from '../../../session/utils';
|
||||
import { ReduxConversationType } from '../../../state/ducks/conversations';
|
||||
import { removeAllStagedAttachmentsInConversation } from '../../../state/ducks/stagedAttachments';
|
||||
import { StateType } from '../../../state/reducer';
|
||||
import {
|
||||
getIsTypingEnabled,
|
||||
getMentionsInput,
|
||||
getQuotedMessage,
|
||||
getSelectedConversation,
|
||||
getSelectedConversationKey,
|
||||
} from '../../../state/selectors/conversations';
|
||||
import { getTheme } from '../../../state/selectors/theme';
|
||||
import { AttachmentUtil } from '../../../util';
|
||||
import { Flex } from '../../basic/Flex';
|
||||
import { CaptionEditor } from '../../CaptionEditor';
|
||||
import { StagedAttachmentList } from '../StagedAttachmentList';
|
||||
|
||||
export interface ReplyingToMessageProps {
|
||||
convoId: string;
|
||||
|
@ -413,7 +412,6 @@ class CompositionBoxInner extends React.Component<Props, State> {
|
|||
? i18n('unblockGroupToSend')
|
||||
: i18n('sendMessage');
|
||||
const { typingEnabled } = this.props;
|
||||
let index = 0;
|
||||
|
||||
return (
|
||||
<MentionsInput
|
||||
|
@ -439,21 +437,7 @@ class CompositionBoxInner extends React.Component<Props, State> {
|
|||
displayTransform={(_id, display) => `@${display}`}
|
||||
data={this.fetchUsersForGroup}
|
||||
renderSuggestion={(suggestion, _search, _highlightedDisplay, _index, focused) => (
|
||||
<SessionMemberListItem
|
||||
isSelected={focused}
|
||||
index={index++}
|
||||
key={suggestion.id}
|
||||
member={{
|
||||
id: `${suggestion.id}`,
|
||||
authorPhoneNumber: `${suggestion.id}`,
|
||||
selected: focused,
|
||||
authorProfileName: `${suggestion.display}`,
|
||||
authorName: `${suggestion.display}`,
|
||||
existingMember: false,
|
||||
checkmarked: false,
|
||||
authorAvatarPath: '',
|
||||
}}
|
||||
/>
|
||||
<MemberListItem isSelected={focused} key={suggestion.id} pubkey={`${suggestion.id}`} />
|
||||
)}
|
||||
/>
|
||||
</MentionsInput>
|
|
@ -2,8 +2,8 @@ import React from 'react';
|
|||
|
||||
import { DocumentListItem } from './DocumentListItem';
|
||||
import { MediaGridItem } from './MediaGridItem';
|
||||
import { MediaItemType } from '../../LightboxGallery';
|
||||
import { missingCaseError } from '../../../util/missingCaseError';
|
||||
import { MediaItemType } from '../../lightbox/LightboxGallery';
|
||||
|
||||
type Props = {
|
||||
type: 'media' | 'documents';
|
||||
|
|
|
@ -4,10 +4,10 @@ import classNames from 'classnames';
|
|||
import moment from 'moment';
|
||||
// tslint:disable-next-line:match-default-export-name
|
||||
import formatFileSize from 'filesize';
|
||||
import { MediaItemType } from '../../LightboxGallery';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { getSelectedConversationKey } from '../../../state/selectors/conversations';
|
||||
import { saveAttachmentToDisk } from '../../../util/attachmentsUtil';
|
||||
import { MediaItemType } from '../../lightbox/LightboxGallery';
|
||||
|
||||
type Props = {
|
||||
// Required
|
||||
|
|
|
@ -3,8 +3,8 @@ import classNames from 'classnames';
|
|||
|
||||
import { AttachmentSection } from './AttachmentSection';
|
||||
import { EmptyState } from './EmptyState';
|
||||
import { MediaItemType } from '../../lightbox/LightboxGallery';
|
||||
|
||||
import { MediaItemType } from '../../LightboxGallery';
|
||||
type Props = {
|
||||
documents: Array<MediaItemType>;
|
||||
media: Array<MediaItemType>;
|
||||
|
|
|
@ -2,11 +2,11 @@ import React, { useState } from 'react';
|
|||
import classNames from 'classnames';
|
||||
|
||||
import { isImageTypeSupported, isVideoTypeSupported } from '../../../util/GoogleChrome';
|
||||
import { MediaItemType } from '../../LightboxGallery';
|
||||
import { useEncryptedFileFetch } from '../../../hooks/useEncryptedFileFetch';
|
||||
import { showLightBox } from '../../../state/ducks/conversations';
|
||||
import { LightBoxOptions } from '../../session/conversation/SessionConversation';
|
||||
import { useDisableDrag } from '../../../hooks/useDisableDrag';
|
||||
import { LightBoxOptions } from '../SessionConversation';
|
||||
import { MediaItemType } from '../../lightbox/LightboxGallery';
|
||||
|
||||
type Props = {
|
||||
mediaItem: MediaItemType;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import moment from 'moment';
|
||||
import { compact, groupBy, sortBy } from 'lodash';
|
||||
|
||||
import { MediaItemType } from '../../LightboxGallery';
|
||||
import { MediaItemType } from '../../lightbox/LightboxGallery';
|
||||
|
||||
// import { missingCaseError } from '../../../util/missingCaseError';
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { getMessageById, getMessagesByConversation } from '../../../data/data';
|
||||
import { getConversationController } from '../../../session/conversations';
|
||||
import { AttachmentDownloads } from '../../../session/utils';
|
||||
import { updateConfirmModal } from '../../../state/ducks/modalDialog';
|
||||
import { SessionIcon } from '../../session/icon';
|
||||
import { SessionButtonColor } from '../../session/SessionButton';
|
||||
import { getMessageById, getMessagesByConversation } from '../../../../data/data';
|
||||
import { getConversationController } from '../../../../session/conversations';
|
||||
import { AttachmentDownloads } from '../../../../session/utils';
|
||||
import { updateConfirmModal } from '../../../../state/ducks/modalDialog';
|
||||
import { SessionButtonColor } from '../../../basic/SessionButton';
|
||||
import { SessionIcon } from '../../../icon';
|
||||
|
||||
const StyledTrustSenderUI = styled.div`
|
||||
padding-inline: var(--margins-xs);
|
|
@ -2,17 +2,17 @@ import classNames from 'classnames';
|
|||
import React, { useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import _ from 'underscore';
|
||||
import { getMessageById } from '../../../data/data';
|
||||
import { MessageRenderingProps } from '../../../models/messageType';
|
||||
import { getMessageById } from '../../../../data/data';
|
||||
import { MessageRenderingProps } from '../../../../models/messageType';
|
||||
import {
|
||||
PropsForAttachment,
|
||||
showLightBox,
|
||||
toggleSelectedMessageId,
|
||||
} from '../../../state/ducks/conversations';
|
||||
} from '../../../../state/ducks/conversations';
|
||||
import {
|
||||
getMessageAttachmentProps,
|
||||
isMessageSelectionMode,
|
||||
} from '../../../state/selectors/conversations';
|
||||
} from '../../../../state/selectors/conversations';
|
||||
import {
|
||||
AttachmentType,
|
||||
AttachmentTypeWithPath,
|
||||
|
@ -23,13 +23,13 @@ import {
|
|||
isAudio,
|
||||
isImage,
|
||||
isVideo,
|
||||
} from '../../../types/Attachment';
|
||||
import { isFileDangerous } from '../../../util';
|
||||
import { saveAttachmentToDisk } from '../../../util/attachmentsUtil';
|
||||
import { Spinner } from '../../basic/Spinner';
|
||||
import { LightBoxOptions } from '../../session/conversation/SessionConversation';
|
||||
import { AudioPlayerWithEncryptedFile } from '../H5AudioPlayer';
|
||||
import { ImageGrid } from '../ImageGrid';
|
||||
} from '../../../../types/Attachment';
|
||||
import { isFileDangerous } from '../../../../util';
|
||||
import { saveAttachmentToDisk } from '../../../../util/attachmentsUtil';
|
||||
import { Spinner } from '../../../basic/Spinner';
|
||||
import { AudioPlayerWithEncryptedFile } from '../../H5AudioPlayer';
|
||||
import { ImageGrid } from '../../ImageGrid';
|
||||
import { LightBoxOptions } from '../../SessionConversation';
|
||||
import { ClickToTrustSender } from './ClickToTrustSender';
|
||||
|
||||
export type MessageAttachmentSelectorProps = Pick<
|
|
@ -1,14 +1,14 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { MessageRenderingProps } from '../../../models/messageType';
|
||||
import { PubKey } from '../../../session/types/PubKey';
|
||||
import { MessageRenderingProps } from '../../../../models/messageType';
|
||||
import { PubKey } from '../../../../session/types';
|
||||
import {
|
||||
getMessageAuthorProps,
|
||||
isGroupConversation,
|
||||
isPublicGroupConversation,
|
||||
} from '../../../state/selectors/conversations';
|
||||
import { Flex } from '../../basic/Flex';
|
||||
import { ContactName } from '../ContactName';
|
||||
} from '../../../../state/selectors/conversations';
|
||||
import { Flex } from '../../../basic/Flex';
|
||||
import { ContactName } from '../../ContactName';
|
||||
|
||||
export type MessageAuthorSelectorProps = Pick<
|
||||
MessageRenderingProps,
|
|
@ -1,9 +1,10 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { MessageRenderingProps } from '../../../models/messageType';
|
||||
import { updateUserDetailsModal } from '../../../state/ducks/modalDialog';
|
||||
import { getMessageAvatarProps } from '../../../state/selectors/conversations';
|
||||
import { Avatar, AvatarSize } from '../../Avatar';
|
||||
import { MessageRenderingProps } from '../../../../models/messageType';
|
||||
import { updateUserDetailsModal } from '../../../../state/ducks/modalDialog';
|
||||
import { getMessageAvatarProps } from '../../../../state/selectors/conversations';
|
||||
import { Avatar, AvatarSize } from '../../../avatar/Avatar';
|
||||
// tslint:disable: use-simple-attributes
|
||||
|
||||
export type MessageAvatarSelectorProps = Pick<
|
||||
MessageRenderingProps,
|
||||
|
@ -62,7 +63,11 @@ export const MessageAvatar = (props: Props) => {
|
|||
|
||||
return (
|
||||
<div className="module-message__author-avatar" key={`msg-avatar-${authorPhoneNumber}`}>
|
||||
<Avatar size={AvatarSize.S} onAvatarClick={onMessageAvatarClick} pubkey={authorPhoneNumber} />
|
||||
<Avatar
|
||||
size={AvatarSize.S}
|
||||
onAvatarClick={(!isPublic && onMessageAvatarClick) || undefined}
|
||||
pubkey={authorPhoneNumber}
|
||||
/>
|
||||
{isPublic && isSenderAdmin && (
|
||||
<div className="module-avatar__icon--crown-wrapper">
|
||||
<div className="module-avatar__icon--crown" />
|
|
@ -1,12 +1,10 @@
|
|||
import React from 'react';
|
||||
|
||||
import { getSizeClass, SizeClassType } from '../../util/emoji';
|
||||
import { Emojify } from './Emojify';
|
||||
import { AddNewLines } from './AddNewLines';
|
||||
import { AddMentions } from './AddMentions';
|
||||
import { Linkify } from './Linkify';
|
||||
|
||||
import { RenderTextCallbackType } from '../../types/Util';
|
||||
import { RenderTextCallbackType } from '../../../../types/Util';
|
||||
import { getSizeClass, SizeClassType } from '../../../../util/emoji';
|
||||
import { AddMentions } from '../../AddMentions';
|
||||
import { AddNewLines } from '../../AddNewLines';
|
||||
import { Emojify } from '../../Emojify';
|
||||
import { Linkify } from '../../Linkify';
|
||||
|
||||
interface Props {
|
||||
text: string;
|
|
@ -4,11 +4,11 @@ import React, { createContext, useCallback, useState } from 'react';
|
|||
import { InView } from 'react-intersection-observer';
|
||||
import { useSelector } from 'react-redux';
|
||||
import _ from 'underscore';
|
||||
import { MessageRenderingProps, QuoteClickOptions } from '../../../models/messageType';
|
||||
import { MessageRenderingProps, QuoteClickOptions } from '../../../../models/messageType';
|
||||
import {
|
||||
getMessageContentSelectorProps,
|
||||
getMessageTextProps,
|
||||
} from '../../../state/selectors/conversations';
|
||||
} from '../../../../state/selectors/conversations';
|
||||
import {
|
||||
canDisplayImage,
|
||||
getGridDimensions,
|
||||
|
@ -18,9 +18,9 @@ import {
|
|||
isImage,
|
||||
isImageAttachment,
|
||||
isVideo,
|
||||
} from '../../../types/Attachment';
|
||||
import { Flex } from '../../basic/Flex';
|
||||
import { MINIMUM_LINK_PREVIEW_IMAGE_WIDTH } from '../Message';
|
||||
} from '../../../../types/Attachment';
|
||||
import { Flex } from '../../../basic/Flex';
|
||||
import { MINIMUM_LINK_PREVIEW_IMAGE_WIDTH } from '../message-item/Message';
|
||||
import { MessageAttachment } from './MessageAttachment';
|
||||
import { MessagePreview } from './MessagePreview';
|
||||
import { MessageQuote } from './MessageQuote';
|
|
@ -2,13 +2,14 @@ import classNames from 'classnames';
|
|||
import React, { useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import _ from 'underscore';
|
||||
import { replyToMessage } from '../../../interactions/conversationInteractions';
|
||||
import { MessageRenderingProps, QuoteClickOptions } from '../../../models/messageType';
|
||||
import { toggleSelectedMessageId } from '../../../state/ducks/conversations';
|
||||
import { replyToMessage } from '../../../../interactions/conversationInteractions';
|
||||
import { MessageRenderingProps, QuoteClickOptions } from '../../../../models/messageType';
|
||||
import { toggleSelectedMessageId } from '../../../../state/ducks/conversations';
|
||||
import {
|
||||
getMessageContentWithStatusesSelectorProps,
|
||||
isMessageSelectionMode,
|
||||
} from '../../../state/selectors/conversations';
|
||||
} from '../../../../state/selectors/conversations';
|
||||
|
||||
import { MessageAuthorText } from './MessageAuthorText';
|
||||
import { MessageContent } from './MessageContent';
|
||||
import { MessageContextMenu } from './MessageContextMenu';
|
|
@ -2,26 +2,26 @@ import React, { useCallback } from 'react';
|
|||
|
||||
import { animation, Item, Menu } from 'react-contexify';
|
||||
|
||||
import { MessageInteraction } from '../../../interactions';
|
||||
import { getMessageById } from '../../../data/data';
|
||||
import { replyToMessage } from '../../../interactions/conversationInteractions';
|
||||
import {
|
||||
showMessageDetailsView,
|
||||
toggleSelectedMessageId,
|
||||
} from '../../../state/ducks/conversations';
|
||||
import { saveAttachmentToDisk } from '../../../util/attachmentsUtil';
|
||||
import {
|
||||
addSenderAsModerator,
|
||||
removeSenderFromModerator,
|
||||
} from '../../../interactions/messageInteractions';
|
||||
import { MessageRenderingProps } from '../../../models/messageType';
|
||||
import { pushUnblockToSend } from '../../../session/utils/Toast';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { getMessageContextMenuProps } from '../../../state/selectors/conversations';
|
||||
import { getMessageById } from '../../../../data/data';
|
||||
import { MessageInteraction } from '../../../../interactions';
|
||||
import { replyToMessage } from '../../../../interactions/conversationInteractions';
|
||||
import {
|
||||
deleteMessagesById,
|
||||
deleteMessagesByIdForEveryone,
|
||||
} from '../../../interactions/conversations/unsendingInteractions';
|
||||
} from '../../../../interactions/conversations/unsendingInteractions';
|
||||
import {
|
||||
addSenderAsModerator,
|
||||
removeSenderFromModerator,
|
||||
} from '../../../../interactions/messageInteractions';
|
||||
import { MessageRenderingProps } from '../../../../models/messageType';
|
||||
import { pushUnblockToSend } from '../../../../session/utils/Toast';
|
||||
import {
|
||||
showMessageDetailsView,
|
||||
toggleSelectedMessageId,
|
||||
} from '../../../../state/ducks/conversations';
|
||||
import { getMessageContextMenuProps } from '../../../../state/selectors/conversations';
|
||||
import { saveAttachmentToDisk } from '../../../../util/attachmentsUtil';
|
||||
|
||||
export type MessageContextMenuSelectorProps = Pick<
|
||||
MessageRenderingProps,
|
||||
|
@ -183,7 +183,9 @@ export const MessageContextMenu = (props: Props) => {
|
|||
|
||||
<Item onClick={copyText}>{window.i18n('copyMessage')}</Item>
|
||||
{(isSent || !isOutgoing) && <Item onClick={onReply}>{window.i18n('replyToMessage')}</Item>}
|
||||
<Item onClick={onShowDetail}>{window.i18n('moreInformation')}</Item>
|
||||
{(!isPublic || isOutgoing) && (
|
||||
<Item onClick={onShowDetail}>{window.i18n('moreInformation')}</Item>
|
||||
)}
|
||||
{showRetry ? <Item onClick={onRetry}>{window.i18n('resend')}</Item> : null}
|
||||
{isDeletable ? (
|
||||
<>
|
|
@ -1,13 +1,13 @@
|
|||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import { isImageAttachment } from '../../../types/Attachment';
|
||||
import { SessionIcon } from '../../session/icon';
|
||||
import { ImageGrid } from '../ImageGrid';
|
||||
import { Image } from '../Image';
|
||||
import { MINIMUM_LINK_PREVIEW_IMAGE_WIDTH } from '../Message';
|
||||
import { MessageRenderingProps } from '../../../models/messageType';
|
||||
import { isImageAttachment } from '../../../../types/Attachment';
|
||||
import { ImageGrid } from '../../ImageGrid';
|
||||
import { Image } from '../../Image';
|
||||
import { MessageRenderingProps } from '../../../../models/messageType';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { getMessagePreviewProps } from '../../../state/selectors/conversations';
|
||||
import { getMessagePreviewProps } from '../../../../state/selectors/conversations';
|
||||
import { SessionIcon } from '../../../icon';
|
||||
import { MINIMUM_LINK_PREVIEW_IMAGE_WIDTH } from '../message-item/Message';
|
||||
|
||||
export type MessagePreviewSelectorProps = Pick<MessageRenderingProps, 'attachments' | 'previews'>;
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import _ from 'lodash';
|
||||
import { MessageRenderingProps, QuoteClickOptions } from '../../../models/messageType';
|
||||
import { PubKey } from '../../../session/types';
|
||||
import { toggleSelectedMessageId } from '../../../state/ducks/conversations';
|
||||
import { MessageRenderingProps, QuoteClickOptions } from '../../../../models/messageType';
|
||||
import { PubKey } from '../../../../session/types';
|
||||
import { toggleSelectedMessageId } from '../../../../state/ducks/conversations';
|
||||
import {
|
||||
getMessageQuoteProps,
|
||||
isMessageSelectionMode,
|
||||
} from '../../../state/selectors/conversations';
|
||||
import { Quote } from '../Quote';
|
||||
} from '../../../../state/selectors/conversations';
|
||||
import { Quote } from './Quote';
|
||||
|
||||
// tslint:disable: use-simple-attributes
|
||||
|
||||
type Props = {
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { MessageRenderingProps } from '../../../models/messageType';
|
||||
import { getMessageStatusProps } from '../../../state/selectors/conversations';
|
||||
import { MessageRenderingProps } from '../../../../models/messageType';
|
||||
import { getMessageStatusProps } from '../../../../state/selectors/conversations';
|
||||
import { OutgoingMessageStatus } from './OutgoingMessageStatus';
|
||||
|
||||
type Props = {
|
|
@ -1,13 +1,13 @@
|
|||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { MessageRenderingProps } from '../../../models/messageType';
|
||||
import { MessageRenderingProps } from '../../../../models/messageType';
|
||||
import {
|
||||
getMessageTextProps,
|
||||
isMessageSelectionMode,
|
||||
} from '../../../state/selectors/conversations';
|
||||
import { SessionIcon } from '../../session/icon';
|
||||
import { MessageBody } from '../MessageBody';
|
||||
} from '../../../../state/selectors/conversations';
|
||||
import { SessionIcon } from '../../../icon';
|
||||
import { MessageBody } from './MessageBody';
|
||||
|
||||
type Props = {
|
||||
messageId: string;
|
|
@ -1,8 +1,8 @@
|
|||
import { ipcRenderer } from 'electron';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { MessageDeliveryStatus } from '../../../models/messageType';
|
||||
import { SessionIcon } from '../../session/icon';
|
||||
import { MessageDeliveryStatus } from '../../../../models/messageType';
|
||||
import { SessionIcon } from '../../../icon';
|
||||
|
||||
const MessageStatusSendingContainer = styled.div`
|
||||
display: inline-block;
|
|
@ -1,22 +1,22 @@
|
|||
import React, { useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import * as MIME from '../../../ts/types/MIME';
|
||||
import * as GoogleChrome from '../../../ts/util/GoogleChrome';
|
||||
import * as MIME from '../../../../../ts/types/MIME';
|
||||
import * as GoogleChrome from '../../../../../ts/util/GoogleChrome';
|
||||
|
||||
import { MessageBody } from './MessageBody';
|
||||
import { ContactName } from './ContactName';
|
||||
import { PubKey } from '../../session/types';
|
||||
|
||||
import { useEncryptedFileFetch } from '../../hooks/useEncryptedFileFetch';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { noop } from 'underscore';
|
||||
import { useDisableDrag } from '../../../../hooks/useDisableDrag';
|
||||
import { useEncryptedFileFetch } from '../../../../hooks/useEncryptedFileFetch';
|
||||
import { PubKey } from '../../../../session/types';
|
||||
import {
|
||||
getSelectedConversationKey,
|
||||
isGroupConversation,
|
||||
isPublicGroupConversation,
|
||||
} from '../../state/selectors/conversations';
|
||||
import { noop } from 'underscore';
|
||||
import { useDisableDrag } from '../../hooks/useDisableDrag';
|
||||
} from '../../../../state/selectors/conversations';
|
||||
import { ContactName } from '../../ContactName';
|
||||
import { MessageBody } from './MessageBody';
|
||||
|
||||
export type QuotePropsWithoutListener = {
|
||||
attachment?: QuotedAttachmentType;
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import { PropsForDataExtractionNotification } from '../../models/messageType';
|
||||
import { SignalService } from '../../protobuf';
|
||||
import { Flex } from '../basic/Flex';
|
||||
import { SessionIcon } from '../session/icon';
|
||||
import { SpacerSM, Text } from '../basic/Text';
|
||||
import { PropsForDataExtractionNotification } from '../../../../models/messageType';
|
||||
import { SignalService } from '../../../../protobuf';
|
||||
import { Flex } from '../../../basic/Flex';
|
||||
import { SpacerSM, Text } from '../../../basic/Text';
|
||||
import { SessionIcon } from '../../../icon';
|
||||
import { ReadableMessage } from './ReadableMessage';
|
||||
|
||||
export const DataExtractionNotification = (props: PropsForDataExtractionNotification) => {
|
|
@ -5,21 +5,21 @@ import { useDispatch, useSelector } from 'react-redux';
|
|||
// tslint:disable-next-line: no-submodule-imports
|
||||
import useInterval from 'react-use/lib/useInterval';
|
||||
import _ from 'lodash';
|
||||
import { removeMessage } from '../../../data/data';
|
||||
import { MessageRenderingProps, QuoteClickOptions } from '../../../models/messageType';
|
||||
import { getConversationController } from '../../../session/conversations';
|
||||
import { messageExpired } from '../../../state/ducks/conversations';
|
||||
import { removeMessage } from '../../../../data/data';
|
||||
import { MessageRenderingProps, QuoteClickOptions } from '../../../../models/messageType';
|
||||
import { getConversationController } from '../../../../session/conversations';
|
||||
import { messageExpired } from '../../../../state/ducks/conversations';
|
||||
import {
|
||||
getGenericReadableMessageSelectorProps,
|
||||
getIsMessageSelected,
|
||||
getQuotedMessageToAnimate,
|
||||
isMessageSelectionMode,
|
||||
} from '../../../state/selectors/conversations';
|
||||
import { getIncrement } from '../../../util/timer';
|
||||
import { ExpireTimer } from '../ExpireTimer';
|
||||
import { ReadableMessage } from '../ReadableMessage';
|
||||
import { MessageAvatar } from './MessageAvatar';
|
||||
import { MessageContentWithStatuses } from './MessageContentWithStatus';
|
||||
} from '../../../../state/selectors/conversations';
|
||||
import { getIncrement } from '../../../../util/timer';
|
||||
import { ExpireTimer } from '../../ExpireTimer';
|
||||
import { MessageAvatar } from '../message-content/MessageAvatar';
|
||||
import { MessageContentWithStatuses } from '../message-content/MessageContentWithStatus';
|
||||
import { ReadableMessage } from './ReadableMessage';
|
||||
|
||||
export type GenericReadableMessageSelectorProps = Pick<
|
||||
MessageRenderingProps,
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { SessionIconButton } from '../session/icon';
|
||||
import { PropsForGroupInvitation } from '../../state/ducks/conversations';
|
||||
import { acceptOpenGroupInvitation } from '../../interactions/messageInteractions';
|
||||
import { PropsForGroupInvitation } from '../../../../state/ducks/conversations';
|
||||
import { acceptOpenGroupInvitation } from '../../../../interactions/messageInteractions';
|
||||
import { SessionIconButton } from '../../../icon';
|
||||
import { ReadableMessage } from './ReadableMessage';
|
||||
|
||||
export const GroupInvitation = (props: PropsForGroupInvitation) => {
|
|
@ -2,10 +2,10 @@ import React from 'react';
|
|||
|
||||
import _ from 'lodash';
|
||||
import uuid from 'uuid';
|
||||
import { QuoteClickOptions } from '../../models/messageType';
|
||||
import { GenericReadableMessage } from './message/GenericReadableMessage';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { getGenericReadableMessageSelectorProps } from '../../state/selectors/conversations';
|
||||
import { QuoteClickOptions } from '../../../../models/messageType';
|
||||
import { getGenericReadableMessageSelectorProps } from '../../../../state/selectors/conversations';
|
||||
import { GenericReadableMessage } from './GenericReadableMessage';
|
||||
|
||||
// Same as MIN_WIDTH in ImageGrid.tsx
|
||||
export const MINIMUM_LINK_PREVIEW_IMAGE_WIDTH = 200;
|
|
@ -2,16 +2,16 @@ import React from 'react';
|
|||
import classNames from 'classnames';
|
||||
import moment from 'moment';
|
||||
|
||||
import { Avatar, AvatarSize } from '../Avatar';
|
||||
import { ContactName } from './ContactName';
|
||||
import { Message } from './Message';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { ContactPropsMessageDetail } from '../../state/ducks/conversations';
|
||||
import { Avatar, AvatarSize } from '../../../avatar/Avatar';
|
||||
import { deleteMessagesById } from '../../../../interactions/conversations/unsendingInteractions';
|
||||
import { ContactPropsMessageDetail } from '../../../../state/ducks/conversations';
|
||||
import {
|
||||
getMessageDetailsViewProps,
|
||||
getMessageIsDeletable,
|
||||
} from '../../state/selectors/conversations';
|
||||
import { deleteMessagesById } from '../../interactions/conversations/unsendingInteractions';
|
||||
} from '../../../../state/selectors/conversations';
|
||||
import { ContactName } from '../../ContactName';
|
||||
|
||||
const AvatarItem = (props: { pubkey: string }) => {
|
||||
const { pubkey } = props;
|
|
@ -2,14 +2,14 @@ import _, { noop } from 'lodash';
|
|||
import React, { useCallback } from 'react';
|
||||
import { InView } from 'react-intersection-observer';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { getMessageById } from '../../data/data';
|
||||
import { Constants } from '../../session';
|
||||
import { getConversationController } from '../../session/conversations';
|
||||
import { getMessageById } from '../../../../data/data';
|
||||
import { Constants } from '../../../../session';
|
||||
import { getConversationController } from '../../../../session/conversations';
|
||||
import {
|
||||
fetchMessagesForConversation,
|
||||
markConversationFullyRead,
|
||||
showScrollToBottomButton,
|
||||
} from '../../state/ducks/conversations';
|
||||
} from '../../../../state/ducks/conversations';
|
||||
import {
|
||||
areMoreMessagesBeingFetched,
|
||||
getHaveDoneFirstScroll,
|
||||
|
@ -17,8 +17,8 @@ import {
|
|||
getMostRecentMessageId,
|
||||
getOldestMessageId,
|
||||
getSelectedConversationKey,
|
||||
} from '../../state/selectors/conversations';
|
||||
import { getIsAppFocused } from '../../state/selectors/section';
|
||||
} from '../../../../state/selectors/conversations';
|
||||
import { getIsAppFocused } from '../../../../state/selectors/section';
|
||||
|
||||
type ReadableMessageProps = {
|
||||
children: React.ReactNode;
|
|
@ -1,11 +1,14 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { PubKey } from '../../../session/types';
|
||||
import { PubKey } from '../../../../../session/types';
|
||||
|
||||
import { CallNotificationType, PropsForCallNotification } from '../../../state/ducks/conversations';
|
||||
import { getSelectedConversation } from '../../../state/selectors/conversations';
|
||||
import { LocalizerKeys } from '../../../types/LocalizerKeys';
|
||||
import { SessionIconType } from '../../session/icon';
|
||||
import {
|
||||
CallNotificationType,
|
||||
PropsForCallNotification,
|
||||
} from '../../../../../state/ducks/conversations';
|
||||
import { getSelectedConversation } from '../../../../../state/selectors/conversations';
|
||||
import { LocalizerKeys } from '../../../../../types/LocalizerKeys';
|
||||
import { SessionIconType } from '../../../../icon';
|
||||
import { ReadableMessage } from '../ReadableMessage';
|
||||
import { NotificationBubble } from './NotificationBubble';
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { SessionIcon, SessionIconType } from '../../session/icon';
|
||||
import { SessionIcon, SessionIconType } from '../../../../icon';
|
||||
|
||||
const NotificationBubbleFlex = styled.div`
|
||||
display: flex;
|
|
@ -1,10 +1,9 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
import { SessionButton, SessionButtonColor } from '../session/SessionButton';
|
||||
import { SessionWrapperModal } from '../session/SessionWrapperModal';
|
||||
import { SpacerLG } from '../basic/Text';
|
||||
import { getConversationController } from '../../session/conversations';
|
||||
import { adminLeaveClosedGroup } from '../../state/ducks/modalDialog';
|
||||
import { SessionButton, SessionButtonColor } from '../basic/SessionButton';
|
||||
import { SessionWrapperModal } from '../SessionWrapperModal';
|
||||
|
||||
type Props = {
|
||||
conversationId: string;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import React, { useCallback, useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { ed25519Str } from '../../session/onions/onionPath';
|
||||
import { forceNetworkDeletion } from '../../session/snode_api/SNodeAPI';
|
||||
import { forceNetworkDeletion } from '../../session/apis/snode_api/SNodeAPI';
|
||||
import { forceSyncConfigurationNowIfNeeded } from '../../session/utils/syncUtils';
|
||||
import { updateConfirmModal, updateDeleteAccountModal } from '../../state/ducks/modalDialog';
|
||||
import { SpacerLG } from '../basic/Text';
|
||||
import { SessionButton, SessionButtonColor } from '../session/SessionButton';
|
||||
import { SessionHtmlRenderer } from '../session/SessionHTMLRenderer';
|
||||
import { SessionSpinner } from '../session/SessionSpinner';
|
||||
import { SessionWrapperModal } from '../session/SessionWrapperModal';
|
||||
import { SessionButton, SessionButtonColor } from '../basic/SessionButton';
|
||||
import { SessionHtmlRenderer } from '../basic/SessionHTMLRenderer';
|
||||
import { SessionSpinner } from '../basic/SessionSpinner';
|
||||
import { SessionWrapperModal } from '../SessionWrapperModal';
|
||||
|
||||
const deleteDbLocally = async () => {
|
||||
window?.log?.info('last message sent successfully. Deleting everything');
|
||||
|
|
|
@ -2,24 +2,24 @@ import React from 'react';
|
|||
import classNames from 'classnames';
|
||||
import { QRCode } from 'react-qr-svg';
|
||||
|
||||
import { Avatar, AvatarSize } from '../Avatar';
|
||||
import { Avatar, AvatarSize } from '../avatar/Avatar';
|
||||
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from '../session/SessionButton';
|
||||
|
||||
import { SessionIconButton } from '../session/icon';
|
||||
import { PillDivider } from '../session/PillDivider';
|
||||
import { PillDivider } from '../basic/PillDivider';
|
||||
import { SyncUtils, ToastUtils, UserUtils } from '../../session/utils';
|
||||
import { MAX_USERNAME_LENGTH } from '../session/registration/RegistrationStages';
|
||||
import { SessionSpinner } from '../session/SessionSpinner';
|
||||
|
||||
import { ConversationModel, ConversationTypeEnum } from '../../models/conversation';
|
||||
|
||||
import { SessionWrapperModal } from '../session/SessionWrapperModal';
|
||||
import { AttachmentUtil } from '../../util';
|
||||
import { getConversationController } from '../../session/conversations';
|
||||
import { SpacerLG, SpacerMD } from '../basic/Text';
|
||||
import autoBind from 'auto-bind';
|
||||
import { editProfileModal } from '../../state/ducks/modalDialog';
|
||||
import { uploadOurAvatar } from '../../interactions/conversationInteractions';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
|
||||
import { SessionSpinner } from '../basic/SessionSpinner';
|
||||
import { SessionIconButton } from '../icon';
|
||||
import { MAX_USERNAME_LENGTH } from '../registration/RegistrationStages';
|
||||
import { SessionWrapperModal } from '../SessionWrapperModal';
|
||||
|
||||
interface State {
|
||||
profileName: string;
|
||||
|
|
|
@ -1,87 +1,134 @@
|
|||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { SessionButton, SessionButtonColor } from '../session/SessionButton';
|
||||
import { ContactType, SessionMemberListItem } from '../session/SessionMemberListItem';
|
||||
import { getConversationController } from '../../session/conversations';
|
||||
import { ToastUtils, UserUtils } from '../../session/utils';
|
||||
import { initiateGroupUpdate } from '../../session/group';
|
||||
import { ConversationModel, ConversationTypeEnum } from '../../models/conversation';
|
||||
import { ConversationTypeEnum } from '../../models/conversation';
|
||||
import { getCompleteUrlForV2ConvoId } from '../../interactions/conversationInteractions';
|
||||
import _ from 'lodash';
|
||||
import { VALIDATION } from '../../session/constants';
|
||||
import { SessionWrapperModal } from '../session/SessionWrapperModal';
|
||||
import { SpacerLG } from '../basic/Text';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { updateInviteContactModal } from '../../state/ducks/modalDialog';
|
||||
// tslint:disable-next-line: no-submodule-imports
|
||||
import useKey from 'react-use/lib/useKey';
|
||||
import { SessionButton, SessionButtonColor } from '../basic/SessionButton';
|
||||
import { MemberListItem } from '../MemberListItem';
|
||||
import { SessionWrapperModal } from '../SessionWrapperModal';
|
||||
import { getPrivateContactsPubkeys } from '../../state/selectors/conversations';
|
||||
import { useConversationPropsById } from '../../hooks/useParamSelector';
|
||||
import { useSet } from '../../hooks/useSet';
|
||||
|
||||
type Props = {
|
||||
conversationId: string;
|
||||
};
|
||||
|
||||
const submitForOpenGroup = async (conversationId: string, pubkeys: Array<string>) => {
|
||||
const completeUrl = await getCompleteUrlForV2ConvoId(conversationId);
|
||||
const convo = getConversationController().get(conversationId);
|
||||
if (!convo || !convo.isPublic()) {
|
||||
throw new Error('submitForOpenGroup group not found');
|
||||
}
|
||||
const groupInvitation = {
|
||||
url: completeUrl,
|
||||
name: convo.getName() || 'Unknown',
|
||||
};
|
||||
pubkeys.forEach(async pubkeyStr => {
|
||||
const privateConvo = await getConversationController().getOrCreateAndWait(
|
||||
pubkeyStr,
|
||||
ConversationTypeEnum.PRIVATE
|
||||
);
|
||||
|
||||
if (privateConvo) {
|
||||
void privateConvo.sendMessage({
|
||||
body: '',
|
||||
attachments: undefined,
|
||||
groupInvitation,
|
||||
preview: undefined,
|
||||
quote: undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const submitForClosedGroup = async (convoId: string, pubkeys: Array<string>) => {
|
||||
const convo = getConversationController().get(convoId);
|
||||
if (!convo || !convo.isGroup()) {
|
||||
throw new Error('submitForClosedGroup group not found');
|
||||
}
|
||||
// closed group chats
|
||||
const ourPK = UserUtils.getOurPubKeyStrFromCache();
|
||||
// we only care about real members. If a member is currently a zombie we have to be able to add him back
|
||||
let existingMembers = convo.get('members') || [];
|
||||
// at least make sure it's an array
|
||||
if (!Array.isArray(existingMembers)) {
|
||||
existingMembers = [];
|
||||
}
|
||||
existingMembers = _.compact(existingMembers);
|
||||
const existingZombies = convo.get('zombies') || [];
|
||||
const newMembers = pubkeys.filter(d => !existingMembers.includes(d));
|
||||
|
||||
if (newMembers.length > 0) {
|
||||
// Do not trigger an update if there is too many members
|
||||
// be sure to include current zombies in this count
|
||||
if (
|
||||
newMembers.length + existingMembers.length + existingZombies.length >
|
||||
VALIDATION.CLOSED_GROUP_SIZE_LIMIT
|
||||
) {
|
||||
ToastUtils.pushTooManyMembers();
|
||||
return;
|
||||
}
|
||||
|
||||
const allMembers = _.concat(existingMembers, newMembers, [ourPK]);
|
||||
const uniqMembers = _.uniq(allMembers);
|
||||
|
||||
const groupId = convo.get('id');
|
||||
const groupName = convo.get('name');
|
||||
|
||||
await initiateGroupUpdate(groupId, groupName || window.i18n('unknown'), uniqMembers, undefined);
|
||||
}
|
||||
};
|
||||
|
||||
// tslint:disable-next-line: max-func-body-length
|
||||
const InviteContactsDialogInner = (props: Props) => {
|
||||
const { conversationId } = props;
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const convo = getConversationController().get(conversationId);
|
||||
// tslint:disable-next-line: max-func-body-length
|
||||
const privateContactPubkeys = useSelector(getPrivateContactsPubkeys);
|
||||
let validContactsForInvite = _.clone(privateContactPubkeys);
|
||||
|
||||
let contacts = getConversationController()
|
||||
.getConversations()
|
||||
.filter(d => !!d && !d.isBlocked() && d.isPrivate() && !d.isMe() && !!d.get('active_at'));
|
||||
if (!convo.isPublic()) {
|
||||
const convoProps = useConversationPropsById(conversationId);
|
||||
|
||||
const { uniqueValues: selectedContacts, addTo, removeFrom } = useSet<string>();
|
||||
|
||||
if (!convoProps) {
|
||||
throw new Error('InviteContactsDialogInner not a valid convoId given');
|
||||
}
|
||||
if (!convoProps.isGroup) {
|
||||
throw new Error('InviteContactsDialogInner must be a group');
|
||||
}
|
||||
if (!convoProps.isPublic) {
|
||||
// filter our zombies and current members from the list of contact we can add
|
||||
|
||||
const members = convo.get('members') || [];
|
||||
const zombies = convo.get('zombies') || [];
|
||||
contacts = contacts.filter(d => !members.includes(d.id) && !zombies.includes(d.id));
|
||||
const members = convoProps.members || [];
|
||||
const zombies = convoProps.zombies || [];
|
||||
validContactsForInvite = validContactsForInvite.filter(
|
||||
d => !members.includes(d) && !zombies.includes(d)
|
||||
);
|
||||
}
|
||||
|
||||
const chatName = convo.get('name');
|
||||
const isPublicConvo = convo.isPublic();
|
||||
|
||||
const [contactList, setContactList] = useState(
|
||||
contacts.map((d: ConversationModel) => {
|
||||
const lokiProfile = d.getLokiProfile();
|
||||
const nickname = d.getNickname();
|
||||
const name = nickname
|
||||
? nickname
|
||||
: lokiProfile
|
||||
? lokiProfile.displayName
|
||||
: window.i18n('anonymous');
|
||||
|
||||
// TODO: should take existing members into account
|
||||
const existingMember = false;
|
||||
|
||||
return {
|
||||
id: d.id,
|
||||
authorPhoneNumber: d.id,
|
||||
authorProfileName: name,
|
||||
authorAvatarPath: d?.getAvatarPath() || '',
|
||||
selected: false,
|
||||
authorName: name,
|
||||
checkmarked: false,
|
||||
existingMember,
|
||||
};
|
||||
})
|
||||
);
|
||||
const chatName = convoProps.name;
|
||||
const isPublicConvo = convoProps.isPublic;
|
||||
|
||||
const closeDialog = () => {
|
||||
dispatch(updateInviteContactModal(null));
|
||||
};
|
||||
|
||||
const onClickOK = () => {
|
||||
const selectedContacts = contactList
|
||||
.filter((d: ContactType) => d.checkmarked)
|
||||
.map((d: ContactType) => d.id);
|
||||
|
||||
if (selectedContacts.length > 0) {
|
||||
if (isPublicConvo) {
|
||||
void submitForOpenGroup(selectedContacts);
|
||||
void submitForOpenGroup(conversationId, selectedContacts);
|
||||
} else {
|
||||
void submitForClosedGroup(selectedContacts);
|
||||
void submitForClosedGroup(conversationId, selectedContacts);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,123 +143,37 @@ const InviteContactsDialogInner = (props: Props) => {
|
|||
return event.key === 'Esc' || event.key === 'Escape';
|
||||
}, closeDialog);
|
||||
|
||||
const titleText = `${window.i18n('addingContacts')} ${chatName}`;
|
||||
const unknown = window.i18n('unknown');
|
||||
|
||||
const titleText = `${window.i18n('addingContacts', [chatName || unknown])}`;
|
||||
const cancelText = window.i18n('cancel');
|
||||
const okText = window.i18n('ok');
|
||||
|
||||
const hasContacts = contactList.length !== 0;
|
||||
|
||||
const submitForOpenGroup = async (pubkeys: Array<string>) => {
|
||||
if (convo.isOpenGroupV2()) {
|
||||
const completeUrl = await getCompleteUrlForV2ConvoId(convo.id);
|
||||
const groupInvitation = {
|
||||
url: completeUrl,
|
||||
name: convo.getName() || 'Unknown',
|
||||
};
|
||||
pubkeys.forEach(async pubkeyStr => {
|
||||
const privateConvo = await getConversationController().getOrCreateAndWait(
|
||||
pubkeyStr,
|
||||
ConversationTypeEnum.PRIVATE
|
||||
);
|
||||
|
||||
if (privateConvo) {
|
||||
void privateConvo.sendMessage({
|
||||
body: '',
|
||||
attachments: undefined,
|
||||
groupInvitation,
|
||||
preview: undefined,
|
||||
quote: undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const submitForClosedGroup = async (pubkeys: Array<string>) => {
|
||||
// closed group chats
|
||||
const ourPK = UserUtils.getOurPubKeyStrFromCache();
|
||||
// we only care about real members. If a member is currently a zombie we have to be able to add him back
|
||||
let existingMembers = convo.get('members') || [];
|
||||
// at least make sure it's an array
|
||||
if (!Array.isArray(existingMembers)) {
|
||||
existingMembers = [];
|
||||
}
|
||||
existingMembers = _.compact(existingMembers);
|
||||
const existingZombies = convo.get('zombies') || [];
|
||||
const newMembers = pubkeys.filter(d => !existingMembers.includes(d));
|
||||
|
||||
if (newMembers.length > 0) {
|
||||
// Do not trigger an update if there is too many members
|
||||
// be sure to include current zombies in this count
|
||||
if (
|
||||
newMembers.length + existingMembers.length + existingZombies.length >
|
||||
VALIDATION.CLOSED_GROUP_SIZE_LIMIT
|
||||
) {
|
||||
ToastUtils.pushTooManyMembers();
|
||||
return;
|
||||
}
|
||||
|
||||
const allMembers = _.concat(existingMembers, newMembers, [ourPK]);
|
||||
const uniqMembers = _.uniq(allMembers);
|
||||
|
||||
const groupId = convo.get('id');
|
||||
const groupName = convo.get('name');
|
||||
|
||||
await initiateGroupUpdate(
|
||||
groupId,
|
||||
groupName || window.i18n('unknown'),
|
||||
uniqMembers,
|
||||
undefined
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderMemberList = () => {
|
||||
const members = contactList;
|
||||
const selectedContacts = contactList
|
||||
.filter((d: ContactType) => d.checkmarked)
|
||||
.map((d: ContactType) => d.id);
|
||||
|
||||
return members.map((member: ContactType, index: number) => (
|
||||
<SessionMemberListItem
|
||||
member={member}
|
||||
key={member.id}
|
||||
index={index}
|
||||
isSelected={selectedContacts.some(m => m === member.id)}
|
||||
onSelect={(selectedMember: ContactType) => {
|
||||
onMemberClicked(selectedMember);
|
||||
}}
|
||||
onUnselect={(selectedMember: ContactType) => {
|
||||
onMemberClicked(selectedMember);
|
||||
}}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
const onMemberClicked = (clickedMember: ContactType) => {
|
||||
const updatedContacts = contactList.map((member: ContactType) => {
|
||||
if (member.id === clickedMember.id) {
|
||||
return { ...member, checkmarked: !member.checkmarked };
|
||||
} else {
|
||||
return member;
|
||||
}
|
||||
});
|
||||
setContactList(updatedContacts);
|
||||
};
|
||||
const hasContacts = validContactsForInvite.length > 0;
|
||||
|
||||
return (
|
||||
<SessionWrapperModal title={titleText} onClose={closeDialog}>
|
||||
<SpacerLG />
|
||||
|
||||
<div className="contact-selection-list">{renderMemberList()}</div>
|
||||
{hasContacts ? null : (
|
||||
<>
|
||||
<SpacerLG />
|
||||
<p className="no-contacts">{window.i18n('noContactsToAdd')}</p>
|
||||
<SpacerLG />
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="contact-selection-list">
|
||||
{hasContacts ? (
|
||||
validContactsForInvite.map((member: string) => (
|
||||
<MemberListItem
|
||||
key={member}
|
||||
pubkey={member}
|
||||
isSelected={selectedContacts.includes(member)}
|
||||
onSelect={addTo}
|
||||
onUnselect={removeFrom}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<>
|
||||
<SpacerLG />
|
||||
<p className="no-contacts">{window.i18n('noContactsToAdd')}</p>
|
||||
<SpacerLG />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<SpacerLG />
|
||||
|
||||
<div className="session-modal__button-group">
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import React, { useState } from 'react';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from '../session/SessionButton';
|
||||
import { PubKey } from '../../session/types';
|
||||
import { ToastUtils } from '../../session/utils';
|
||||
import { SessionSpinner } from '../session/SessionSpinner';
|
||||
import { Flex } from '../basic/Flex';
|
||||
import { ApiV2 } from '../../opengroup/opengroupV2';
|
||||
import { SessionWrapperModal } from '../session/SessionWrapperModal';
|
||||
import { ApiV2 } from '../../session/apis/open_group_api/opengroupV2';
|
||||
import { getConversationController } from '../../session/conversations';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { updateAddModeratorsModal } from '../../state/ducks/modalDialog';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
|
||||
import { SessionSpinner } from '../basic/SessionSpinner';
|
||||
import { SessionWrapperModal } from '../SessionWrapperModal';
|
||||
|
||||
type Props = {
|
||||
conversationId: string;
|
||||
|
|
|
@ -1,217 +1,134 @@
|
|||
import React from 'react';
|
||||
import { ApiV2 } from '../../opengroup/opengroupV2';
|
||||
import React, { useState } from 'react';
|
||||
import { ApiV2 } from '../../session/apis/open_group_api/opengroupV2';
|
||||
import { getConversationController } from '../../session/conversations';
|
||||
import { PubKey } from '../../session/types';
|
||||
import { ToastUtils } from '../../session/utils';
|
||||
import { Flex } from '../basic/Flex';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from '../session/SessionButton';
|
||||
import { ContactType, SessionMemberListItem } from '../session/SessionMemberListItem';
|
||||
import { SessionSpinner } from '../session/SessionSpinner';
|
||||
import _ from 'lodash';
|
||||
import { SessionWrapperModal } from '../session/SessionWrapperModal';
|
||||
import { updateRemoveModeratorsModal } from '../../state/ducks/modalDialog';
|
||||
interface Props {
|
||||
import { SessionWrapperModal } from '../SessionWrapperModal';
|
||||
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
|
||||
import { SessionSpinner } from '../basic/SessionSpinner';
|
||||
import { MemberListItem } from '../MemberListItem';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useConversationPropsById } from '../../hooks/useParamSelector';
|
||||
|
||||
type Props = {
|
||||
conversationId: string;
|
||||
}
|
||||
};
|
||||
|
||||
interface State {
|
||||
modList: Array<ContactType>;
|
||||
removingInProgress: boolean;
|
||||
firstLoading: boolean;
|
||||
}
|
||||
|
||||
export class RemoveModeratorsDialog extends React.Component<Props, State> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.onModClicked = this.onModClicked.bind(this);
|
||||
this.closeDialog = this.closeDialog.bind(this);
|
||||
this.removeThem = this.removeThem.bind(this);
|
||||
|
||||
this.state = {
|
||||
modList: [],
|
||||
removingInProgress: false,
|
||||
firstLoading: true,
|
||||
};
|
||||
async function removeMods(convoId: string, modsToRemove: Array<string>) {
|
||||
if (modsToRemove.length === 0) {
|
||||
window?.log?.info('No moderators removed. Nothing todo');
|
||||
return false;
|
||||
}
|
||||
window?.log?.info(`asked to remove moderators: ${modsToRemove}`);
|
||||
|
||||
public componentDidMount() {
|
||||
this.refreshModList();
|
||||
}
|
||||
try {
|
||||
let res;
|
||||
const convo = getConversationController().get(convoId);
|
||||
|
||||
public render() {
|
||||
const { i18n } = window;
|
||||
const { removingInProgress, firstLoading } = this.state;
|
||||
const hasMods = this.state.modList.length !== 0;
|
||||
|
||||
const convo = getConversationController().get(this.props.conversationId);
|
||||
|
||||
const chatName = convo.get('name');
|
||||
|
||||
const title = `${i18n('removeModerators')}: ${chatName}`;
|
||||
|
||||
const renderContent = !firstLoading;
|
||||
|
||||
return (
|
||||
<SessionWrapperModal title={title} onClose={this.closeDialog}>
|
||||
<Flex container={true} flexDirection="column" alignItems="center">
|
||||
{renderContent && (
|
||||
<>
|
||||
<p>Existing moderators:</p>
|
||||
<div className="contact-selection-list">{this.renderMemberList()}</div>
|
||||
|
||||
{hasMods ? null : <p>{i18n('noModeratorsToRemove')}</p>}
|
||||
<SessionSpinner loading={removingInProgress} />
|
||||
|
||||
<div className="session-modal__button-group">
|
||||
<SessionButton
|
||||
buttonType={SessionButtonType.Brand}
|
||||
buttonColor={SessionButtonColor.Green}
|
||||
onClick={this.removeThem}
|
||||
disabled={removingInProgress}
|
||||
text={i18n('ok')}
|
||||
/>
|
||||
<SessionButton
|
||||
buttonType={SessionButtonType.Brand}
|
||||
buttonColor={SessionButtonColor.Primary}
|
||||
onClick={this.closeDialog}
|
||||
disabled={removingInProgress}
|
||||
text={i18n('cancel')}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<SessionSpinner loading={firstLoading} />
|
||||
</Flex>
|
||||
</SessionWrapperModal>
|
||||
const roomInfos = convo.toOpenGroupV2();
|
||||
const modsToRemovePubkey = _.compact(modsToRemove.map(m => PubKey.from(m)));
|
||||
res = await Promise.all(
|
||||
modsToRemovePubkey.map(async m => {
|
||||
return ApiV2.removeModerator(m, roomInfos);
|
||||
})
|
||||
);
|
||||
}
|
||||
// all moderators are removed means all promise resolved with bool= true
|
||||
res = res.every(r => !!r);
|
||||
|
||||
private closeDialog() {
|
||||
window.inboxStore?.dispatch(updateRemoveModeratorsModal(null));
|
||||
}
|
||||
if (!res) {
|
||||
window?.log?.warn('failed to remove moderators:', res);
|
||||
|
||||
private renderMemberList() {
|
||||
const members = this.state.modList;
|
||||
const selectedContacts = members.filter(d => d.checkmarked).map(d => d.id);
|
||||
|
||||
return members.map((member: ContactType, index: number) => (
|
||||
<SessionMemberListItem
|
||||
member={member}
|
||||
key={member.id}
|
||||
index={index}
|
||||
isSelected={selectedContacts.some(m => m === member.id)}
|
||||
onSelect={(selectedMember: ContactType) => {
|
||||
this.onModClicked(selectedMember);
|
||||
}}
|
||||
onUnselect={(selectedMember: ContactType) => {
|
||||
this.onModClicked(selectedMember);
|
||||
}}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
private onModClicked(selected: ContactType) {
|
||||
const updatedContacts = this.state.modList.map(member => {
|
||||
if (member.id === selected.id) {
|
||||
return { ...member, checkmarked: !member.checkmarked };
|
||||
} else {
|
||||
return member;
|
||||
}
|
||||
});
|
||||
|
||||
this.setState(state => {
|
||||
return {
|
||||
...state,
|
||||
modList: updatedContacts,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private refreshModList() {
|
||||
let modPubKeys: Array<string> = [];
|
||||
const convo = getConversationController().get(this.props.conversationId);
|
||||
|
||||
modPubKeys = convo.getGroupAdmins() || [];
|
||||
|
||||
const convos = getConversationController().getConversations();
|
||||
const moderatorsConvos = modPubKeys
|
||||
.map(
|
||||
pubKey =>
|
||||
convos.find(c => c.id === pubKey) || {
|
||||
id: pubKey, // memberList need a key
|
||||
authorPhoneNumber: pubKey,
|
||||
}
|
||||
)
|
||||
.filter(c => !!c);
|
||||
|
||||
const mods = moderatorsConvos.map((d: any) => {
|
||||
let name = '';
|
||||
if (d.getLokiProfile) {
|
||||
const lokiProfile = d.getLokiProfile();
|
||||
name = lokiProfile ? lokiProfile.displayName : 'Anonymous';
|
||||
}
|
||||
// TODO: should take existing members into account
|
||||
const existingMember = false;
|
||||
|
||||
return {
|
||||
id: d.id,
|
||||
authorPhoneNumber: d.id,
|
||||
authorProfileName: name,
|
||||
selected: false,
|
||||
authorAvatarPath: '',
|
||||
authorName: name,
|
||||
checkmarked: true,
|
||||
existingMember,
|
||||
};
|
||||
});
|
||||
this.setState({
|
||||
modList: mods,
|
||||
firstLoading: false,
|
||||
removingInProgress: false,
|
||||
});
|
||||
}
|
||||
|
||||
private async removeThem() {
|
||||
const removedMods = this.state.modList.filter(d => !d.checkmarked).map(d => d.id);
|
||||
|
||||
if (removedMods.length === 0) {
|
||||
window?.log?.info('No moderators removed. Nothing todo');
|
||||
return;
|
||||
}
|
||||
window?.log?.info(`asked to remove moderator: ${removedMods}`);
|
||||
|
||||
try {
|
||||
this.setState({
|
||||
removingInProgress: true,
|
||||
});
|
||||
let res;
|
||||
const convo = getConversationController().get(this.props.conversationId);
|
||||
|
||||
const roomInfos = convo.toOpenGroupV2();
|
||||
const modsToRemove = _.compact(removedMods.map(m => PubKey.from(m)));
|
||||
res = await Promise.all(
|
||||
modsToRemove.map(async m => {
|
||||
return ApiV2.removeModerator(m, roomInfos);
|
||||
})
|
||||
);
|
||||
// all moderators are removed means all promise resolved with bool= true
|
||||
res = res.every(r => !!r);
|
||||
|
||||
if (!res) {
|
||||
window?.log?.warn('failed to remove moderators:', res);
|
||||
|
||||
ToastUtils.pushFailedToRemoveFromModerator();
|
||||
} else {
|
||||
window?.log?.info(`${removedMods} removed from moderators...`);
|
||||
ToastUtils.pushUserRemovedFromModerators();
|
||||
this.closeDialog();
|
||||
}
|
||||
} catch (e) {
|
||||
window?.log?.error('Got error while removing moderator:', e);
|
||||
} finally {
|
||||
this.refreshModList();
|
||||
ToastUtils.pushFailedToRemoveFromModerator();
|
||||
return false;
|
||||
} else {
|
||||
window?.log?.info(`${modsToRemove} removed from moderators...`);
|
||||
ToastUtils.pushUserRemovedFromModerators();
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
window?.log?.error('Got error while removing moderator:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const RemoveModeratorsDialog = (props: Props) => {
|
||||
const { conversationId } = props;
|
||||
const [removingInProgress, setRemovingInProgress] = useState(false);
|
||||
const [modsToRemove, setModsToRemove] = useState<Array<string>>([]);
|
||||
const { i18n } = window;
|
||||
const dispatch = useDispatch();
|
||||
const closeDialog = () => {
|
||||
dispatch(updateRemoveModeratorsModal(null));
|
||||
};
|
||||
|
||||
const removeModsCall = async () => {
|
||||
if (modsToRemove.length) {
|
||||
setRemovingInProgress(true);
|
||||
const removed = await removeMods(conversationId, modsToRemove);
|
||||
setRemovingInProgress(false);
|
||||
if (removed) {
|
||||
closeDialog();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const convoProps = useConversationPropsById(conversationId);
|
||||
if (!convoProps || !convoProps.isPublic || !convoProps.weAreAdmin) {
|
||||
throw new Error('RemoveModeratorsDialog: convoProps invalid');
|
||||
}
|
||||
|
||||
const existingMods = convoProps.groupAdmins || [];
|
||||
const hasMods = existingMods.length !== 0;
|
||||
|
||||
const title = `${i18n('removeModerators')}: ${convoProps.name}`;
|
||||
return (
|
||||
<SessionWrapperModal title={title} onClose={closeDialog}>
|
||||
<Flex container={true} flexDirection="column" alignItems="center">
|
||||
{hasMods ? (
|
||||
<div className="contact-selection-list">
|
||||
{existingMods.map(modId => (
|
||||
<MemberListItem
|
||||
key={modId}
|
||||
pubkey={modId}
|
||||
isSelected={modsToRemove.some(m => m === modId)}
|
||||
onSelect={(selectedMember: string) => {
|
||||
const updatedList = [...modsToRemove, selectedMember];
|
||||
setModsToRemove(updatedList);
|
||||
}}
|
||||
onUnselect={(selectedMember: string) => {
|
||||
const updatedList = modsToRemove.filter(m => m !== selectedMember);
|
||||
setModsToRemove(updatedList);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p>{i18n('noModeratorsToRemove')}</p>
|
||||
)}
|
||||
<SessionSpinner loading={removingInProgress} />
|
||||
|
||||
<div className="session-modal__button-group">
|
||||
<SessionButton
|
||||
buttonType={SessionButtonType.Brand}
|
||||
buttonColor={SessionButtonColor.Green}
|
||||
onClick={removeModsCall}
|
||||
disabled={removingInProgress}
|
||||
text={i18n('ok')}
|
||||
/>
|
||||
<SessionButton
|
||||
buttonType={SessionButtonType.Brand}
|
||||
buttonColor={SessionButtonColor.Primary}
|
||||
onClick={closeDialog}
|
||||
disabled={removingInProgress}
|
||||
text={i18n('cancel')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SessionSpinner loading={removingInProgress} />
|
||||
</Flex>
|
||||
</SessionWrapperModal>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -15,11 +15,11 @@ import {
|
|||
getOnionPathsCount,
|
||||
} from '../../state/selectors/onions';
|
||||
import { Flex } from '../basic/Flex';
|
||||
import { SessionIcon, SessionIconButton } from '../session/icon';
|
||||
import { SessionSpinner } from '../session/SessionSpinner';
|
||||
import { SessionWrapperModal } from '../session/SessionWrapperModal';
|
||||
// tslint:disable-next-line: no-submodule-imports
|
||||
import useHover from 'react-use/lib/useHover';
|
||||
import { SessionSpinner } from '../basic/SessionSpinner';
|
||||
import { SessionIcon, SessionIconButton } from '../icon';
|
||||
import { SessionWrapperModal } from '../SessionWrapperModal';
|
||||
|
||||
export type StatusLightType = {
|
||||
glowStartDelay: number;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React, { useState } from 'react';
|
||||
import { SessionButton, SessionButtonColor } from '../session/SessionButton';
|
||||
import { SessionHtmlRenderer } from '../session/SessionHTMLRenderer';
|
||||
import { SessionIcon, SessionIconSize, SessionIconType } from '../session/icon';
|
||||
import { SessionWrapperModal } from '../session/SessionWrapperModal';
|
||||
import { SessionHtmlRenderer } from '../basic/SessionHTMLRenderer';
|
||||
import { updateConfirmModal } from '../../state/ducks/modalDialog';
|
||||
import { SpacerLG } from '../basic/Text';
|
||||
import { SessionSpinner } from '../session/SessionSpinner';
|
||||
import { SessionButton, SessionButtonColor } from '../basic/SessionButton';
|
||||
import { SessionSpinner } from '../basic/SessionSpinner';
|
||||
import { SessionIcon, SessionIconSize, SessionIconType } from '../icon';
|
||||
import { SessionWrapperModal } from '../SessionWrapperModal';
|
||||
|
||||
export interface SessionConfirmDialogProps {
|
||||
message?: string;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { SessionIconButton, SessionIconType } from '../session/icon';
|
||||
import { SessionButtonColor, SessionButtonType } from '../session/SessionButton';
|
||||
import { SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
|
||||
import { SessionIconButton, SessionIconType } from '../icon';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
|
|
|
@ -5,8 +5,8 @@ import _ from 'lodash';
|
|||
import { SpacerLG } from '../basic/Text';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { changeNickNameModal } from '../../state/ducks/modalDialog';
|
||||
import { SessionButton, SessionButtonColor } from '../session/SessionButton';
|
||||
import { SessionWrapperModal } from '../session/SessionWrapperModal';
|
||||
import { SessionButton, SessionButtonColor } from '../basic/SessionButton';
|
||||
import { SessionWrapperModal } from '../SessionWrapperModal';
|
||||
|
||||
type Props = {
|
||||
conversationId: string;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue