Skip to content

Commit 2c9f7bd

Browse files
authored
Merge pull request #409 from GetStream/message-input-mobile-ux
Message input mobile ux
2 parents 1a24031 + 620606d commit 2c9f7bd

18 files changed

+129
-26
lines changed

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
"@ngx-translate/core": "^13.0.0",
118118
"@ngx-translate/http-loader": "^6.0.0",
119119
"@popperjs/core": "^2.11.5",
120-
"@stream-io/stream-chat-css": "3.8.0",
120+
"@stream-io/stream-chat-css": "3.9.0",
121121
"@stream-io/transliterate": "^1.5.2",
122122
"angular-mentions": "^1.4.0",
123123
"dayjs": "^1.10.7",

projects/stream-chat-angular/src/lib/message-input/autocomplete-textarea/autocomplete-textarea.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"
1313
rows="1"
1414
(input)="inputChanged()"
15-
(keydown.enter)="sent($event)"
15+
(keydown.enter)="enterHit($event)"
1616
[mentionConfig]="autocompleteConfig"
1717
(searchTerm)="autcompleteSearchTermChanged($event)"
1818
[mentionListTemplate]="autocompleteItem"

projects/stream-chat-angular/src/lib/message-input/autocomplete-textarea/autocomplete-textarea.component.spec.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ describe('AutocompleteTextareaComponent', () => {
6060
fixture = TestBed.createComponent(AutocompleteTextareaComponent);
6161
component = fixture.componentInstance;
6262
nativeElement = fixture.nativeElement as HTMLElement;
63+
component.inputMode = 'desktop';
6364
queryTextarea = () =>
6465
nativeElement.querySelector('[data-testid="textarea"]');
6566
fixture.detectChanges();
@@ -101,7 +102,7 @@ describe('AutocompleteTextareaComponent', () => {
101102
expect(spy).toHaveBeenCalledWith('message');
102103
});
103104

104-
it(`shouldn't emit #valueChange if enter is hit`, () => {
105+
it(`shouldn't emit #valueChange if enter is hit and #inputMode is desktop`, () => {
105106
const spy = jasmine.createSpy();
106107
component.valueChange.subscribe(spy);
107108
const textarea = queryTextarea();
@@ -114,7 +115,7 @@ describe('AutocompleteTextareaComponent', () => {
114115
expect(spy).not.toHaveBeenCalled();
115116
});
116117

117-
it('should emit #send if enter is hit', () => {
118+
it('should emit #send if enter is hit and #inputMode is desktop', () => {
118119
const spy = jasmine.createSpy();
119120
component.send.subscribe(spy);
120121
const textarea = queryTextarea();
@@ -129,6 +130,22 @@ describe('AutocompleteTextareaComponent', () => {
129130
expect(event.preventDefault).toHaveBeenCalledWith();
130131
});
131132

133+
it(`shouldn't emit #send if enter is hit and #inputMode is mobile`, () => {
134+
component.inputMode = 'mobile';
135+
const spy = jasmine.createSpy();
136+
component.send.subscribe(spy);
137+
const textarea = queryTextarea();
138+
const message = 'This is my message';
139+
textarea!.value = message;
140+
const event = new KeyboardEvent('keydown', { key: 'Enter' });
141+
spyOn(event, 'preventDefault');
142+
textarea?.dispatchEvent(event);
143+
fixture.detectChanges();
144+
145+
expect(spy).not.toHaveBeenCalled();
146+
expect(event.preventDefault).not.toHaveBeenCalled();
147+
});
148+
132149
it(`shouldn't emit #send if shift+enter is hit`, () => {
133150
const spy = jasmine.createSpy();
134151
component.send.subscribe(spy);

projects/stream-chat-angular/src/lib/message-input/autocomplete-textarea/autocomplete-textarea.component.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ export class AutocompleteTextareaComponent
5454
* If true, users can mention other users in messages. You can also set this input on the [`MessageInput`](./MessageInputComponent.mdx/#inputs-and-outputs) component.
5555
*/
5656
@Input() areMentionsEnabled: boolean | undefined = true;
57+
/**
58+
* See [`MessageInputConfigService`](../services/MessageInputConfigService.mdx) for more information
59+
*/
60+
@Input() inputMode!: 'desktop' | 'mobile';
5761
/**
5862
* The scope for user mentions, either members of the current channel of members of the application. You can also set this input on the [`MessageInput`](./MessageInputComponent.mdx/#inputs-and-outputs) component.
5963
*/
@@ -236,10 +240,12 @@ export class AutocompleteTextareaComponent
236240
this.updateMentionedUsersFromText();
237241
}
238242

239-
sent(event: Event) {
240-
event.preventDefault();
241-
this.updateMentionedUsersFromText();
242-
this.send.next();
243+
enterHit(event: Event) {
244+
if (this.inputMode === 'desktop') {
245+
event.preventDefault();
246+
this.updateMentionedUsersFromText();
247+
this.send.next();
248+
}
243249
}
244250

245251
private adjustTextareaHeight() {

projects/stream-chat-angular/src/lib/message-input/message-input-config.service.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,10 @@ export class MessageInputConfigService {
2424
*/
2525
mentionScope: 'channel' | 'application' | undefined = 'channel';
2626

27+
/**
28+
* In `desktop` mode the `Enter` key will trigger message sending, in `mobile` mode the `Enter` key will insert a new line to the message input.
29+
*/
30+
inputMode: 'desktop' | 'mobile' = 'desktop';
31+
2732
constructor() {}
2833
}

projects/stream-chat-angular/src/lib/message-input/message-input.component.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
[areMentionsEnabled]="areMentionsEnabled"
108108
[mentionScope]="mentionScope"
109109
[placeholder]="textareaPlaceholder"
110+
[inputMode]="inputMode"
110111
></ng-container>
111112
</ng-container>
112113
<ng-template #disabledTextarea>
@@ -286,6 +287,7 @@
286287
(userMentions)="mentionedUsers = $event"
287288
[areMentionsEnabled]="areMentionsEnabled"
288289
[mentionScope]="mentionScope"
290+
[inputMode]="inputMode"
289291
[placeholder]="textareaPlaceholder"
290292
></ng-container>
291293
<div class="str-chat__message-textarea-emoji-picker">

projects/stream-chat-angular/src/lib/message-input/message-input.component.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ describe('MessageInputComponent', () => {
176176

177177
expect(textarea?.value).toEqual('Hi');
178178
expect(textarea?.areMentionsEnabled).toBeTrue();
179+
expect(textarea?.inputMode).toBe(component.inputMode);
179180

180181
textarea?.valueChange.next('Hi, how are you?');
181182
fixture.detectChanges();

projects/stream-chat-angular/src/lib/message-input/message-input.component.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ export class MessageInputComponent
8282
* An observable that can be used to trigger message sending from the outside
8383
*/
8484
@Input() sendMessage$: Observable<void> | undefined;
85+
/**
86+
* In `desktop` mode the `Enter` key will trigger message sending, in `mobile` mode the `Enter` key will insert a new line to the message input. If no value is provided, it is set from the [`MessageInputConfigService`](../services/MessageInputConfigService.mdx).
87+
*/
88+
@Input() inputMode: 'desktop' | 'mobile';
8589
/**
8690
* Emits when a message was successfuly sent or updated
8791
*/
@@ -182,6 +186,7 @@ export class MessageInputComponent
182186
this.configService.isMultipleFileUploadEnabled;
183187
this.areMentionsEnabled = this.configService.areMentionsEnabled;
184188
this.mentionScope = this.configService.mentionScope;
189+
this.inputMode = this.configService.inputMode;
185190

186191
this.subscriptions.push(
187192
this.typingStart$.subscribe(
@@ -271,6 +276,9 @@ export class MessageInputComponent
271276
if (changes.mode) {
272277
this.setCanSendMessages();
273278
}
279+
if (changes.inputMode) {
280+
this.configService.inputMode = this.inputMode;
281+
}
274282
if (changes.sendMessage$) {
275283
if (this.sendMessageSubcription) {
276284
this.sendMessageSubcription.unsubscribe();

projects/stream-chat-angular/src/lib/message-input/textarea.directive.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ describe('TextareaDirective', () => {
1717
directive = new TextareaDirective({} as ViewContainerRef);
1818
mockComponent = {
1919
value: '',
20+
inputMode: 'desktop',
2021
valueChange: new EventEmitter<string>(),
2122
send: new EventEmitter<void>(),
2223
ngOnChanges: () => {},
@@ -57,6 +58,7 @@ describe('TextareaDirective', () => {
5758
directive = new TextareaDirective({} as ViewContainerRef);
5859
mockComponent = {
5960
value: '',
61+
inputMode: 'desktop',
6062
valueChange: new EventEmitter<string>(),
6163
send: new EventEmitter<void>(),
6264
userMentions: new EventEmitter<UserResponse[]>(),
@@ -86,5 +88,16 @@ describe('TextareaDirective', () => {
8688
jasmine.any(Object)
8789
);
8890
});
91+
92+
it('should pass on #inputMode', () => {
93+
directive.inputMode = 'mobile';
94+
spyOn(mockComponent, 'ngOnChanges');
95+
directive.ngOnChanges({ inputMode: {} as any as SimpleChange });
96+
97+
expect(mockComponent.inputMode).toBe('mobile');
98+
expect(mockComponent.ngOnChanges).toHaveBeenCalledWith(
99+
jasmine.any(Object)
100+
);
101+
});
89102
});
90103
});

0 commit comments

Comments
 (0)