fix: autocomplete
parent
0f2dd90f73
commit
44a41b3b62
|
@ -2,7 +2,6 @@
|
|||
"src/components/views/auth/AuthFooter.tsx": "src/components/views/auth/VectorAuthFooter.tsx",
|
||||
"src/components/views/auth/AuthHeaderLogo.tsx": "src/components/views/auth/VectorAuthHeaderLogo.tsx",
|
||||
"src/components/views/auth/AuthPage.tsx": "src/components/views/auth/VectorAuthPage.tsx",
|
||||
"src/components/views/rooms/Autocomplete.tsx": "src/components/views/rooms/Autocomplete.tsx",
|
||||
"src/components/views/rooms/RoomTile.tsx": "src/components/views/rooms/RoomTile.tsx",
|
||||
"src/components/views/rooms/NewRoomIntro.tsx": "src/components/views/rooms/NewRoomIntro.tsx",
|
||||
"src/components/views/elements/RoomName.tsx": "src/components/views/elements/RoomName.tsx",
|
||||
|
@ -10,5 +9,6 @@
|
|||
"src/components/views/avatars/BaseAvatar.tsx": "src/components/views/avatars/BaseAvatar.tsx",
|
||||
"src/components/views/spaces/SpacePanel.tsx": "src/components/views/spaces/SpacePanel.tsx",
|
||||
"src/hooks/useRoomName.ts": "src/hooks/useRoomName.ts",
|
||||
"src/editor/commands.tsx": "src/editor/commands.tsx"
|
||||
"src/editor/commands.tsx": "src/editor/commands.tsx",
|
||||
"src/autocomplete/Autocompleter.ts": "src/autocomplete/Autocompleter.ts"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
Copyright 2016 Aviral Dasgupta
|
||||
Copyright 2017, 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { Room } from "matrix-js-sdk/src/matrix";
|
||||
import AutocompleteProvider, { ICommand } from "matrix-react-sdk/src/autocomplete/AutocompleteProvider";
|
||||
import EmojiProvider from "matrix-react-sdk/src/autocomplete/EmojiProvider";
|
||||
import NotifProvider from "matrix-react-sdk/src/autocomplete/NotifProvider";
|
||||
import RoomProvider from "matrix-react-sdk/src/autocomplete/RoomProvider";
|
||||
import SpaceProvider from "matrix-react-sdk/src/autocomplete/SpaceProvider";
|
||||
import UserProvider from "matrix-react-sdk/src/autocomplete/UserProvider";
|
||||
import { TimelineRenderingType } from "matrix-react-sdk/src/contexts/RoomContext";
|
||||
import { filterBoolean } from "matrix-react-sdk/src/utils/arrays";
|
||||
import { timeout } from "matrix-react-sdk/src/utils/promise";
|
||||
import { ReactElement } from "react";
|
||||
|
||||
export interface ISelectionRange {
|
||||
beginning?: boolean; // whether the selection is in the first block of the editor or not
|
||||
start: number; // byte offset relative to the start anchor of the current editor selection.
|
||||
end: number; // byte offset relative to the end anchor of the current editor selection.
|
||||
}
|
||||
|
||||
export interface ICompletion {
|
||||
type?: "at-room" | "command" | "community" | "room" | "user";
|
||||
completion: string;
|
||||
completionId?: string;
|
||||
component: ReactElement;
|
||||
range: ISelectionRange;
|
||||
command?: string;
|
||||
suffix?: string;
|
||||
// If provided, apply a LINK entity to the completion with the
|
||||
// data = { url: href }.
|
||||
href?: string;
|
||||
}
|
||||
|
||||
const PROVIDERS = [UserProvider, RoomProvider, EmojiProvider, NotifProvider, SpaceProvider];
|
||||
|
||||
// Providers will get rejected if they take longer than this.
|
||||
const PROVIDER_COMPLETION_TIMEOUT = 3000;
|
||||
|
||||
export interface IProviderCompletions {
|
||||
completions: ICompletion[];
|
||||
provider: AutocompleteProvider;
|
||||
command: Partial<ICommand>;
|
||||
}
|
||||
|
||||
export default class Autocompleter {
|
||||
public room: Room;
|
||||
public providers: AutocompleteProvider[];
|
||||
|
||||
public constructor(room: Room, renderingType: TimelineRenderingType = TimelineRenderingType.Room) {
|
||||
this.room = room;
|
||||
this.providers = PROVIDERS.map((Prov) => {
|
||||
return new Prov(room, renderingType);
|
||||
});
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this.providers.forEach((p) => {
|
||||
p.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
public async getCompletions(
|
||||
query: string,
|
||||
selection: ISelectionRange,
|
||||
force = false,
|
||||
limit = -1,
|
||||
): Promise<IProviderCompletions[]> {
|
||||
/* Note: This intentionally waits for all providers to return,
|
||||
otherwise, we run into a condition where new completions are displayed
|
||||
while the user is interacting with the list, which makes it difficult
|
||||
to predict whether an action will actually do what is intended
|
||||
*/
|
||||
// list of results from each provider, each being a list of completions or null if it times out
|
||||
const completionsList: Array<ICompletion[] | null> = await Promise.all(
|
||||
this.providers.map(async (provider): Promise<ICompletion[] | null> => {
|
||||
return timeout(
|
||||
provider.getCompletions(query, selection, force, limit),
|
||||
null,
|
||||
PROVIDER_COMPLETION_TIMEOUT,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
// map then filter to maintain the index for the map-operation, for this.providers to line up
|
||||
return filterBoolean(
|
||||
completionsList.map((completions, i) => {
|
||||
if (!completions || !completions.length) return;
|
||||
|
||||
return {
|
||||
completions,
|
||||
provider: this.providers[i],
|
||||
|
||||
/* the currently matched "command" the completer tried to complete
|
||||
* we pass this through so that Autocomplete can figure out when to
|
||||
* re-show itself once hidden.
|
||||
*/
|
||||
command: this.providers[i].getCurrentCommand(query, selection, force),
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
export const generateCompletionDomId = (n: number): string => `mx_Autocomplete_Completion_${n}`;
|
||||
|
||||
export default class Autocomplete extends React.PureComponent {
|
||||
public constructor(props: {} | Readonly<{}>) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
// list of completionResults, each containing completions
|
||||
completions: [],
|
||||
|
||||
// array of completions, so we can look up current selection by offset quickly
|
||||
completionList: [],
|
||||
|
||||
// how far down the completion list we are (THIS IS 1-INDEXED!)
|
||||
selectionOffset: 1,
|
||||
|
||||
// whether we should show completions if they're available
|
||||
shouldShowCompletions: true,
|
||||
|
||||
hide: false,
|
||||
|
||||
forceComplete: false,
|
||||
};
|
||||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue