Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bindings/wysiwyg-ffi/src/ffi_pattern_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub enum PatternKey {
Hash,
Slash,
Custom(String),
Colon,
}

impl From<wysiwyg::PatternKey> for PatternKey {
Expand All @@ -19,6 +20,7 @@ impl From<wysiwyg::PatternKey> for PatternKey {
wysiwyg::PatternKey::Hash => Self::Hash,
wysiwyg::PatternKey::Slash => Self::Slash,
wysiwyg::PatternKey::Custom(key) => Self::Custom(key),
wysiwyg::PatternKey::Colon => Self::Colon,
}
}
}
Expand All @@ -30,6 +32,7 @@ impl From<PatternKey> for wysiwyg::PatternKey {
PatternKey::Hash => Self::Hash,
PatternKey::Slash => Self::Slash,
PatternKey::Custom(key) => Self::Custom(key),
PatternKey::Colon => Self::Colon,
}
}
}
6 changes: 6 additions & 0 deletions bindings/wysiwyg-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ pub enum PatternKeyType {
Hash,
Slash,
Custom,
Colon,
}

#[derive(Clone)]
Expand Down Expand Up @@ -748,6 +749,10 @@ impl From<wysiwyg::PatternKey> for PatternKey {
key_type: PatternKeyType::Custom,
custom_key_value: Some(key),
},
wysiwyg::PatternKey::Colon => Self {
key_type: PatternKeyType::Colon,
custom_key_value: None,
},
}
}
}
Expand All @@ -761,6 +766,7 @@ impl From<PatternKey> for wysiwyg::PatternKey {
PatternKeyType::Custom => {
Self::Custom(key.custom_key_value.unwrap())
}
PatternKeyType::Colon => Self::Colon,
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion crates/wysiwyg/src/pattern_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ pub enum PatternKey {
Hash,
Slash,
Custom(String),
Colon,
}

impl PatternKey {
pub(crate) fn is_static_pattern(&self) -> bool {
matches!(self, Self::At | Self::Hash | Self::Slash)
matches!(self, Self::At | Self::Hash | Self::Slash | Self::Colon)
}

pub(crate) fn from_string_and_suggestions(
Expand All @@ -31,6 +32,7 @@ impl PatternKey {
'\u{0040}' => Some(Self::At),
'\u{0023}' => Some(Self::Hash),
'\u{002F}' => Some(Self::Slash),
'\u{003A}' => Some(Self::Colon),
_ => None,
}
}
Expand Down
14 changes: 13 additions & 1 deletion crates/wysiwyg/src/tests/test_menu_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE in the repository root for full details.

use crate::PatternKey::{At, Hash, Slash};
use crate::PatternKey::{At, Colon, Hash, Slash};
use crate::{
tests::testutils_composer_model::cm, Location, MenuAction, PatternKey,
SuggestionPattern,
Expand Down Expand Up @@ -193,6 +193,18 @@ fn at_pattern_is_not_detected_after_moving_in_code_block() {
assert_eq!(update.menu_action, MenuAction::None);
}

#[test]
fn emoji_pattern_is_detected() {
let model = cm(":smil|");
assert_eq!(model.compute_menu_action(), sp(Colon, "smil", 0, 5));
}

#[test]
fn emoji_pattern_is_not_detected_after_immediate_preceeding_text() {
let model = cm("text:smil|");
assert_eq!(model.compute_menu_action(), MenuAction::None);
}

#[test]
fn menu_action_retuns_keep_after_format_with_cursor() {
let mut model = cm("@alic|");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private fun processSuggestion(suggestion: MenuAction.Suggestion, roomMemberSugge
PatternKey.At -> people + everyone
PatternKey.Hash -> rooms
PatternKey.Slash -> slashCommands
is PatternKey.Custom -> listOf()
is PatternKey.Custom, PatternKey.Colon -> listOf()
}

val suggestions = names
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ class RichTextEditor : LinearLayout {
val names = when (menuAction.suggestionPattern.key) {
PatternKey.At -> people + everyone
PatternKey.Hash -> rooms
PatternKey.Slash, is PatternKey.Custom ->
PatternKey.Slash, is PatternKey.Custom, PatternKey.Colon ->
emptyList() // TODO
}
val suggestions = names
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ struct WysiwygSuggestionList: View {
.accessibilityIdentifier(command.accessibilityIdentifier)
}
}
case .custom:
case .custom, .colon:
EmptyView()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public extension PatternKey {
return .user
case .hash:
return .room
case .slash, .custom:
case .slash, .custom, .colon:
return nil
}
}
Expand Down
6 changes: 4 additions & 2 deletions platforms/web/lib/composer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,15 @@ export function processInput(
}
break;
}
case 'insertCommand': {
case 'insertCommand':
case 'insertEmoji': {
if (suggestion && event.data) {
const appendSpace = event.inputType === 'insertCommand';
return action(
composerModel.replace_text_suggestion(
event.data,
suggestion,
true,
appendSpace,
),
'replace_text_suggestion',
);
Expand Down
2 changes: 1 addition & 1 deletion platforms/web/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ export const ACTION_TYPES = [
'unindent',
] as const;

export const SUGGESTIONS = ['@', '#', '/', ''] as const;
export const SUGGESTIONS = ['@', '#', '/', '', ':'] as const;
61 changes: 44 additions & 17 deletions platforms/web/lib/suggestion.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ describe('getSuggestionType', () => {
expect(getSuggestionType(slashSuggestion)).toBe('command');
});

it('returns the expected type for a custom suggestion', () => {
const slashSuggestion = {
key: { key_type: 3 },
} as unknown as SuggestionPattern;

expect(getSuggestionType(slashSuggestion)).toBe('custom');
});

it('returns the expected type for an emoji suggestion', () => {
const slashSuggestion = {
key: { key_type: 4 },
} as unknown as SuggestionPattern;

expect(getSuggestionType(slashSuggestion)).toBe('emoji');
});

it('returns unknown for any other implementations', () => {
const slashSuggestion = { key: 200 } as unknown as SuggestionPattern;

Expand All @@ -70,24 +86,35 @@ describe('mapSuggestion', () => {
expect(mapSuggestion(null)).toBe(null);
});

it('returns the input with additional keys keyChar and type', () => {
const suggestion: SuggestionPattern = {
free: () => {},
start: 1,
end: 2,
key: {
it('returns the input with additional keys keyChar and type for each key_type', () => {
const testCases = [
{ key_type: 0, expectedKeyChar: '@', expectedType: 'mention' }, // user mention
{ key_type: 1, expectedKeyChar: '#', expectedType: 'mention' }, // room mention
{ key_type: 2, expectedKeyChar: '/', expectedType: 'command' }, // slash command
{ key_type: 3, expectedKeyChar: '', expectedType: 'custom' }, // custom
{ key_type: 4, expectedKeyChar: ':', expectedType: 'emoji' }, // emoji
{ key_type: 200, expectedKeyChar: '', expectedType: 'unknown' }, // unknown
];

testCases.forEach(({ key_type, expectedKeyChar, expectedType }) => {
const suggestion: SuggestionPattern = {
free: () => {},
key_type: 0,
custom_key_value: undefined,
},
text: 'some text',
};

const mappedSuggestion = mapSuggestion(suggestion);
expect(mappedSuggestion).toMatchObject({
keyChar: '@',
type: 'mention',
text: suggestion.text,
start: 1,
end: 2,
key: {
free: () => {},
key_type,
custom_key_value: undefined,
},
text: 'some text',
};

const mappedSuggestion = mapSuggestion(suggestion);
expect(mappedSuggestion).toMatchObject({
keyChar: expectedKeyChar,
type: expectedType,
text: suggestion.text,
});
});
});
});
Expand Down
2 changes: 2 additions & 0 deletions platforms/web/lib/suggestion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export function getSuggestionType(
return 'command';
case 3:
return 'custom';
case 4:
return 'emoji';
default:
return 'unknown';
}
Expand Down
4 changes: 3 additions & 1 deletion platforms/web/lib/testUtils/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export const Editor = forwardRef<HTMLDivElement, EditorProps>(function Editor(
key !== 'mention' &&
key !== 'command' &&
key !== 'indent' &&
key !== 'unindent',
key !== 'unindent' &&
key !== `emoji`,
) as Array<
Exclude<
keyof typeof wysiwyg,
Expand All @@ -57,6 +58,7 @@ export const Editor = forwardRef<HTMLDivElement, EditorProps>(function Editor(
| 'indent'
| 'unindent'
| 'mentionAtRoom'
| 'emoji'
>
>;

Expand Down
8 changes: 7 additions & 1 deletion platforms/web/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type FormattingFunctions = Record<
) => void;
mentionAtRoom: (attributes: AllowedMentionAttributes) => void;
command: (text: string) => void;
emoji: (text: string) => void;
removeLinks: () => void;
getLink: () => string;
};
Expand All @@ -62,7 +63,12 @@ export type InputEventProcessor = (
) => WysiwygEvent | null;

export type SuggestionChar = (typeof SUGGESTIONS)[number] | '';
export type SuggestionType = 'mention' | 'command' | 'custom' | 'unknown';
export type SuggestionType =
| 'mention'
| 'command'
| 'custom'
| 'emoji'
| 'unknown';

export type MappedSuggestion = {
keyChar: SuggestionChar;
Expand Down
1 change: 1 addition & 0 deletions platforms/web/lib/useFormattingFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export function useFormattingFunctions(
attributes: AllowedMentionAttributes,
): void => sendEvent('insertSuggestion', { url, text, attributes }),
command: (text: string): void => sendEvent('insertCommand', text),
emoji: (text: string): void => sendEvent('insertEmoji', text),
mentionAtRoom: (attributes: AllowedMentionAttributes): void =>
sendEvent('insertAtRoomSuggestion', { attributes }),
};
Expand Down
Loading