mirror of
https://github.com/oxen-io/session-desktop.git
synced 2023-12-14 02:12:57 +01:00
split up a bit of the mentions/emoji input
This commit is contained in:
parent
2478a78794
commit
445852eca1
5 changed files with 107 additions and 73 deletions
|
@ -14,7 +14,6 @@ import {
|
|||
import { AbortController } from 'abort-controller';
|
||||
import { SessionQuotedMessageComposition } from '../SessionQuotedMessageComposition';
|
||||
import { Mention, MentionsInput, SuggestionDataItem } from 'react-mentions';
|
||||
import { MemberListItem } from '../../MemberListItem';
|
||||
import autoBind from 'auto-bind';
|
||||
import { getMediaPermissionsSettings } from '../../settings/SessionSettings';
|
||||
import { getDraftForConversation, updateDraftForConversation } from '../SessionConversationDrafts';
|
||||
|
@ -48,27 +47,13 @@ import {
|
|||
StagedAttachmentImportedType,
|
||||
StagedPreviewImportedType,
|
||||
} from '../../../util/attachmentsUtil';
|
||||
import { BaseEmoji, emojiIndex } from 'emoji-mart';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const queryEmojis = (query: string, _callback: any): Array<SuggestionDataItem> => {
|
||||
if (query.length === 0 || !emojiIndex) {
|
||||
return [];
|
||||
}
|
||||
const results = emojiIndex.search(query);
|
||||
if (!results || !results.length) {
|
||||
return [];
|
||||
}
|
||||
return results
|
||||
.map(o => {
|
||||
const onlyBaseEmokji = o as BaseEmoji;
|
||||
return {
|
||||
id: onlyBaseEmokji.native,
|
||||
display: `${onlyBaseEmokji.native} ${onlyBaseEmokji.colons}`,
|
||||
};
|
||||
})
|
||||
.slice(0, 8);
|
||||
};
|
||||
import {
|
||||
cleanMentions,
|
||||
mentionsRegex,
|
||||
renderUserMentionRow,
|
||||
styleForCompositionBoxSuggestions,
|
||||
} from './UserMentions';
|
||||
import { renderEmojiQuickResultRow, searchEmojiForQuery } from './EmojiQuickResult';
|
||||
|
||||
export interface ReplyingToMessageProps {
|
||||
convoId: string;
|
||||
|
@ -146,23 +131,7 @@ const sendMessageStyle = {
|
|||
flexGrow: 1,
|
||||
minHeight: '24px',
|
||||
width: '100%',
|
||||
|
||||
suggestions: {
|
||||
list: {
|
||||
fontSize: 14,
|
||||
boxShadow: 'rgba(0, 0, 0, 0.24) 0px 3px 8px',
|
||||
backgroundColor: 'var(--color-cell-background)',
|
||||
},
|
||||
item: {
|
||||
height: '100%',
|
||||
paddingTop: '5px',
|
||||
paddingBottom: '5px',
|
||||
|
||||
'&focused': {
|
||||
backgroundColor: 'var(--color-clickable-hovered)',
|
||||
},
|
||||
},
|
||||
},
|
||||
...styleForCompositionBoxSuggestions,
|
||||
};
|
||||
|
||||
const getDefaultState = (newConvoId?: string) => {
|
||||
|
@ -176,8 +145,6 @@ const getDefaultState = (newConvoId?: string) => {
|
|||
};
|
||||
};
|
||||
|
||||
const mentionsRegex = /@\uFFD205[0-9a-f]{64}\uFFD7[^\uFFD2]+\uFFD2/gu;
|
||||
|
||||
const getSelectionBasedOnMentions = (draft: string, index: number) => {
|
||||
// we have to get the real selectionStart/end of an index in the mentions box.
|
||||
// this is kind of a pain as the mentions box has two inputs, one with the real text, and one with the extracted mentions
|
||||
|
@ -235,23 +202,6 @@ const getSelectionBasedOnMentions = (draft: string, index: number) => {
|
|||
return Number.MAX_SAFE_INTEGER;
|
||||
};
|
||||
|
||||
// this is dirty but we have to replace all @(xxx) by @xxx manually here
|
||||
function cleanMentions(text: string): string {
|
||||
const matches = text.match(mentionsRegex);
|
||||
let replacedMentions = text;
|
||||
(matches || []).forEach(match => {
|
||||
const replacedMention = match.substring(2, match.indexOf('\uFFD7'));
|
||||
replacedMentions = replacedMentions.replace(match, `@${replacedMention}`);
|
||||
});
|
||||
|
||||
return replacedMentions;
|
||||
}
|
||||
|
||||
const EmojiQuickResult = styled.span<{ focused: boolean }>`
|
||||
height: 30px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
class CompositionBoxInner extends React.Component<Props, State> {
|
||||
private readonly textarea: React.RefObject<any>;
|
||||
private readonly fileInput: React.RefObject<HTMLInputElement>;
|
||||
|
@ -490,24 +440,15 @@ class CompositionBoxInner extends React.Component<Props, State> {
|
|||
// this is only for the composition box visible content. The real stuff on the backend box is the @markup
|
||||
displayTransform={(_id, display) => `@${display}`}
|
||||
data={this.fetchUsersForGroup}
|
||||
renderSuggestion={suggestion => (
|
||||
<MemberListItem
|
||||
isSelected={false}
|
||||
key={suggestion.id}
|
||||
pubkey={`${suggestion.id}`}
|
||||
disableBg={true}
|
||||
/>
|
||||
)}
|
||||
renderSuggestion={renderUserMentionRow}
|
||||
/>
|
||||
<Mention
|
||||
trigger=":"
|
||||
markup="__id__"
|
||||
appendSpaceOnAdd={true}
|
||||
regex={neverMatchingRegex}
|
||||
data={queryEmojis}
|
||||
renderSuggestion={(suggestion, _search, _highlightedDisplay, _index, focused) => (
|
||||
<EmojiQuickResult focused={focused}>{suggestion.display}</EmojiQuickResult>
|
||||
)}
|
||||
data={searchEmojiForQuery}
|
||||
renderSuggestion={renderEmojiQuickResultRow}
|
||||
/>
|
||||
</MentionsInput>
|
||||
);
|
||||
|
|
44
ts/components/conversation/composition/EmojiQuickResult.tsx
Normal file
44
ts/components/conversation/composition/EmojiQuickResult.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import React from 'react';
|
||||
import { SuggestionDataItem } from 'react-mentions';
|
||||
import styled from 'styled-components';
|
||||
import { BaseEmoji, emojiIndex } from 'emoji-mart';
|
||||
|
||||
const EmojiQuickResult = styled.span`
|
||||
width: 100%;
|
||||
padding-inline-end: 20px;
|
||||
padding-inline-start: 10px;
|
||||
`;
|
||||
const EmojiQuickResultIcon = styled.span`
|
||||
padding-inline-end: 20px;
|
||||
padding-inline-start: 10px;
|
||||
font-size: 1.4em;
|
||||
`;
|
||||
const EmojiQuickResultText = styled.span``;
|
||||
|
||||
export const renderEmojiQuickResultRow = (suggestion: SuggestionDataItem) => {
|
||||
return (
|
||||
<EmojiQuickResult>
|
||||
<EmojiQuickResultIcon>{suggestion.id}</EmojiQuickResultIcon>
|
||||
<EmojiQuickResultText>{suggestion.display}</EmojiQuickResultText>
|
||||
</EmojiQuickResult>
|
||||
);
|
||||
};
|
||||
|
||||
export const searchEmojiForQuery = (query: string): Array<SuggestionDataItem> => {
|
||||
if (query.length === 0 || !emojiIndex) {
|
||||
return [];
|
||||
}
|
||||
const results = emojiIndex.search(query);
|
||||
if (!results || !results.length) {
|
||||
return [];
|
||||
}
|
||||
return results
|
||||
.map(o => {
|
||||
const onlyBaseEmokji = o as BaseEmoji;
|
||||
return {
|
||||
id: onlyBaseEmokji.native,
|
||||
display: onlyBaseEmokji.colons,
|
||||
};
|
||||
})
|
||||
.slice(0, 8);
|
||||
};
|
49
ts/components/conversation/composition/UserMentions.tsx
Normal file
49
ts/components/conversation/composition/UserMentions.tsx
Normal file
|
@ -0,0 +1,49 @@
|
|||
import React from 'react';
|
||||
import { SuggestionDataItem } from 'react-mentions';
|
||||
import { MemberListItem } from '../../MemberListItem';
|
||||
|
||||
export const styleForCompositionBoxSuggestions = {
|
||||
suggestions: {
|
||||
list: {
|
||||
fontSize: 14,
|
||||
boxShadow: 'rgba(0, 0, 0, 0.24) 0px 3px 8px',
|
||||
backgroundColor: 'var(--color-cell-background)',
|
||||
},
|
||||
item: {
|
||||
height: '100%',
|
||||
paddingTop: '5px',
|
||||
paddingBottom: '5px',
|
||||
backgroundColor: 'var(--color-cell-background)',
|
||||
transition: '0.25s',
|
||||
|
||||
'&focused': {
|
||||
backgroundColor: 'var(--color-clickable-hovered)',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const renderUserMentionRow = (suggestion: SuggestionDataItem) => {
|
||||
return (
|
||||
<MemberListItem
|
||||
isSelected={false}
|
||||
key={suggestion.id}
|
||||
pubkey={`${suggestion.id}`}
|
||||
disableBg={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// this is dirty but we have to replace all @(xxx) by @xxx manually here
|
||||
export function cleanMentions(text: string): string {
|
||||
const matches = text.match(mentionsRegex);
|
||||
let replacedMentions = text;
|
||||
(matches || []).forEach(match => {
|
||||
const replacedMention = match.substring(2, match.indexOf('\uFFD7'));
|
||||
replacedMentions = replacedMentions.replace(match, `@${replacedMention}`);
|
||||
});
|
||||
|
||||
return replacedMentions;
|
||||
}
|
||||
|
||||
export const mentionsRegex = /@\uFFD205[0-9a-f]{64}\uFFD7[^\uFFD2]+\uFFD2/gu;
|
|
@ -464,4 +464,4 @@ export type LocalizerKeys =
|
|||
| 'searchFor...'
|
||||
| 'joinedTheGroup'
|
||||
| 'editGroupName'
|
||||
| 'reportIssue'
|
||||
| 'reportIssue';
|
||||
|
|
|
@ -19,12 +19,12 @@ function hasNormalCharacters(str: string) {
|
|||
|
||||
export function getEmojiSizeClass(str: string): SizeClassType {
|
||||
if (hasNormalCharacters(str)) {
|
||||
return 'default';
|
||||
return 'small';
|
||||
}
|
||||
|
||||
const emojiCount = getCountOfAllMatches(str);
|
||||
if (emojiCount > 8) {
|
||||
return 'default';
|
||||
return 'small';
|
||||
} else if (emojiCount > 6) {
|
||||
return 'small';
|
||||
} else if (emojiCount > 4) {
|
||||
|
|
Loading…
Reference in a new issue