mirror of https://github.com/vector-im/riot-web
Merge pull request #5736 from matrix-org/t3chguy/spaces4.5
Spaces suggested rooms supportpull/21833/head
commit
98338f1505
|
@ -203,8 +203,9 @@ limitations under the License.
|
|||
.mx_SpaceRoomDirectory_actions {
|
||||
width: 180px;
|
||||
text-align: right;
|
||||
height: min-content;
|
||||
margin: auto 0 auto 28px;
|
||||
margin-left: 28px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
.mx_AccessibleButton {
|
||||
vertical-align: middle;
|
||||
|
@ -223,9 +224,5 @@ limitations under the License.
|
|||
line-height: $font-15px;
|
||||
color: $secondary-fg-color;
|
||||
}
|
||||
|
||||
.mx_Checkbox {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
// XXX: We shouldn't be using TemporaryTile anywhere - delete it.
|
||||
.mx_DecoratedRoomAvatar, .mx_TemporaryTile {
|
||||
.mx_DecoratedRoomAvatar, .mx_ExtraTile {
|
||||
position: relative;
|
||||
|
||||
&.mx_DecoratedRoomAvatar_cutout .mx_BaseAvatar {
|
||||
|
|
|
@ -22,7 +22,7 @@ import MultiInviter from './utils/MultiInviter';
|
|||
import Modal from './Modal';
|
||||
import * as sdk from './';
|
||||
import { _t } from './languageHandler';
|
||||
import InviteDialog, {KIND_DM, KIND_INVITE, KIND_SPACE_INVITE} from "./components/views/dialogs/InviteDialog";
|
||||
import InviteDialog, {KIND_DM, KIND_INVITE} from "./components/views/dialogs/InviteDialog";
|
||||
import CommunityPrototypeInviteDialog from "./components/views/dialogs/CommunityPrototypeInviteDialog";
|
||||
import {CommunityPrototypeStore} from "./stores/CommunityPrototypeStore";
|
||||
|
||||
|
@ -50,11 +50,10 @@ export function showStartChatInviteDialog(initialText) {
|
|||
}
|
||||
|
||||
export function showRoomInviteDialog(roomId) {
|
||||
const isSpace = MatrixClientPeg.get()?.getRoom(roomId)?.isSpaceRoom();
|
||||
// This dialog handles the room creation internally - we don't need to worry about it.
|
||||
Modal.createTrackedDialog(
|
||||
"Invite Users", isSpace ? "Space" : "Room", InviteDialog, {
|
||||
kind: isSpace ? KIND_SPACE_INVITE : KIND_INVITE,
|
||||
"Invite Users", "", InviteDialog, {
|
||||
kind: KIND_INVITE,
|
||||
roomId,
|
||||
},
|
||||
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
|
||||
|
|
|
@ -37,7 +37,7 @@ export default class VoipUserMapper {
|
|||
return results[0].userid;
|
||||
}
|
||||
|
||||
public async getOrCreateVirtualRoomForRoom(roomId: string):Promise<string> {
|
||||
public async getOrCreateVirtualRoomForRoom(roomId: string): Promise<string> {
|
||||
const userId = DMRoomMap.shared().getUserIdForRoomId(roomId);
|
||||
if (!userId) return null;
|
||||
|
||||
|
@ -52,7 +52,7 @@ export default class VoipUserMapper {
|
|||
return virtualRoomId;
|
||||
}
|
||||
|
||||
public nativeRoomForVirtualRoom(roomId: string):string {
|
||||
public nativeRoomForVirtualRoom(roomId: string): string {
|
||||
const virtualRoom = MatrixClientPeg.get().getRoom(roomId);
|
||||
if (!virtualRoom) return null;
|
||||
const virtualRoomEvent = virtualRoom.getAccountData(VIRTUAL_ROOM_EVENT_TYPE);
|
||||
|
@ -60,7 +60,7 @@ export default class VoipUserMapper {
|
|||
return virtualRoomEvent.getContent()['native_room'] || null;
|
||||
}
|
||||
|
||||
public isVirtualRoom(room: Room):boolean {
|
||||
public isVirtualRoom(room: Room): boolean {
|
||||
if (this.nativeRoomForVirtualRoom(room.roomId)) return true;
|
||||
|
||||
if (this.virtualRoomIdCache.has(room.roomId)) return true;
|
||||
|
@ -79,6 +79,8 @@ export default class VoipUserMapper {
|
|||
}
|
||||
|
||||
public async onNewInvitedRoom(invitedRoom: Room) {
|
||||
if (!CallHandler.sharedInstance().getSupportsVirtualRooms()) return;
|
||||
|
||||
const inviterId = invitedRoom.getDMInviter();
|
||||
console.log(`Checking virtual-ness of room ID ${invitedRoom.roomId}, invited by ${inviterId}`);
|
||||
const result = await CallHandler.sharedInstance().sipNativeLookup(inviterId);
|
||||
|
|
|
@ -64,6 +64,7 @@ export interface ISpaceSummaryEvent {
|
|||
state_key: string;
|
||||
content: {
|
||||
order?: string;
|
||||
suggested?: boolean;
|
||||
auto_join?: boolean;
|
||||
via?: string;
|
||||
};
|
||||
|
@ -91,7 +92,7 @@ const SubSpace: React.FC<ISubspaceProps> = ({
|
|||
const name = space.name || space.canonical_alias || space.aliases?.[0] || _t("Unnamed Space");
|
||||
|
||||
const evContent = event?.getContent();
|
||||
const [autoJoin, _setAutoJoin] = useState(evContent?.auto_join);
|
||||
const [suggested, _setSuggested] = useState(evContent?.suggested);
|
||||
const [removed, _setRemoved] = useState(!evContent?.via);
|
||||
|
||||
const cli = MatrixClientPeg.get();
|
||||
|
@ -102,12 +103,12 @@ const SubSpace: React.FC<ISubspaceProps> = ({
|
|||
let actions;
|
||||
if (editing && queueAction) {
|
||||
if (event && cli.getRoom(event.getRoomId())?.currentState.maySendStateEvent(event.getType(), cli.getUserId())) {
|
||||
const setAutoJoin = () => {
|
||||
_setAutoJoin(v => {
|
||||
const setSuggested = () => {
|
||||
_setSuggested(v => {
|
||||
queueAction({
|
||||
event,
|
||||
removed,
|
||||
autoJoin: !v,
|
||||
suggested: !v,
|
||||
});
|
||||
return !v;
|
||||
});
|
||||
|
@ -118,7 +119,7 @@ const SubSpace: React.FC<ISubspaceProps> = ({
|
|||
queueAction({
|
||||
event,
|
||||
removed: !v,
|
||||
autoJoin,
|
||||
suggested,
|
||||
});
|
||||
return !v;
|
||||
});
|
||||
|
@ -131,7 +132,7 @@ const SubSpace: React.FC<ISubspaceProps> = ({
|
|||
} else {
|
||||
actions = <React.Fragment>
|
||||
<FormButton kind="danger" onClick={setRemoved} label={_t("Remove from Space")} />
|
||||
<StyledCheckbox checked={autoJoin} onChange={setAutoJoin} />
|
||||
<StyledCheckbox checked={suggested} onChange={setSuggested} />
|
||||
</React.Fragment>;
|
||||
}
|
||||
} else {
|
||||
|
@ -182,8 +183,8 @@ const SubSpace: React.FC<ISubspaceProps> = ({
|
|||
|
||||
interface IAction {
|
||||
event: MatrixEvent;
|
||||
suggested: boolean;
|
||||
removed: boolean;
|
||||
autoJoin: boolean;
|
||||
}
|
||||
|
||||
interface IRoomTileProps {
|
||||
|
@ -199,7 +200,7 @@ const RoomTile = ({ room, event, editing, queueAction, onPreviewClick, onJoinCli
|
|||
const name = room.name || room.canonical_alias || room.aliases?.[0] || _t("Unnamed Room");
|
||||
|
||||
const evContent = event?.getContent();
|
||||
const [autoJoin, _setAutoJoin] = useState(evContent?.auto_join);
|
||||
const [suggested, _setSuggested] = useState(evContent?.suggested);
|
||||
const [removed, _setRemoved] = useState(!evContent?.via);
|
||||
|
||||
const cli = MatrixClientPeg.get();
|
||||
|
@ -209,12 +210,12 @@ const RoomTile = ({ room, event, editing, queueAction, onPreviewClick, onJoinCli
|
|||
let actions;
|
||||
if (editing && queueAction) {
|
||||
if (event && cli.getRoom(event.getRoomId())?.currentState.maySendStateEvent(event.getType(), cli.getUserId())) {
|
||||
const setAutoJoin = () => {
|
||||
_setAutoJoin(v => {
|
||||
const setSuggested = () => {
|
||||
_setSuggested(v => {
|
||||
queueAction({
|
||||
event,
|
||||
removed,
|
||||
autoJoin: !v,
|
||||
suggested: !v,
|
||||
});
|
||||
return !v;
|
||||
});
|
||||
|
@ -225,7 +226,7 @@ const RoomTile = ({ room, event, editing, queueAction, onPreviewClick, onJoinCli
|
|||
queueAction({
|
||||
event,
|
||||
removed: !v,
|
||||
autoJoin,
|
||||
suggested,
|
||||
});
|
||||
return !v;
|
||||
});
|
||||
|
@ -238,7 +239,7 @@ const RoomTile = ({ room, event, editing, queueAction, onPreviewClick, onJoinCli
|
|||
} else {
|
||||
actions = <React.Fragment>
|
||||
<FormButton kind="danger" onClick={setRemoved} label={_t("Remove from Space")} />
|
||||
<StyledCheckbox checked={autoJoin} onChange={setAutoJoin} />
|
||||
<StyledCheckbox checked={suggested} onChange={setSuggested} />
|
||||
</React.Fragment>;
|
||||
}
|
||||
} else {
|
||||
|
@ -445,10 +446,10 @@ const SpaceRoomDirectory: React.FC<IProps> = ({ space, initialText = "", onFinis
|
|||
|
||||
const onSaveButtonClicked = () => {
|
||||
// TODO setBusy
|
||||
pendingActions.current.forEach(({event, autoJoin, removed}) => {
|
||||
pendingActions.current.forEach(({event, suggested, removed}) => {
|
||||
const content = {
|
||||
...event.getContent(),
|
||||
auto_join: autoJoin,
|
||||
suggested,
|
||||
};
|
||||
|
||||
if (removed) {
|
||||
|
@ -463,7 +464,7 @@ const SpaceRoomDirectory: React.FC<IProps> = ({ space, initialText = "", onFinis
|
|||
if (isEditing) {
|
||||
adminButton = <React.Fragment>
|
||||
<FormButton label={_t("Save changes")} onClick={onSaveButtonClicked} />
|
||||
<span>{ _t("All users join by default") }</span>
|
||||
<span>{ _t("Promoted to users") }</span>
|
||||
</React.Fragment>;
|
||||
} else {
|
||||
adminButton = <FormButton label={_t("Manage rooms")} onClick={onManageButtonClicked} />;
|
||||
|
|
|
@ -557,7 +557,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
|||
case Phase.PublicCreateRooms:
|
||||
return <SpaceSetupFirstRooms
|
||||
space={this.props.space}
|
||||
title={_t("What discussions do you want to have?")}
|
||||
title={_t("What are some things you want to discuss?")}
|
||||
description={_t("We'll create rooms for each topic.")}
|
||||
onFinished={() => this.setState({ phase: Phase.PublicShare })}
|
||||
/>;
|
||||
|
|
|
@ -27,7 +27,7 @@ export default class InfoDialog extends React.Component {
|
|||
className: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
description: PropTypes.node,
|
||||
button: PropTypes.oneOfType(PropTypes.string, PropTypes.bool),
|
||||
button: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
|
||||
onFinished: PropTypes.func,
|
||||
hasCloseButton: PropTypes.bool,
|
||||
onKeyDown: PropTypes.func,
|
||||
|
|
|
@ -48,7 +48,6 @@ import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
|||
|
||||
export const KIND_DM = "dm";
|
||||
export const KIND_INVITE = "invite";
|
||||
export const KIND_SPACE_INVITE = "space_invite";
|
||||
export const KIND_CALL_TRANSFER = "call_transfer";
|
||||
|
||||
const INITIAL_ROOMS_SHOWN = 3; // Number of rooms to show at first
|
||||
|
@ -310,7 +309,7 @@ interface IInviteDialogProps {
|
|||
// not provided.
|
||||
kind: string,
|
||||
|
||||
// The room ID this dialog is for. Only required for KIND_INVITE and KIND_SPACE_INVITE.
|
||||
// The room ID this dialog is for. Only required for KIND_INVITE.
|
||||
roomId: string,
|
||||
|
||||
// The call to transfer. Only required for KIND_CALL_TRANSFER.
|
||||
|
@ -349,8 +348,8 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
if ((props.kind === KIND_INVITE || props.kind === KIND_SPACE_INVITE) && !props.roomId) {
|
||||
throw new Error("When using KIND_INVITE or KIND_SPACE_INVITE a roomId is required for an InviteDialog");
|
||||
if ((props.kind === KIND_INVITE) && !props.roomId) {
|
||||
throw new Error("When using KIND_INVITE a roomId is required for an InviteDialog");
|
||||
} else if (props.kind === KIND_CALL_TRANSFER && !props.call) {
|
||||
throw new Error("When using KIND_CALL_TRANSFER a call is required for an InviteDialog");
|
||||
}
|
||||
|
@ -1027,7 +1026,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
sectionSubname = _t("May include members not in %(communityName)s", {communityName});
|
||||
}
|
||||
|
||||
if (this.props.kind === KIND_INVITE || this.props.kind === KIND_SPACE_INVITE) {
|
||||
if (this.props.kind === KIND_INVITE) {
|
||||
sectionName = kind === 'recents' ? _t("Recently Direct Messaged") : _t("Suggestions");
|
||||
}
|
||||
|
||||
|
@ -1248,25 +1247,31 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
}
|
||||
buttonText = _t("Go");
|
||||
goButtonFn = this._startDm;
|
||||
} else if (this.props.kind === KIND_INVITE || this.props.kind === KIND_SPACE_INVITE) {
|
||||
title = this.props.kind === KIND_INVITE ? _t("Invite to this room") : _t("Invite to this space");
|
||||
} else if (this.props.kind === KIND_INVITE) {
|
||||
const room = MatrixClientPeg.get()?.getRoom(this.props.roomId);
|
||||
const isSpace = room?.isSpaceRoom();
|
||||
title = isSpace
|
||||
? _t("Invite to %(spaceName)s", {
|
||||
spaceName: room.name || _t("Unnamed Space"),
|
||||
})
|
||||
: _t("Invite to this room");
|
||||
|
||||
let helpTextUntranslated;
|
||||
if (this.props.kind === KIND_INVITE) {
|
||||
if (isSpace) {
|
||||
if (identityServersEnabled) {
|
||||
helpTextUntranslated = _td("Invite someone using their name, email address, username " +
|
||||
"(like <userId/>) or <a>share this room</a>.");
|
||||
"(like <userId/>) or <a>share this space</a>.");
|
||||
} else {
|
||||
helpTextUntranslated = _td("Invite someone using their name, username " +
|
||||
"(like <userId/>) or <a>share this room</a>.");
|
||||
"(like <userId/>) or <a>share this space</a>.");
|
||||
}
|
||||
} else { // KIND_SPACE_INVITE
|
||||
} else {
|
||||
if (identityServersEnabled) {
|
||||
helpTextUntranslated = _td("Invite someone using their name, email address, username " +
|
||||
"(like <userId/>) or <a>share this space</a>.");
|
||||
"(like <userId/>) or <a>share this room</a>.");
|
||||
} else {
|
||||
helpTextUntranslated = _td("Invite someone using their name, username " +
|
||||
"(like <userId/>) or <a>share this space</a>.");
|
||||
"(like <userId/>) or <a>share this room</a>.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -28,7 +28,7 @@ interface IProps {
|
|||
isSelected: boolean;
|
||||
displayName: string;
|
||||
avatar: React.ReactElement;
|
||||
notificationState: NotificationState;
|
||||
notificationState?: NotificationState;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
|
@ -36,8 +36,7 @@ interface IState {
|
|||
hover: boolean;
|
||||
}
|
||||
|
||||
// TODO: Remove with community invites in the room list: https://github.com/vector-im/element-web/issues/14456
|
||||
export default class TemporaryTile extends React.Component<IProps, IState> {
|
||||
export default class ExtraTile extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
|
@ -57,18 +56,21 @@ export default class TemporaryTile extends React.Component<IProps, IState> {
|
|||
public render(): React.ReactElement {
|
||||
// XXX: We copy classes because it's easier
|
||||
const classes = classNames({
|
||||
'mx_ExtraTile': true,
|
||||
'mx_RoomTile': true,
|
||||
'mx_TemporaryTile': true,
|
||||
'mx_RoomTile_selected': this.props.isSelected,
|
||||
'mx_RoomTile_minimized': this.props.isMinimized,
|
||||
});
|
||||
|
||||
const badge = (
|
||||
let badge;
|
||||
if (this.props.notificationState) {
|
||||
badge = (
|
||||
<NotificationBadge
|
||||
notification={this.props.notificationState}
|
||||
forceCount={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let name = this.props.displayName;
|
||||
if (typeof name !== 'string') name = '';
|
||||
|
@ -76,7 +78,7 @@ export default class TemporaryTile extends React.Component<IProps, IState> {
|
|||
|
||||
const nameClasses = classNames({
|
||||
"mx_RoomTile_name": true,
|
||||
"mx_RoomTile_nameHasUnreadEvents": this.props.notificationState.isUnread,
|
||||
"mx_RoomTile_nameHasUnreadEvents": this.props.notificationState?.isUnread,
|
||||
});
|
||||
|
||||
let nameContainer = (
|
|
@ -16,9 +16,10 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import React, { ReactComponentElement } from "react";
|
||||
import { Dispatcher } from "flux";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import * as fbEmitter from "fbemitter";
|
||||
|
||||
import { _t, _td } from "../../../languageHandler";
|
||||
import { RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex";
|
||||
|
@ -33,7 +34,7 @@ import RoomSublist from "./RoomSublist";
|
|||
import { ActionPayload } from "../../../dispatcher/payloads";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import GroupAvatar from "../avatars/GroupAvatar";
|
||||
import TemporaryTile from "./TemporaryTile";
|
||||
import ExtraTile from "./ExtraTile";
|
||||
import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState";
|
||||
import { NotificationColor } from "../../../stores/notifications/NotificationColor";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
|
@ -47,9 +48,11 @@ import { IconizedContextMenuOption, IconizedContextMenuOptionList } from "../con
|
|||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore";
|
||||
import CallHandler from "../../../CallHandler";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore, { SUGGESTED_ROOMS } from "../../../stores/SpaceStore";
|
||||
import { showAddExistingRooms, showCreateNewRoom } from "../../../utils/space";
|
||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||
import { ISpaceSummaryRoom } from "../../structures/SpaceRoomDirectory";
|
||||
import RoomAvatar from "../avatars/RoomAvatar";
|
||||
|
||||
interface IProps {
|
||||
onKeyDown: (ev: React.KeyboardEvent) => void;
|
||||
|
@ -63,6 +66,8 @@ interface IProps {
|
|||
interface IState {
|
||||
sublists: ITagMap;
|
||||
isNameFiltering: boolean;
|
||||
currentRoomId?: string;
|
||||
suggestedRooms: ISpaceSummaryRoom[];
|
||||
}
|
||||
|
||||
const TAG_ORDER: TagID[] = [
|
||||
|
@ -75,6 +80,7 @@ const TAG_ORDER: TagID[] = [
|
|||
|
||||
DefaultTagID.LowPriority,
|
||||
DefaultTagID.ServerNotice,
|
||||
DefaultTagID.Suggested,
|
||||
DefaultTagID.Archived,
|
||||
];
|
||||
const CUSTOM_TAGS_BEFORE_TAG = DefaultTagID.LowPriority;
|
||||
|
@ -242,6 +248,12 @@ const TAG_AESTHETICS: ITagAestheticsMap = {
|
|||
isInvite: false,
|
||||
defaultHidden: true,
|
||||
},
|
||||
|
||||
[DefaultTagID.Suggested]: {
|
||||
sectionLabel: _td("Suggested Rooms"),
|
||||
isInvite: false,
|
||||
defaultHidden: false,
|
||||
},
|
||||
};
|
||||
|
||||
function customTagAesthetics(tagId: TagID): ITagAesthetics {
|
||||
|
@ -260,6 +272,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
private dispatcherRef;
|
||||
private customTagStoreRef;
|
||||
private tagAesthetics: ITagAestheticsMap;
|
||||
private roomStoreToken: fbEmitter.EventSubscription;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
@ -267,6 +280,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
this.state = {
|
||||
sublists: {},
|
||||
isNameFiltering: !!RoomListStore.instance.getFirstNameFilterCondition(),
|
||||
suggestedRooms: SpaceStore.instance.suggestedRooms,
|
||||
};
|
||||
|
||||
// shallow-copy from the template as we need to make modifications to it
|
||||
|
@ -274,20 +288,30 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
this.updateDmAddRoomAction();
|
||||
|
||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
||||
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
SpaceStore.instance.on(SUGGESTED_ROOMS, this.updateSuggestedRooms);
|
||||
RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.updateLists);
|
||||
this.customTagStoreRef = CustomRoomTagStore.addListener(this.updateLists);
|
||||
this.updateLists(); // trigger the first update
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
SpaceStore.instance.off(SUGGESTED_ROOMS, this.updateSuggestedRooms);
|
||||
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.updateLists);
|
||||
defaultDispatcher.unregister(this.dispatcherRef);
|
||||
if (this.customTagStoreRef) this.customTagStoreRef.remove();
|
||||
if (this.roomStoreToken) this.roomStoreToken.remove();
|
||||
}
|
||||
|
||||
private onRoomViewStoreUpdate = () => {
|
||||
this.setState({
|
||||
currentRoomId: RoomViewStore.getRoomId(),
|
||||
});
|
||||
};
|
||||
|
||||
private updateDmAddRoomAction() {
|
||||
const dmTagAesthetics = objectShallowClone(TAG_AESTHETICS[DefaultTagID.DM]);
|
||||
if (CallHandler.sharedInstance().getSupportsPstnProtocol()) {
|
||||
|
@ -319,7 +343,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
|
||||
private getRoomDelta = (roomId: string, delta: number, unread = false) => {
|
||||
const lists = RoomListStore.instance.orderedLists;
|
||||
const rooms: Room = [];
|
||||
const rooms: Room[] = [];
|
||||
TAG_ORDER.forEach(t => {
|
||||
let listRooms = lists[t];
|
||||
|
||||
|
@ -340,6 +364,10 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
return room;
|
||||
};
|
||||
|
||||
private updateSuggestedRooms = (suggestedRooms: ISpaceSummaryRoom[]) => {
|
||||
this.setState({ suggestedRooms });
|
||||
};
|
||||
|
||||
private updateLists = () => {
|
||||
const newLists = RoomListStore.instance.orderedLists;
|
||||
if (SettingsStore.getValue("advancedRoomListLogging")) {
|
||||
|
@ -394,7 +422,44 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
dis.dispatch({ action: Action.ViewRoomDirectory, initialText });
|
||||
};
|
||||
|
||||
private renderCommunityInvites(): TemporaryTile[] {
|
||||
private renderSuggestedRooms(): ReactComponentElement<typeof ExtraTile>[] {
|
||||
return this.state.suggestedRooms.map(room => {
|
||||
const name = room.name || room.canonical_alias || room.aliases.pop() || _t("Empty room");
|
||||
const avatar = (
|
||||
<RoomAvatar
|
||||
oobData={{
|
||||
name,
|
||||
avatarUrl: room.avatar_url,
|
||||
}}
|
||||
width={32}
|
||||
height={32}
|
||||
resizeMethod="crop"
|
||||
/>
|
||||
);
|
||||
const viewRoom = () => {
|
||||
defaultDispatcher.dispatch({
|
||||
action: "view_room",
|
||||
room_id: room.room_id,
|
||||
oobData: {
|
||||
avatarUrl: room.avatar_url,
|
||||
name,
|
||||
},
|
||||
});
|
||||
};
|
||||
return (
|
||||
<ExtraTile
|
||||
isMinimized={this.props.isMinimized}
|
||||
isSelected={this.state.currentRoomId === room.room_id}
|
||||
displayName={name}
|
||||
avatar={avatar}
|
||||
onClick={viewRoom}
|
||||
key={`suggestedRoomTile_${room.room_id}`}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private renderCommunityInvites(): ReactComponentElement<typeof ExtraTile>[] {
|
||||
// TODO: Put community invites in a more sensible place (not in the room list)
|
||||
// See https://github.com/vector-im/element-web/issues/14456
|
||||
return MatrixClientPeg.get().getGroups().filter(g => {
|
||||
|
@ -415,7 +480,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
});
|
||||
};
|
||||
return (
|
||||
<TemporaryTile
|
||||
<ExtraTile
|
||||
isMinimized={this.props.isMinimized}
|
||||
isSelected={false}
|
||||
displayName={g.name}
|
||||
|
@ -447,7 +512,14 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
|
||||
for (const orderedTagId of tagOrder) {
|
||||
const orderedRooms = this.state.sublists[orderedTagId] || [];
|
||||
const extraTiles = orderedTagId === DefaultTagID.Invite ? this.renderCommunityInvites() : null;
|
||||
|
||||
let extraTiles = null;
|
||||
if (orderedTagId === DefaultTagID.Invite) {
|
||||
extraTiles = this.renderCommunityInvites();
|
||||
} else if (orderedTagId === DefaultTagID.Suggested) {
|
||||
extraTiles = this.renderSuggestedRooms();
|
||||
}
|
||||
|
||||
const totalTiles = orderedRooms.length + (extraTiles ? extraTiles.length : 0);
|
||||
if (totalTiles === 0 && !ALWAYS_VISIBLE_TAGS.includes(orderedTagId)) {
|
||||
continue; // skip tag - not needed
|
||||
|
@ -470,7 +542,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
isMinimized={this.props.isMinimized}
|
||||
onResize={this.props.onResize}
|
||||
showSkeleton={showSkeleton}
|
||||
extraBadTilesThatShouldntExist={extraTiles}
|
||||
extraTiles={extraTiles}
|
||||
/>);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import {createRef} from "react";
|
||||
import { createRef, ReactComponentElement } from "react";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import classNames from 'classnames';
|
||||
import { RovingAccessibleButton, RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex";
|
||||
|
@ -48,7 +48,7 @@ import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNo
|
|||
import RoomListLayoutStore from "../../../stores/room-list/RoomListLayoutStore";
|
||||
import { arrayFastClone, arrayHasOrderChange } from "../../../utils/arrays";
|
||||
import { objectExcluding, objectHasDiff } from "../../../utils/objects";
|
||||
import TemporaryTile from "./TemporaryTile";
|
||||
import ExtraTile from "./ExtraTile";
|
||||
import { ListNotificationState } from "../../../stores/notifications/ListNotificationState";
|
||||
import IconizedContextMenu from "../context_menus/IconizedContextMenu";
|
||||
|
||||
|
@ -73,9 +73,7 @@ interface IProps {
|
|||
onResize: () => void;
|
||||
showSkeleton?: boolean;
|
||||
|
||||
// TODO: Don't use this. It's for community invites, and community invites shouldn't be here.
|
||||
// You should feel bad if you use this.
|
||||
extraBadTilesThatShouldntExist?: TemporaryTile[];
|
||||
extraTiles?: ReactComponentElement<typeof ExtraTile>[];
|
||||
|
||||
// TODO: Account for https://github.com/vector-im/element-web/issues/14179
|
||||
}
|
||||
|
@ -95,7 +93,7 @@ interface IState {
|
|||
isExpanded: boolean; // used for the for expand of the sublist when the room list is being filtered
|
||||
height: number;
|
||||
rooms: Room[];
|
||||
filteredExtraTiles?: TemporaryTile[];
|
||||
filteredExtraTiles?: ReactComponentElement<typeof ExtraTile>[];
|
||||
}
|
||||
|
||||
export default class RoomSublist extends React.Component<IProps, IState> {
|
||||
|
@ -153,12 +151,12 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
return padding;
|
||||
}
|
||||
|
||||
private get extraTiles(): TemporaryTile[] | null {
|
||||
private get extraTiles(): ReactComponentElement<typeof ExtraTile>[] | null {
|
||||
if (this.state.filteredExtraTiles) {
|
||||
return this.state.filteredExtraTiles;
|
||||
}
|
||||
if (this.props.extraBadTilesThatShouldntExist) {
|
||||
return this.props.extraBadTilesThatShouldntExist;
|
||||
if (this.props.extraTiles) {
|
||||
return this.props.extraTiles;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -177,7 +175,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Readonly<IProps>, prevState: Readonly<IState>) {
|
||||
const prevExtraTiles = prevState.filteredExtraTiles || prevProps.extraBadTilesThatShouldntExist;
|
||||
const prevExtraTiles = prevState.filteredExtraTiles || prevProps.extraTiles;
|
||||
// as the rooms can come in one by one we need to reevaluate
|
||||
// the amount of available rooms to cap the amount of requested visible rooms by the layout
|
||||
if (RoomSublist.calcNumTiles(prevState.rooms, prevExtraTiles) !== this.numTiles) {
|
||||
|
@ -200,8 +198,8 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
|
||||
// If we're supposed to handle extra tiles, take the performance hit and re-render all the
|
||||
// time so we don't have to consider them as part of the visible room optimization.
|
||||
const prevExtraTiles = this.props.extraBadTilesThatShouldntExist || [];
|
||||
const nextExtraTiles = (nextState.filteredExtraTiles || nextProps.extraBadTilesThatShouldntExist) || [];
|
||||
const prevExtraTiles = this.props.extraTiles || [];
|
||||
const nextExtraTiles = (nextState.filteredExtraTiles || nextProps.extraTiles) || [];
|
||||
if (prevExtraTiles.length > 0 || nextExtraTiles.length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
@ -249,10 +247,10 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
private onListsUpdated = () => {
|
||||
const stateUpdates: IState & any = {}; // &any is to avoid a cast on the initializer
|
||||
|
||||
if (this.props.extraBadTilesThatShouldntExist) {
|
||||
if (this.props.extraTiles) {
|
||||
const nameCondition = RoomListStore.instance.getFirstNameFilterCondition();
|
||||
if (nameCondition) {
|
||||
stateUpdates.filteredExtraTiles = this.props.extraBadTilesThatShouldntExist
|
||||
stateUpdates.filteredExtraTiles = this.props.extraTiles
|
||||
.filter(t => nameCondition.matches(t.props.displayName || ""));
|
||||
} else if (this.state.filteredExtraTiles) {
|
||||
stateUpdates.filteredExtraTiles = null;
|
||||
|
|
|
@ -107,7 +107,8 @@ const SpaceCreateMenu = ({ onFinished }) => {
|
|||
if (visibility === null) {
|
||||
body = <React.Fragment>
|
||||
<h2>{ _t("Create a space") }</h2>
|
||||
<p>{ _t("Organise rooms into spaces, for just you or anyone") }</p>
|
||||
<p>{ _t("Spaces are new ways to group rooms and people. " +
|
||||
"To join an existing space you’ll need an invite") }</p>
|
||||
|
||||
<SpaceCreateMenuType
|
||||
title={_t("Public")}
|
||||
|
@ -117,12 +118,12 @@ const SpaceCreateMenu = ({ onFinished }) => {
|
|||
/>
|
||||
<SpaceCreateMenuType
|
||||
title={_t("Private")}
|
||||
description={_t("Invite only space, best for yourself or teams")}
|
||||
description={_t("Invite only, best for yourself or teams")}
|
||||
className="mx_SpaceCreateMenuType_private"
|
||||
onClick={() => setVisibility(Visibility.Private)}
|
||||
/>
|
||||
|
||||
{/*<p>{ _t("Looking to join an existing space?") }</p>*/}
|
||||
<p>{ _t("You can change this later") }</p>
|
||||
</React.Fragment>;
|
||||
} else {
|
||||
body = <React.Fragment>
|
||||
|
@ -134,9 +135,7 @@ const SpaceCreateMenu = ({ onFinished }) => {
|
|||
|
||||
<h2>
|
||||
{
|
||||
visibility === Visibility.Public
|
||||
? _t("Personalise your public space")
|
||||
: _t("Personalise your private space")
|
||||
visibility === Visibility.Public ? _t("Your public space") : _t("Your private space")
|
||||
}
|
||||
</h2>
|
||||
<p>
|
||||
|
|
|
@ -988,14 +988,15 @@
|
|||
"Name": "Name",
|
||||
"Description": "Description",
|
||||
"Create a space": "Create a space",
|
||||
"Organise rooms into spaces, for just you or anyone": "Organise rooms into spaces, for just you or anyone",
|
||||
"Spaces are new ways to group rooms and people. To join an existing space you’ll need an invite": "Spaces are new ways to group rooms and people. To join an existing space you’ll need an invite",
|
||||
"Public": "Public",
|
||||
"Open space for anyone, best for communities": "Open space for anyone, best for communities",
|
||||
"Private": "Private",
|
||||
"Invite only space, best for yourself or teams": "Invite only space, best for yourself or teams",
|
||||
"Invite only, best for yourself or teams": "Invite only, best for yourself or teams",
|
||||
"You can change this later": "You can change this later",
|
||||
"Go back": "Go back",
|
||||
"Personalise your public space": "Personalise your public space",
|
||||
"Personalise your private space": "Personalise your private space",
|
||||
"Your public space": "Your public space",
|
||||
"Your private space": "Your private space",
|
||||
"Give it a photo, name and description to help you identify it.": "Give it a photo, name and description to help you identify it.",
|
||||
"You can change these at any point.": "You can change these at any point.",
|
||||
"Creating...": "Creating...",
|
||||
|
@ -1532,7 +1533,9 @@
|
|||
"Low priority": "Low priority",
|
||||
"System Alerts": "System Alerts",
|
||||
"Historical": "Historical",
|
||||
"Suggested Rooms": "Suggested Rooms",
|
||||
"Custom Tag": "Custom Tag",
|
||||
"Empty room": "Empty room",
|
||||
"Can't see what you’re looking for?": "Can't see what you’re looking for?",
|
||||
"Start a new chat": "Start a new chat",
|
||||
"Explore all public rooms": "Explore all public rooms",
|
||||
|
@ -2194,10 +2197,12 @@
|
|||
"Start a conversation with someone using their name or username (like <userId/>).": "Start a conversation with someone using their name or username (like <userId/>).",
|
||||
"This won't invite them to %(communityName)s. To invite someone to %(communityName)s, click <a>here</a>": "This won't invite them to %(communityName)s. To invite someone to %(communityName)s, click <a>here</a>",
|
||||
"Go": "Go",
|
||||
"Invite someone using their name, email address, username (like <userId/>) or <a>share this room</a>.": "Invite someone using their name, email address, username (like <userId/>) or <a>share this room</a>.",
|
||||
"Invite someone using their name, username (like <userId/>) or <a>share this room</a>.": "Invite someone using their name, username (like <userId/>) or <a>share this room</a>.",
|
||||
"Invite to %(spaceName)s": "Invite to %(spaceName)s",
|
||||
"Unnamed Space": "Unnamed Space",
|
||||
"Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.": "Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.",
|
||||
"Invite someone using their name, username (like <userId/>) or <a>share this space</a>.": "Invite someone using their name, username (like <userId/>) or <a>share this space</a>.",
|
||||
"Invite someone using their name, email address, username (like <userId/>) or <a>share this room</a>.": "Invite someone using their name, email address, username (like <userId/>) or <a>share this room</a>.",
|
||||
"Invite someone using their name, username (like <userId/>) or <a>share this room</a>.": "Invite someone using their name, username (like <userId/>) or <a>share this room</a>.",
|
||||
"Transfer": "Transfer",
|
||||
"a new master key signature": "a new master key signature",
|
||||
"a new cross-signing key signature": "a new cross-signing key signature",
|
||||
|
@ -2595,14 +2600,13 @@
|
|||
"Drop file here to upload": "Drop file here to upload",
|
||||
"You have %(count)s unread notifications in a prior version of this room.|other": "You have %(count)s unread notifications in a prior version of this room.",
|
||||
"You have %(count)s unread notifications in a prior version of this room.|one": "You have %(count)s unread notification in a prior version of this room.",
|
||||
"Unnamed Space": "Unnamed Space",
|
||||
"Undo": "Undo",
|
||||
"Remove from Space": "Remove from Space",
|
||||
"No permissions": "No permissions",
|
||||
"You're in this space": "You're in this space",
|
||||
"You're in this room": "You're in this room",
|
||||
"Save changes": "Save changes",
|
||||
"All users join by default": "All users join by default",
|
||||
"Promoted to users": "Promoted to users",
|
||||
"Manage rooms": "Manage rooms",
|
||||
"Find a room...": "Find a room...",
|
||||
"Accept Invite": "Accept Invite",
|
||||
|
@ -2634,7 +2638,7 @@
|
|||
"Invite your teammates": "Invite your teammates",
|
||||
"Invite by username": "Invite by username",
|
||||
"Inviting...": "Inviting...",
|
||||
"What discussions do you want to have?": "What discussions do you want to have?",
|
||||
"What are some things you want to discuss?": "What are some things you want to discuss?",
|
||||
"We'll create rooms for each topic.": "We'll create rooms for each topic.",
|
||||
"What projects are you working on?": "What projects are you working on?",
|
||||
"We'll create rooms for each of them. You can add existing rooms after setup.": "We'll create rooms for each of them. You can add existing rooms after setup.",
|
||||
|
|
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {throttle, sortBy} from "lodash";
|
||||
import {EventType} from "matrix-js-sdk/src/@types/event";
|
||||
import {sortBy, throttle} from "lodash";
|
||||
import {EventType, RoomType} from "matrix-js-sdk/src/@types/event";
|
||||
import {Room} from "matrix-js-sdk/src/models/room";
|
||||
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
|
||||
|
||||
|
@ -33,6 +33,7 @@ import {EnhancedMap, mapDiff} from "../utils/maps";
|
|||
import {setHasDiff} from "../utils/sets";
|
||||
import {objectDiff} from "../utils/objects";
|
||||
import {arrayHasDiff} from "../utils/arrays";
|
||||
import {ISpaceSummaryEvent, ISpaceSummaryRoom} from "../components/structures/SpaceRoomDirectory";
|
||||
|
||||
type SpaceKey = string | symbol;
|
||||
|
||||
|
@ -41,11 +42,14 @@ interface IState {}
|
|||
const ACTIVE_SPACE_LS_KEY = "mx_active_space";
|
||||
|
||||
export const HOME_SPACE = Symbol("home-space");
|
||||
export const SUGGESTED_ROOMS = Symbol("suggested-rooms");
|
||||
|
||||
export const UPDATE_TOP_LEVEL_SPACES = Symbol("top-level-spaces");
|
||||
export const UPDATE_SELECTED_SPACE = Symbol("selected-space");
|
||||
// Space Room ID/HOME_SPACE will be emitted when a Space's children change
|
||||
|
||||
const MAX_SUGGESTED_ROOMS = 20;
|
||||
|
||||
const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => { // [spaces, rooms]
|
||||
return arr.reduce((result, room: Room) => {
|
||||
result[room.isSpaceRoom() ? 0 : 1].push(room);
|
||||
|
@ -85,6 +89,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
private spaceFilteredRooms = new Map<string | symbol, Set<string>>();
|
||||
// The space currently selected in the Space Panel - if null then `Home` is selected
|
||||
private _activeSpace?: Room = null;
|
||||
private _suggestedRooms: ISpaceSummaryRoom[] = [];
|
||||
|
||||
public get spacePanelSpaces(): Room[] {
|
||||
return this.rootSpaces;
|
||||
|
@ -94,11 +99,16 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
return this._activeSpace || null;
|
||||
}
|
||||
|
||||
public setActiveSpace(space: Room | null) {
|
||||
public get suggestedRooms(): ISpaceSummaryRoom[] {
|
||||
return this._suggestedRooms;
|
||||
}
|
||||
|
||||
public async setActiveSpace(space: Room | null) {
|
||||
if (space === this.activeSpace) return;
|
||||
|
||||
this._activeSpace = space;
|
||||
this.emit(UPDATE_SELECTED_SPACE, this.activeSpace);
|
||||
this.emit(SUGGESTED_ROOMS, this._suggestedRooms = []);
|
||||
|
||||
// persist space selected
|
||||
if (space) {
|
||||
|
@ -106,11 +116,29 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
} else {
|
||||
window.localStorage.removeItem(ACTIVE_SPACE_LS_KEY);
|
||||
}
|
||||
|
||||
if (space) {
|
||||
try {
|
||||
const data: {
|
||||
rooms: ISpaceSummaryRoom[];
|
||||
events: ISpaceSummaryEvent[];
|
||||
} = await this.matrixClient.getSpaceSummary(space.roomId, 0, true, false, MAX_SUGGESTED_ROOMS);
|
||||
if (this._activeSpace === space) {
|
||||
this._suggestedRooms = data.rooms.filter(roomInfo => {
|
||||
return roomInfo.room_type !== RoomType.Space && !this.matrixClient.getRoom(roomInfo.room_id);
|
||||
});
|
||||
this.emit(SUGGESTED_ROOMS, this._suggestedRooms);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public addRoomToSpace(space: Room, roomId: string, via: string[], autoJoin = false) {
|
||||
public addRoomToSpace(space: Room, roomId: string, via: string[], suggested = false, autoJoin = false) {
|
||||
return this.matrixClient.sendStateEvent(space.roomId, EventType.SpaceChild, {
|
||||
via,
|
||||
suggested,
|
||||
auto_join: autoJoin,
|
||||
}, roomId);
|
||||
}
|
||||
|
@ -327,6 +355,12 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
// this.onRoomUpdate(room);
|
||||
this.onRoomsUpdate();
|
||||
}
|
||||
|
||||
const numSuggestedRooms = this._suggestedRooms.length;
|
||||
this._suggestedRooms = this._suggestedRooms.filter(r => r.room_id !== room.roomId);
|
||||
if (numSuggestedRooms !== this._suggestedRooms.length) {
|
||||
this.emit(SUGGESTED_ROOMS, this._suggestedRooms);
|
||||
}
|
||||
};
|
||||
|
||||
private onRoomState = (ev: MatrixEvent) => {
|
||||
|
@ -408,6 +442,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
}
|
||||
|
||||
protected async onAction(payload: ActionPayload) {
|
||||
if (!SettingsStore.getValue("feature_spaces")) return;
|
||||
switch (payload.action) {
|
||||
case "view_room": {
|
||||
const room = this.matrixClient?.getRoom(payload.room_id);
|
||||
|
|
|
@ -409,7 +409,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
|||
}
|
||||
|
||||
private async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<any> {
|
||||
if (cause === RoomUpdateCause.NewRoom) {
|
||||
if (cause === RoomUpdateCause.NewRoom && room.getMyMembership() === "invite") {
|
||||
// Let the visibility provider know that there is a new invited room. It would be nice
|
||||
// if this could just be an event that things listen for but the point of this is that
|
||||
// we delay doing anything about this room until the VoipUserMapper had had a chance
|
||||
|
|
|
@ -24,6 +24,7 @@ export enum DefaultTagID {
|
|||
Favourite = "m.favourite",
|
||||
DM = "im.vector.fake.direct",
|
||||
ServerNotice = "m.server_notice",
|
||||
Suggested = "im.vector.fake.suggested",
|
||||
}
|
||||
|
||||
export const OrderedDefaultTagIDs = [
|
||||
|
@ -33,6 +34,7 @@ export const OrderedDefaultTagIDs = [
|
|||
DefaultTagID.Untagged,
|
||||
DefaultTagID.LowPriority,
|
||||
DefaultTagID.ServerNotice,
|
||||
DefaultTagID.Suggested,
|
||||
DefaultTagID.Archived,
|
||||
];
|
||||
|
||||
|
|
Loading…
Reference in New Issue