Apply prettier formatting

This commit is contained in:
Michael Weimann 2022-12-12 12:24:14 +01:00
parent 1cac306093
commit 526645c791
No known key found for this signature in database
GPG key ID: 53F535A266BB9584
1576 changed files with 65385 additions and 62478 deletions

View file

@ -16,10 +16,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
import { TimelineRenderingType } from '../contexts/RoomContext';
import type { ICompletion, ISelectionRange } from './Autocompleter';
import { TimelineRenderingType } from "../contexts/RoomContext";
import type { ICompletion, ISelectionRange } from "./Autocompleter";
export interface ICommand {
command: string | null;
@ -44,13 +44,13 @@ export default abstract class AutocompleteProvider {
protected constructor({ commandRegex, forcedCommandRegex, renderingType }: IAutocompleteOptions) {
if (commandRegex) {
if (!commandRegex.global) {
throw new Error('commandRegex must have global flag set');
throw new Error("commandRegex must have global flag set");
}
this.commandRegex = commandRegex;
}
if (forcedCommandRegex) {
if (!forcedCommandRegex.global) {
throw new Error('forcedCommandRegex must have global flag set');
throw new Error("forcedCommandRegex must have global flag set");
}
this.forcedCommandRegex = forcedCommandRegex;
}

View file

@ -15,18 +15,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { ReactElement } from 'react';
import { Room } from 'matrix-js-sdk/src/models/room';
import { ReactElement } from "react";
import { Room } from "matrix-js-sdk/src/models/room";
import CommandProvider from './CommandProvider';
import RoomProvider from './RoomProvider';
import UserProvider from './UserProvider';
import EmojiProvider from './EmojiProvider';
import NotifProvider from './NotifProvider';
import CommandProvider from "./CommandProvider";
import RoomProvider from "./RoomProvider";
import UserProvider from "./UserProvider";
import EmojiProvider from "./EmojiProvider";
import NotifProvider from "./NotifProvider";
import { timeout } from "../utils/promise";
import AutocompleteProvider, { ICommand } from "./AutocompleteProvider";
import SpaceProvider from "./SpaceProvider";
import { TimelineRenderingType } from '../contexts/RoomContext';
import { TimelineRenderingType } from "../contexts/RoomContext";
export interface ISelectionRange {
beginning?: boolean; // whether the selection is in the first block of the editor or not
@ -47,14 +47,7 @@ export interface ICompletion {
href?: string;
}
const PROVIDERS = [
UserProvider,
RoomProvider,
EmojiProvider,
NotifProvider,
CommandProvider,
SpaceProvider,
];
const PROVIDERS = [UserProvider, RoomProvider, EmojiProvider, NotifProvider, CommandProvider, SpaceProvider];
// Providers will get rejected if they take longer than this.
const PROVIDER_COMPLETION_TIMEOUT = 3000;
@ -94,28 +87,32 @@ export default class Autocompleter {
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: ICompletion[][] = await Promise.all(this.providers.map(async provider => {
return timeout(
provider.getCompletions(query, selection, force, limit),
null,
PROVIDER_COMPLETION_TIMEOUT,
);
}));
const completionsList: ICompletion[][] = await Promise.all(
this.providers.map(async (provider) => {
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 completionsList.map((completions, i) => {
if (!completions || !completions.length) return;
return completionsList
.map((completions, i) => {
if (!completions || !completions.length) return;
return {
completions,
provider: this.providers[i],
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),
};
}).filter(Boolean);
/* 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),
};
})
.filter(Boolean);
}
}

View file

@ -17,16 +17,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { Room } from 'matrix-js-sdk/src/models/room';
import React from "react";
import { Room } from "matrix-js-sdk/src/models/room";
import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider';
import QueryMatcher from './QueryMatcher';
import { TextualCompletion } from './Components';
import { _t } from "../languageHandler";
import AutocompleteProvider from "./AutocompleteProvider";
import QueryMatcher from "./QueryMatcher";
import { TextualCompletion } from "./Components";
import { ICompletion, ISelectionRange } from "./Autocompleter";
import { Command, Commands, CommandMap } from '../SlashCommands';
import { TimelineRenderingType } from '../contexts/RoomContext';
import { Command, Commands, CommandMap } from "../SlashCommands";
import { TimelineRenderingType } from "../contexts/RoomContext";
const COMMAND_RE = /(^\/\w*)(?: .*)?/g;
@ -36,7 +36,7 @@ export default class CommandProvider extends AutocompleteProvider {
constructor(room: Room, renderingType?: TimelineRenderingType) {
super({ commandRegex: COMMAND_RE, renderingType });
this.matcher = new QueryMatcher(Commands, {
keys: ['command', 'args', 'description'],
keys: ["command", "args", "description"],
funcs: [({ aliases }) => aliases.join(" ")], // aliases
context: renderingType,
});
@ -62,7 +62,7 @@ export default class CommandProvider extends AutocompleteProvider {
matches = [CommandMap.get(name)];
}
} else {
if (query === '/') {
if (query === "/") {
// If they have just entered `/` show everything
// We exclude the limit on purpose to have a comprehensive list
matches = Commands;
@ -72,31 +72,36 @@ export default class CommandProvider extends AutocompleteProvider {
}
}
return matches.filter(cmd => {
const display = !cmd.renderingTypes || cmd.renderingTypes.includes(this.renderingType);
return cmd.isEnabled() && display;
}).map((result) => {
let completion = result.getCommand() + ' ';
const usedAlias = result.aliases.find(alias => `/${alias}` === command[1]);
// If the command (or an alias) is the same as the one they entered, we don't want to discard their arguments
if (usedAlias || result.getCommand() === command[1]) {
completion = command[0];
}
return matches
.filter((cmd) => {
const display = !cmd.renderingTypes || cmd.renderingTypes.includes(this.renderingType);
return cmd.isEnabled() && display;
})
.map((result) => {
let completion = result.getCommand() + " ";
const usedAlias = result.aliases.find((alias) => `/${alias}` === command[1]);
// If the command (or an alias) is the same as the one they entered, we don't want to discard their arguments
if (usedAlias || result.getCommand() === command[1]) {
completion = command[0];
}
return {
completion,
type: "command",
component: <TextualCompletion
title={`/${usedAlias || result.command}`}
subtitle={result.args}
description={_t(result.description)} />,
range,
};
});
return {
completion,
type: "command",
component: (
<TextualCompletion
title={`/${usedAlias || result.command}`}
subtitle={result.args}
description={_t(result.description)}
/>
),
range,
};
});
}
getName() {
return '*️⃣ ' + _t('Commands');
return "*️⃣ " + _t("Commands");
}
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
@ -106,7 +111,7 @@ export default class CommandProvider extends AutocompleteProvider {
role="presentation"
aria-label={_t("Command Autocomplete")}
>
{ completions }
{completions}
</div>
);
}

View file

@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { forwardRef } from 'react';
import classNames from 'classnames';
import React, { forwardRef } from "react";
import classNames from "classnames";
/* These were earlier stateless functional components but had to be converted
since we need to use refs/findDOMNode to access the underlying DOM node to focus the correct completion,
@ -31,24 +31,18 @@ interface ITextualCompletionProps {
}
export const TextualCompletion = forwardRef<ITextualCompletionProps, any>((props, ref) => {
const {
title,
subtitle,
description,
className,
'aria-selected': ariaSelectedAttribute,
...restProps
} = props;
const { title, subtitle, description, className, "aria-selected": ariaSelectedAttribute, ...restProps } = props;
return (
<div {...restProps}
className={classNames('mx_Autocomplete_Completion_block', className)}
<div
{...restProps}
className={classNames("mx_Autocomplete_Completion_block", className)}
role="option"
aria-selected={ariaSelectedAttribute}
ref={ref}
>
<span className="mx_Autocomplete_Completion_title">{ title }</span>
<span className="mx_Autocomplete_Completion_subtitle">{ subtitle }</span>
<span className="mx_Autocomplete_Completion_description">{ description }</span>
<span className="mx_Autocomplete_Completion_title">{title}</span>
<span className="mx_Autocomplete_Completion_subtitle">{subtitle}</span>
<span className="mx_Autocomplete_Completion_description">{description}</span>
</div>
);
});
@ -64,20 +58,21 @@ export const PillCompletion = forwardRef<IPillCompletionProps, any>((props, ref)
description,
className,
children,
'aria-selected': ariaSelectedAttribute,
"aria-selected": ariaSelectedAttribute,
...restProps
} = props;
return (
<div {...restProps}
className={classNames('mx_Autocomplete_Completion_pill', className)}
<div
{...restProps}
className={classNames("mx_Autocomplete_Completion_pill", className)}
role="option"
aria-selected={ariaSelectedAttribute}
ref={ref}
>
{ children }
<span className="mx_Autocomplete_Completion_title">{ title }</span>
<span className="mx_Autocomplete_Completion_subtitle">{ subtitle }</span>
<span className="mx_Autocomplete_Completion_description">{ description }</span>
{children}
<span className="mx_Autocomplete_Completion_title">{title}</span>
<span className="mx_Autocomplete_Completion_subtitle">{subtitle}</span>
<span className="mx_Autocomplete_Completion_description">{description}</span>
</div>
);
});

View file

@ -18,26 +18,26 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { uniq, sortBy } from 'lodash';
import EMOTICON_REGEX from 'emojibase-regex/emoticon';
import { Room } from 'matrix-js-sdk/src/models/room';
import React from "react";
import { uniq, sortBy } from "lodash";
import EMOTICON_REGEX from "emojibase-regex/emoticon";
import { Room } from "matrix-js-sdk/src/models/room";
import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider';
import QueryMatcher from './QueryMatcher';
import { PillCompletion } from './Components';
import { ICompletion, ISelectionRange } from './Autocompleter';
import { _t } from "../languageHandler";
import AutocompleteProvider from "./AutocompleteProvider";
import QueryMatcher from "./QueryMatcher";
import { PillCompletion } from "./Components";
import { ICompletion, ISelectionRange } from "./Autocompleter";
import SettingsStore from "../settings/SettingsStore";
import { EMOJI, IEmoji, getEmojiFromUnicode } from '../emoji';
import { TimelineRenderingType } from '../contexts/RoomContext';
import * as recent from '../emojipicker/recent';
import { EMOJI, IEmoji, getEmojiFromUnicode } from "../emoji";
import { TimelineRenderingType } from "../contexts/RoomContext";
import * as recent from "../emojipicker/recent";
const LIMIT = 20;
// Match for ascii-style ";-)" emoticons or ":wink:" shortcodes provided by emojibase
// anchored to only match from the start of parts otherwise it'll show emoji suggestions whilst typing matrix IDs
const EMOJI_REGEX = new RegExp('(' + EMOTICON_REGEX.source + '|(?:^|\\s):[+-\\w]*:?)$', 'g');
const EMOJI_REGEX = new RegExp("(" + EMOTICON_REGEX.source + "|(?:^|\\s):[+-\\w]*:?)$", "g");
interface ISortedEmoji {
emoji: IEmoji;
@ -80,12 +80,12 @@ export default class EmojiProvider extends AutocompleteProvider {
super({ commandRegex: EMOJI_REGEX, renderingType });
this.matcher = new QueryMatcher<ISortedEmoji>(SORTED_EMOJI, {
keys: [],
funcs: [o => o.emoji.shortcodes.map(s => `:${s}:`)],
funcs: [(o) => o.emoji.shortcodes.map((s) => `:${s}:`)],
// For matching against ascii equivalents
shouldMatchWordsOnly: false,
});
this.nameMatcher = new QueryMatcher(SORTED_EMOJI, {
keys: ['emoji.label'],
keys: ["emoji.label"],
// For removing punctuation
shouldMatchWordsOnly: true,
});
@ -115,39 +115,37 @@ export default class EmojiProvider extends AutocompleteProvider {
let sorters = [];
// make sure that emoticons come first
sorters.push(c => score(matchedString, c.emoji.emoticon || ""));
sorters.push((c) => score(matchedString, c.emoji.emoticon || ""));
// then sort by score (Infinity if matchedString not in shortcode)
sorters.push(c => score(matchedString, c.emoji.shortcodes[0]));
sorters.push((c) => score(matchedString, c.emoji.shortcodes[0]));
// then sort by max score of all shortcodes, trim off the `:`
const trimmedMatch = colonsTrimmed(matchedString);
sorters.push(c => Math.min(
...c.emoji.shortcodes.map(s => score(trimmedMatch, s)),
));
sorters.push((c) => Math.min(...c.emoji.shortcodes.map((s) => score(trimmedMatch, s))));
// If the matchedString is not empty, sort by length of shortcode. Example:
// matchedString = ":bookmark"
// completions = [":bookmark:", ":bookmark_tabs:", ...]
if (matchedString.length > 1) {
sorters.push(c => c.emoji.shortcodes[0].length);
sorters.push((c) => c.emoji.shortcodes[0].length);
}
// Finally, sort by original ordering
sorters.push(c => c._orderBy);
sorters.push((c) => c._orderBy);
completions = sortBy<ISortedEmoji>(uniq(completions), sorters);
completions = completions.slice(0, LIMIT);
// Do a second sort to place emoji matching with frequently used one on top
sorters = [];
this.recentlyUsed.forEach(emoji => {
sorters.push(c => score(emoji.shortcodes[0], c.emoji.shortcodes[0]));
this.recentlyUsed.forEach((emoji) => {
sorters.push((c) => score(emoji.shortcodes[0], c.emoji.shortcodes[0]));
});
completions = sortBy<ISortedEmoji>(uniq(completions), sorters);
return completions.map(c => ({
return completions.map((c) => ({
completion: c.emoji.unicode,
component: (
<PillCompletion title={`:${c.emoji.shortcodes[0]}:`} aria-label={c.emoji.unicode}>
<span>{ c.emoji.unicode }</span>
<span>{c.emoji.unicode}</span>
</PillCompletion>
),
range,
@ -157,7 +155,7 @@ export default class EmojiProvider extends AutocompleteProvider {
}
getName() {
return '😃 ' + _t('Emoji');
return "😃 " + _t("Emoji");
}
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
@ -167,7 +165,7 @@ export default class EmojiProvider extends AutocompleteProvider {
role="presentation"
aria-label={_t("Emoji Autocomplete")}
>
{ completions }
{completions}
</div>
);
}

View file

@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
import { Room } from "matrix-js-sdk/src/models/room";
import AutocompleteProvider from './AutocompleteProvider';
import { _t } from '../languageHandler';
import { MatrixClientPeg } from '../MatrixClientPeg';
import { PillCompletion } from './Components';
import AutocompleteProvider from "./AutocompleteProvider";
import { _t } from "../languageHandler";
import { MatrixClientPeg } from "../MatrixClientPeg";
import { PillCompletion } from "./Components";
import { ICompletion, ISelectionRange } from "./Autocompleter";
import RoomAvatar from '../components/views/avatars/RoomAvatar';
import { TimelineRenderingType } from '../contexts/RoomContext';
import RoomAvatar from "../components/views/avatars/RoomAvatar";
import { TimelineRenderingType } from "../contexts/RoomContext";
const AT_ROOM_REGEX = /@\S*/g;
@ -32,38 +32,36 @@ export default class NotifProvider extends AutocompleteProvider {
super({ commandRegex: AT_ROOM_REGEX, renderingType });
}
async getCompletions(
query: string,
selection: ISelectionRange,
force = false,
limit = -1,
): Promise<ICompletion[]> {
async getCompletions(query: string, selection: ISelectionRange, force = false, limit = -1): Promise<ICompletion[]> {
const client = MatrixClientPeg.get();
if (!this.room.currentState.mayTriggerNotifOfType('room', client.credentials.userId)) return [];
if (!this.room.currentState.mayTriggerNotifOfType("room", client.credentials.userId)) return [];
const { command, range } = this.getCurrentCommand(query, selection, force);
if (command?.[0].length > 1 &&
['@room', '@channel', '@everyone', '@here'].some(c => c.startsWith(command[0]))
if (
command?.[0].length > 1 &&
["@room", "@channel", "@everyone", "@here"].some((c) => c.startsWith(command[0]))
) {
return [{
completion: '@room',
completionId: '@room',
type: "at-room",
suffix: ' ',
component: (
<PillCompletion title="@room" description={_t("Notify the whole room")}>
<RoomAvatar width={24} height={24} room={this.room} />
</PillCompletion>
),
range,
}];
return [
{
completion: "@room",
completionId: "@room",
type: "at-room",
suffix: " ",
component: (
<PillCompletion title="@room" description={_t("Notify the whole room")}>
<RoomAvatar width={24} height={24} room={this.room} />
</PillCompletion>
),
range,
},
];
}
return [];
}
getName() {
return '❗️ ' + _t('Room Notification');
return "❗️ " + _t("Room Notification");
}
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
@ -73,7 +71,7 @@ export default class NotifProvider extends AutocompleteProvider {
role="presentation"
aria-label={_t("Notification Autocomplete")}
>
{ completions }
{completions}
</div>
);
}

View file

@ -16,10 +16,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { at, uniq } from 'lodash';
import { at, uniq } from "lodash";
import { removeHiddenChars } from "matrix-js-sdk/src/utils";
import { TimelineRenderingType } from '../contexts/RoomContext';
import { TimelineRenderingType } from "../contexts/RoomContext";
import { Leaves } from "../@types/common";
interface IOptions<T extends {}> {
@ -47,7 +47,7 @@ interface IOptions<T extends {}> {
*/
export default class QueryMatcher<T extends {}> {
private _options: IOptions<T>;
private _items: Map<string, {object: T, keyWeight: number}[]>;
private _items: Map<string, { object: T; keyWeight: number }[]>;
constructor(objects: T[], options: IOptions<T> = { keys: [] }) {
this._options = options;
@ -99,7 +99,7 @@ export default class QueryMatcher<T extends {}> {
match(query: string, limit = -1): T[] {
query = this.processQuery(query);
if (this._options.shouldMatchWordsOnly) {
query = query.replace(/[^\w]/g, '');
query = query.replace(/[^\w]/g, "");
}
if (query.length === 0) {
return [];
@ -111,13 +111,11 @@ export default class QueryMatcher<T extends {}> {
for (const [key, candidates] of this._items.entries()) {
let resultKey = key;
if (this._options.shouldMatchWordsOnly) {
resultKey = resultKey.replace(/[^\w]/g, '');
resultKey = resultKey.replace(/[^\w]/g, "");
}
const index = resultKey.indexOf(query);
if (index !== -1) {
matches.push(
...candidates.map((candidate) => ({ index, ...candidate })),
);
matches.push(...candidates.map((candidate) => ({ index, ...candidate })));
}
}

View file

@ -20,14 +20,14 @@ import React from "react";
import { sortBy, uniqBy } from "lodash";
import { Room } from "matrix-js-sdk/src/models/room";
import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider';
import { MatrixClientPeg } from '../MatrixClientPeg';
import QueryMatcher from './QueryMatcher';
import { PillCompletion } from './Components';
import { _t } from "../languageHandler";
import AutocompleteProvider from "./AutocompleteProvider";
import { MatrixClientPeg } from "../MatrixClientPeg";
import QueryMatcher from "./QueryMatcher";
import { PillCompletion } from "./Components";
import { makeRoomPermalink } from "../utils/permalinks/Permalinks";
import { ICompletion, ISelectionRange } from "./Autocompleter";
import RoomAvatar from '../components/views/avatars/RoomAvatar';
import RoomAvatar from "../components/views/avatars/RoomAvatar";
import { TimelineRenderingType } from "../contexts/RoomContext";
const ROOM_REGEX = /\B#\S*/g;
@ -51,7 +51,7 @@ export default class RoomProvider extends AutocompleteProvider {
constructor(room: Room, renderingType?: TimelineRenderingType) {
super({ commandRegex: ROOM_REGEX, renderingType });
this.matcher = new QueryMatcher([], {
keys: ['displayedAlias', 'matchName'],
keys: ["displayedAlias", "matchName"],
});
}
@ -59,15 +59,10 @@ export default class RoomProvider extends AutocompleteProvider {
const cli = MatrixClientPeg.get();
// filter out spaces here as they get their own autocomplete provider
return cli.getVisibleRooms().filter(r => !r.isSpaceRoom());
return cli.getVisibleRooms().filter((r) => !r.isSpaceRoom());
}
async getCompletions(
query: string,
selection: ISelectionRange,
force = false,
limit = -1,
): Promise<ICompletion[]> {
async getCompletions(query: string, selection: ISelectionRange, force = false, limit = -1): Promise<ICompletion[]> {
let completions = [];
const { command, range } = this.getCurrentCommand(query, selection, force);
if (command) {
@ -77,7 +72,7 @@ export default class RoomProvider extends AutocompleteProvider {
aliases = aliases.concat(matcherObject(room, room.getCanonicalAlias(), room.name));
}
if (room.getAltAliases().length) {
const altAliases = room.getAltAliases().map(alias => matcherObject(room, alias));
const altAliases = room.getAltAliases().map((alias) => matcherObject(room, alias));
aliases = aliases.concat(altAliases);
}
return aliases;
@ -85,9 +80,9 @@ export default class RoomProvider extends AutocompleteProvider {
// Filter out any matches where the user will have also autocompleted new rooms
matcherObjects = matcherObjects.filter((r) => {
const tombstone = r.room.currentState.getStateEvents("m.room.tombstone", "");
if (tombstone && tombstone.getContent() && tombstone.getContent()['replacement_room']) {
if (tombstone && tombstone.getContent() && tombstone.getContent()["replacement_room"]) {
const hasReplacementRoom = matcherObjects.some(
(r2) => r2.room.roomId === tombstone.getContent()['replacement_room'],
(r2) => r2.room.roomId === tombstone.getContent()["replacement_room"],
);
return !hasReplacementRoom;
}
@ -102,27 +97,29 @@ export default class RoomProvider extends AutocompleteProvider {
(c) => c.displayedAlias.length,
]);
completions = uniqBy(completions, (match) => match.room);
completions = completions.map((room) => {
return {
completion: room.displayedAlias,
completionId: room.room.roomId,
type: "room",
suffix: ' ',
href: makeRoomPermalink(room.displayedAlias),
component: (
<PillCompletion title={room.room.name} description={room.displayedAlias}>
<RoomAvatar width={24} height={24} room={room.room} />
</PillCompletion>
),
range,
};
}).filter((completion) => !!completion.completion && completion.completion.length > 0);
completions = completions
.map((room) => {
return {
completion: room.displayedAlias,
completionId: room.room.roomId,
type: "room",
suffix: " ",
href: makeRoomPermalink(room.displayedAlias),
component: (
<PillCompletion title={room.room.name} description={room.displayedAlias}>
<RoomAvatar width={24} height={24} room={room.room} />
</PillCompletion>
),
range,
};
})
.filter((completion) => !!completion.completion && completion.completion.length > 0);
}
return completions;
}
getName() {
return _t('Rooms');
return _t("Rooms");
}
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
@ -132,7 +129,7 @@ export default class RoomProvider extends AutocompleteProvider {
role="presentation"
aria-label={_t("Room Autocomplete")}
>
{ completions }
{completions}
</div>
);
}

View file

@ -16,13 +16,15 @@ limitations under the License.
import React from "react";
import { _t } from '../languageHandler';
import { MatrixClientPeg } from '../MatrixClientPeg';
import { _t } from "../languageHandler";
import { MatrixClientPeg } from "../MatrixClientPeg";
import RoomProvider from "./RoomProvider";
export default class SpaceProvider extends RoomProvider {
protected getRooms() {
return MatrixClientPeg.get().getVisibleRooms().filter(r => r.isSpaceRoom());
return MatrixClientPeg.get()
.getVisibleRooms()
.filter((r) => r.isSpaceRoom());
}
getName() {
@ -36,7 +38,7 @@ export default class SpaceProvider extends RoomProvider {
role="listbox"
aria-label={_t("Space Autocomplete")}
>
{ completions }
{completions}
</div>
);
}

View file

@ -17,24 +17,24 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { sortBy } from 'lodash';
import React from "react";
import { sortBy } from "lodash";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { RoomState, RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
import { IRoomTimelineData } from "matrix-js-sdk/src/models/event-timeline-set";
import { MatrixClientPeg } from '../MatrixClientPeg';
import QueryMatcher from './QueryMatcher';
import { PillCompletion } from './Components';
import AutocompleteProvider from './AutocompleteProvider';
import { _t } from '../languageHandler';
import { MatrixClientPeg } from "../MatrixClientPeg";
import QueryMatcher from "./QueryMatcher";
import { PillCompletion } from "./Components";
import AutocompleteProvider from "./AutocompleteProvider";
import { _t } from "../languageHandler";
import { makeUserPermalink } from "../utils/permalinks/Permalinks";
import { ICompletion, ISelectionRange } from "./Autocompleter";
import MemberAvatar from '../components/views/avatars/MemberAvatar';
import { TimelineRenderingType } from '../contexts/RoomContext';
import UserIdentifierCustomisations from '../customisations/UserIdentifier';
import MemberAvatar from "../components/views/avatars/MemberAvatar";
import { TimelineRenderingType } from "../contexts/RoomContext";
import UserIdentifierCustomisations from "../customisations/UserIdentifier";
const USER_REGEX = /\B@\S*/g;
@ -55,8 +55,8 @@ export default class UserProvider extends AutocompleteProvider {
});
this.room = room;
this.matcher = new QueryMatcher([], {
keys: ['name'],
funcs: [obj => obj.userId.slice(1)], // index by user id minus the leading '@'
keys: ["name"],
funcs: [(obj) => obj.userId.slice(1)], // index by user id minus the leading '@'
shouldMatchWordsOnly: false,
});
@ -117,21 +117,22 @@ export default class UserProvider extends AutocompleteProvider {
const fullMatch = command[0];
// Don't search if the query is a single "@"
if (fullMatch && fullMatch !== '@') {
if (fullMatch && fullMatch !== "@") {
// Don't include the '@' in our search query - it's only used as a way to trigger completion
const query = fullMatch.startsWith('@') ? fullMatch.substring(1) : fullMatch;
const query = fullMatch.startsWith("@") ? fullMatch.substring(1) : fullMatch;
completions = this.matcher.match(query, limit).map((user) => {
const description = UserIdentifierCustomisations.getDisplayUserIdentifier(
user.userId, { roomId: this.room.roomId, withDisplayName: true },
);
const displayName = (user.name || user.userId || '');
const description = UserIdentifierCustomisations.getDisplayUserIdentifier(user.userId, {
roomId: this.room.roomId,
withDisplayName: true,
});
const displayName = user.name || user.userId || "";
return {
// Length of completion should equal length of text in decorator. draft-js
// relies on the length of the entity === length of the text in the decoration.
completion: user.rawDisplayName,
completionId: user.userId,
type: "user",
suffix: (selection.beginning && range.start === 0) ? ': ' : ' ',
suffix: selection.beginning && range.start === 0 ? ": " : " ",
href: makeUserPermalink(user.userId),
component: (
<PillCompletion title={displayName} description={description}>
@ -146,7 +147,7 @@ export default class UserProvider extends AutocompleteProvider {
}
getName(): string {
return _t('Users');
return _t("Users");
}
private makeUsers() {
@ -161,7 +162,7 @@ export default class UserProvider extends AutocompleteProvider {
this.users = this.room.getJoinedMembers().filter(({ userId }) => userId !== currentUserId);
this.users = this.users.concat(this.room.getMembersWithMembership("invite"));
this.users = sortBy(this.users, (member) => 1E20 - lastSpoken[member.userId] || 1E20);
this.users = sortBy(this.users, (member) => 1e20 - lastSpoken[member.userId] || 1e20);
this.matcher.setObjects(this.users);
}
@ -173,7 +174,9 @@ export default class UserProvider extends AutocompleteProvider {
// Move the user that spoke to the front of the array
this.users.splice(
this.users.findIndex((user2) => user2.userId === user.userId), 1);
this.users.findIndex((user2) => user2.userId === user.userId),
1,
);
this.users = [user, ...this.users];
this.matcher.setObjects(this.users);
@ -186,7 +189,7 @@ export default class UserProvider extends AutocompleteProvider {
role="presentation"
aria-label={_t("User Autocomplete")}
>
{ completions }
{completions}
</div>
);
}