added keyboard selection
This commit is contained in:
parent
0ced6445b5
commit
fdcdf9d813
|
@ -1,15 +1,17 @@
|
|||
<div class="autosuggest" *ngIf="accounts.length > 0 || hashtags.length > 0">
|
||||
<a href *ngFor="let a of accounts"
|
||||
title="@{{a.acct}}"
|
||||
title="@{{a.account.acct}}"
|
||||
(click)="accountSelected(a)"
|
||||
class="autosuggest__entry autosuggest__account">
|
||||
<img class="autosuggest__account--avatar" src="{{ a.avatar }}" /> {{ a.username }} <span class="autosuggest__account--acct">@{{ a.acct }}</span>
|
||||
class="autosuggest__entry autosuggest__account"
|
||||
[class.autosuggest__entry--selected]="a.selected">
|
||||
<img class="autosuggest__account--avatar" src="{{ a.account.avatar }}" /> {{ a.account.username }} <span class="autosuggest__account--acct">@{{ a.account.acct }}</span>
|
||||
</a>
|
||||
|
||||
<a href *ngFor="let h of hashtags"
|
||||
title="#{{h}}"
|
||||
title="#{{h.hashtag}}"
|
||||
(click)="hashtagSelected(h)"
|
||||
class="autosuggest__entry">
|
||||
#{{ h }}
|
||||
class="autosuggest__entry"
|
||||
[class.autosuggest__entry--selected]="h.selected">
|
||||
#{{ h.hashtag }}
|
||||
</a>
|
||||
</div>
|
|
@ -16,11 +16,13 @@
|
|||
overflow: hidden;
|
||||
width: calc(100%);
|
||||
|
||||
&:hover {
|
||||
&:hover, &--selected {
|
||||
color: $font-color-primary;
|
||||
text-decoration: none;
|
||||
background-color: $column-color;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
&__account {
|
||||
|
@ -39,7 +41,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__entry:hover &__account--acct {
|
||||
&__entry:hover &__account--acct, &__entry--selected &__account--acct {
|
||||
color: $font-link-primary-hover;
|
||||
}
|
||||
}
|
|
@ -1,18 +1,23 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { Component, OnInit, Input, Output, EventEmitter, OnDestroy } from '@angular/core';
|
||||
|
||||
import { ToolsService } from '../../../services/tools.service';
|
||||
import { MastodonService } from '../../../services/mastodon.service';
|
||||
import { NotificationService } from '../../../services/notification.service';
|
||||
import { Results, Account } from '../../../services/models/mastodon.interfaces';
|
||||
import { Actions } from '@ngxs/store';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-autosuggest',
|
||||
templateUrl: './autosuggest.component.html',
|
||||
styleUrls: ['./autosuggest.component.scss']
|
||||
})
|
||||
export class AutosuggestComponent implements OnInit {
|
||||
accounts: Account[] = [];
|
||||
hashtags: string[] = [];
|
||||
export class AutosuggestComponent implements OnInit, OnDestroy {
|
||||
|
||||
private lastPatternUsed: string;
|
||||
private lastPatternUsedWtType: string;
|
||||
accounts: SelectableAccount[] = [];
|
||||
hashtags: SelectableHashtag[] = [];
|
||||
|
||||
@Output() suggestionSelectedEvent = new EventEmitter<AutosuggestSelection>();
|
||||
@Output() hasSuggestionsEvent = new EventEmitter<boolean>();
|
||||
|
@ -32,16 +37,26 @@ export class AutosuggestComponent implements OnInit {
|
|||
return this._pattern;
|
||||
}
|
||||
|
||||
@Input() autoSuggestUserActionsStream: EventEmitter<AutosuggestUserActionEnum>;
|
||||
private autoSuggestUserActionsSub: Subscription;
|
||||
|
||||
constructor(
|
||||
private readonly notificationService: NotificationService,
|
||||
private readonly toolsService: ToolsService,
|
||||
private readonly mastodonService: MastodonService) { }
|
||||
|
||||
ngOnInit() {
|
||||
if (this.autoSuggestUserActionsStream) {
|
||||
this.autoSuggestUserActionsSub = this.autoSuggestUserActionsStream.subscribe((action: AutosuggestUserActionEnum) => {
|
||||
this.processUserInput(action);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.autoSuggestUserActionsSub) this.autoSuggestUserActionsSub.unsubscribe();
|
||||
}
|
||||
|
||||
private lastPatternUsed: string;
|
||||
private lastPatternUsedWtType: string;
|
||||
private analysePattern(value: string) {
|
||||
const selectedAccount = this.toolsService.getSelectedAccounts()[0];
|
||||
const isAccount = value[0] === '@';
|
||||
|
@ -58,21 +73,23 @@ export class AutosuggestComponent implements OnInit {
|
|||
|
||||
if (isAccount) {
|
||||
for (let account of results.accounts) {
|
||||
this.accounts.push(account);
|
||||
if(this.accounts.length > 7) return;
|
||||
this.accounts.push(new SelectableAccount(account));
|
||||
this.accounts[0].selected = true;
|
||||
if (this.accounts.length > 7) return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let hashtag of results.hashtags) {
|
||||
if (hashtag.includes(this.lastPatternUsed) || hashtag === this.lastPatternUsed) {
|
||||
this.hashtags.push(hashtag);
|
||||
if(this.hashtags.length > 7) return;
|
||||
this.hashtags.push(new SelectableHashtag(hashtag));
|
||||
this.hashtags[0].selected = true;
|
||||
if (this.hashtags.length > 7) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
if(this.hashtags.length > 0 || this.accounts.length > 0){
|
||||
if (this.hashtags.length > 0 || this.accounts.length > 0) {
|
||||
this.hasSuggestionsEvent.next(true);
|
||||
} else {
|
||||
this.hasSuggestionsEvent.next(false);
|
||||
|
@ -83,19 +100,81 @@ export class AutosuggestComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
accountSelected(account: Account): boolean {
|
||||
const fullHandle = this.toolsService.getAccountFullHandle(account);
|
||||
private processUserInput(action: AutosuggestUserActionEnum) {
|
||||
const isAutosuggestingHashtag = this.hashtags.length > 0;
|
||||
|
||||
switch (action) {
|
||||
case AutosuggestUserActionEnum.Validate:
|
||||
if (isAutosuggestingHashtag) {
|
||||
let selection = this.hashtags.find(x => x.selected);
|
||||
this.hashtagSelected(selection);
|
||||
} else {
|
||||
let selection = this.accounts.find(x => x.selected);
|
||||
this.accountSelected(selection);
|
||||
}
|
||||
break;
|
||||
case AutosuggestUserActionEnum.MoveDown:
|
||||
if (isAutosuggestingHashtag) {
|
||||
let selectionIndex = this.hashtags.findIndex(x => x.selected);
|
||||
if (selectionIndex < (this.hashtags.length - 1)) {
|
||||
this.hashtags[selectionIndex].selected = false;
|
||||
this.hashtags[selectionIndex + 1].selected = true;
|
||||
}
|
||||
} else {
|
||||
let selectionIndex = this.accounts.findIndex(x => x.selected);
|
||||
if (selectionIndex < (this.accounts.length - 1)) {
|
||||
this.accounts[selectionIndex].selected = false;
|
||||
this.accounts[selectionIndex + 1].selected = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AutosuggestUserActionEnum.MoveUp:
|
||||
if (isAutosuggestingHashtag) {
|
||||
let selectionIndex = this.hashtags.findIndex(x => x.selected);
|
||||
if (selectionIndex > 0) {
|
||||
this.hashtags[selectionIndex].selected = false;
|
||||
this.hashtags[selectionIndex - 1].selected = true;
|
||||
}
|
||||
} else {
|
||||
let selectionIndex = this.accounts.findIndex(x => x.selected);
|
||||
if (selectionIndex > 0) {
|
||||
this.accounts[selectionIndex].selected = false;
|
||||
this.accounts[selectionIndex - 1].selected = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
accountSelected(selAccount: SelectableAccount): boolean {
|
||||
const fullHandle = this.toolsService.getAccountFullHandle(selAccount.account);
|
||||
this.suggestionSelectedEvent.next(new AutosuggestSelection(this.lastPatternUsedWtType, fullHandle));
|
||||
return false;
|
||||
}
|
||||
|
||||
hashtagSelected(hashtag: string): boolean {
|
||||
this.suggestionSelectedEvent.next(new AutosuggestSelection(this.lastPatternUsedWtType, `#${hashtag}`));
|
||||
hashtagSelected(selHashtag: SelectableHashtag): boolean {
|
||||
this.suggestionSelectedEvent.next(new AutosuggestSelection(this.lastPatternUsedWtType, `#${selHashtag.hashtag}`));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class AutosuggestSelection{
|
||||
constructor(public pattern: string, public autosuggest: string) {
|
||||
class SelectableAccount {
|
||||
constructor(public account: Account, public selected: boolean = false) {
|
||||
}
|
||||
}
|
||||
|
||||
class SelectableHashtag {
|
||||
constructor(public hashtag: string, public selected: boolean = false) {
|
||||
}
|
||||
}
|
||||
|
||||
export class AutosuggestSelection {
|
||||
constructor(public pattern: string, public autosuggest: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export enum AutosuggestUserActionEnum {
|
||||
MoveDown,
|
||||
MoveUp,
|
||||
Validate
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
<app-autosuggest class="status-form__autosuggest" *ngIf="autosuggestData"
|
||||
[pattern]="autosuggestData"
|
||||
[autoSuggestUserActionsStream]="autoSuggestUserActionsStream"
|
||||
(suggestionSelectedEvent)="suggestionSelected($event)"
|
||||
(hasSuggestionsEvent)="suggestionsChanged($event)"></app-autosuggest>
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { StatusWrapper } from '../../models/common.model';
|
|||
import { AccountInfo } from '../../states/accounts.state';
|
||||
import { InstancesInfoService } from '../../services/instances-info.service';
|
||||
import { MediaService } from '../../services/media.service';
|
||||
import { AutosuggestSelection } from './autosuggest/autosuggest.component';
|
||||
import { AutosuggestSelection, AutosuggestUserActionEnum } from './autosuggest/autosuggest.component';
|
||||
|
||||
|
||||
@Component({
|
||||
|
@ -21,6 +21,8 @@ import { AutosuggestSelection } from './autosuggest/autosuggest.component';
|
|||
styleUrls: ['./create-status.component.scss']
|
||||
})
|
||||
export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||
autoSuggestUserActionsStream = new EventEmitter<AutosuggestUserActionEnum>();
|
||||
|
||||
private _title: string;
|
||||
set title(value: string) {
|
||||
this._title = value;
|
||||
|
@ -51,7 +53,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
let parser = new DOMParser();
|
||||
var dom = parser.parseFromString(value.status.content, 'text/html')
|
||||
this.status = dom.body.textContent;
|
||||
|
||||
|
||||
this.setVisibilityFromStatus(value.status);
|
||||
this.title = value.status.spoiler_text;
|
||||
|
||||
|
@ -64,7 +66,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
const mentions = this.getMentions(this.statusReplyingToWrapper.status, this.statusReplyingToWrapper.provider);
|
||||
for (const mention of mentions) {
|
||||
const name = `@${mention.split('@')[0]}`;
|
||||
if(this.status.includes(name)){
|
||||
if (this.status.includes(name)) {
|
||||
this.status = this.status.replace(name, `@${mention}`);
|
||||
} else {
|
||||
this.status = `@${mention} ` + this.status;
|
||||
|
@ -170,9 +172,9 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
|
||||
private detectAutosuggestion(status: string) {
|
||||
const parsedStatus = status.split(' ');
|
||||
if(parsedStatus && parsedStatus.length > 0){
|
||||
if (parsedStatus && parsedStatus.length > 0) {
|
||||
const lastElement = parsedStatus[parsedStatus.length - 1];
|
||||
if(lastElement.length > 2 && (lastElement.startsWith('@') || lastElement.startsWith('#'))){
|
||||
if (lastElement.length > 2 && (lastElement.startsWith('@') || lastElement.startsWith('#'))) {
|
||||
//this.autosuggestData = lastElement.substring(1);
|
||||
this.autosuggestData = lastElement;
|
||||
return;
|
||||
|
@ -467,9 +469,9 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
|
||||
suggestionSelected(selection: AutosuggestSelection){
|
||||
suggestionSelected(selection: AutosuggestSelection) {
|
||||
const parsedStatus = this.status.split(' ');
|
||||
if(parsedStatus[parsedStatus.length - 1] === selection.pattern){
|
||||
if (parsedStatus[parsedStatus.length - 1] === selection.pattern) {
|
||||
this.status = `${this.status.replace(new RegExp(`${selection.pattern}$`), selection.autosuggest)} `;
|
||||
this.focus();
|
||||
}
|
||||
|
@ -481,13 +483,24 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
handleKeyDown(event: KeyboardEvent): boolean {
|
||||
if(this.hasSuggestions){
|
||||
if (this.hasSuggestions) {
|
||||
if (event.keyCode === DOWN_ARROW || event.keyCode === UP_ARROW || event.keyCode === ENTER) {
|
||||
event.stopImmediatePropagation();
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
console.warn(event.keyCode);
|
||||
event.stopPropagation();
|
||||
|
||||
switch (event.keyCode) {
|
||||
case DOWN_ARROW:
|
||||
this.autoSuggestUserActionsStream.next(AutosuggestUserActionEnum.MoveDown);
|
||||
break;
|
||||
case UP_ARROW:
|
||||
this.autoSuggestUserActionsStream.next(AutosuggestUserActionEnum.MoveUp);
|
||||
break;
|
||||
case ENTER:
|
||||
this.autoSuggestUserActionsStream.next(AutosuggestUserActionEnum.Validate);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue