Add view_room
to Action
enum (#7203)
* Add ViewRoom action to Action enum Signed-off-by: Renan <renancleyson.f@gmail.com> * Change view_room occurrences to Action.ViewRoom Signed-off-by: Renan <renancleyson.f@gmail.com> * Add missing Action import
This commit is contained in:
parent
965539da2d
commit
ae0dba4e87
42 changed files with 81 additions and 58 deletions
|
@ -175,7 +175,7 @@ class FeaturedRoom extends React.Component {
|
|||
e.stopPropagation();
|
||||
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_alias: this.props.summaryInfo.profile.canonical_alias,
|
||||
room_id: this.props.summaryInfo.room_id,
|
||||
});
|
||||
|
|
|
@ -676,7 +676,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
case 'view_user_info':
|
||||
this.viewUser(payload.userId, payload.subAction);
|
||||
break;
|
||||
case 'view_room': {
|
||||
case Action.ViewRoom: {
|
||||
// Takes either a room ID or room alias: if switching to a room the client is already
|
||||
// known to be in (eg. user clicks on a room in the recents panel), supply the ID
|
||||
// If the user is clicking on a room in the context of the alias being presented
|
||||
|
@ -1124,7 +1124,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
|
||||
if (dmRooms.length > 0) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: dmRooms[0],
|
||||
});
|
||||
} else {
|
||||
|
@ -1378,7 +1378,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
|
||||
private viewLastRoom() {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: localStorage.getItem('mx_last_room_id'),
|
||||
});
|
||||
}
|
||||
|
@ -1793,7 +1793,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
}
|
||||
|
||||
const payload = {
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
event_id: eventId,
|
||||
via_servers: via,
|
||||
// If an event ID is given in the URL hash, notify RoomViewStore to mark
|
||||
|
@ -1849,7 +1849,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
|
||||
onAliasClick(event: MouseEvent, alias: string) {
|
||||
event.preventDefault();
|
||||
dis.dispatch({ action: 'view_room', room_alias: alias });
|
||||
dis.dispatch({ action: Action.ViewRoom, room_alias: alias });
|
||||
}
|
||||
|
||||
onUserClick(event: MouseEvent, userId: string) {
|
||||
|
|
|
@ -200,7 +200,7 @@ export default class RightPanel extends React.Component<IProps, IState> {
|
|||
};
|
||||
|
||||
private onAction = (payload: ActionPayload) => {
|
||||
const isChangingRoom = payload.action === 'view_room' && payload.room_id !== this.props.room.roomId;
|
||||
const isChangingRoom = payload.action === Action.ViewRoom && payload.room_id !== this.props.room.roomId;
|
||||
const isViewingThread = this.state.phase === RightPanelPhases.ThreadView;
|
||||
if (isChangingRoom && isViewingThread) {
|
||||
dispatchShowThreadsPanelEvent();
|
||||
|
|
|
@ -49,6 +49,7 @@ import { ActionPayload } from "../../dispatcher/payloads";
|
|||
import { getDisplayAliasForAliasSet } from "../../Rooms";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { Action } from "../../dispatcher/actions";
|
||||
|
||||
const MAX_NAME_LENGTH = 80;
|
||||
const MAX_TOPIC_LENGTH = 800;
|
||||
|
@ -493,7 +494,7 @@ export default class RoomDirectory extends React.Component<IProps, IState> {
|
|||
private showRoom(room: IPublicRoomsChunkRoom, roomAlias?: string, autoJoin = false, shouldPeek = false) {
|
||||
this.onFinished();
|
||||
const payload: ActionPayload = {
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
auto_join: autoJoin,
|
||||
should_peek: shouldPeek,
|
||||
_type: "room_directory", // instrumentation
|
||||
|
|
|
@ -94,7 +94,7 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
|
|||
};
|
||||
|
||||
private onAction = (payload: ActionPayload) => {
|
||||
if (payload.action === 'view_room' && payload.clear_search) {
|
||||
if (payload.action === Action.ViewRoom && payload.clear_search) {
|
||||
this.clearInput();
|
||||
} else if (payload.action === 'focus_room_filter' && this.inputRef.current) {
|
||||
this.inputRef.current.focus();
|
||||
|
|
|
@ -745,7 +745,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||
private onUserScroll = () => {
|
||||
if (this.state.initialEventId && this.state.isInitialEventHighlighted) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: this.state.room.roomId,
|
||||
event_id: this.state.initialEventId,
|
||||
highlighted: false,
|
||||
|
@ -854,7 +854,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||
|
||||
setImmediate(() => {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: roomId,
|
||||
deferred_action: payload,
|
||||
});
|
||||
|
@ -1236,7 +1236,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||
dis.dispatch({
|
||||
action: 'do_after_sync_prepared',
|
||||
deferred_action: {
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: this.getRoomId(),
|
||||
},
|
||||
});
|
||||
|
@ -1612,7 +1612,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||
// an event will take care of both clearing the URL fragment and
|
||||
// jumping to the bottom
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: this.state.room.roomId,
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -22,6 +22,7 @@ import { throttle } from 'lodash';
|
|||
import AccessibleButton from '../../components/views/elements/AccessibleButton';
|
||||
import classNames from 'classnames';
|
||||
import { replaceableComponent } from "../../utils/replaceableComponent";
|
||||
import { Action } from '../../dispatcher/actions';
|
||||
|
||||
interface IProps {
|
||||
onSearch?: (query: string) => void;
|
||||
|
@ -77,7 +78,7 @@ export default class SearchBox extends React.Component<IProps, IState> {
|
|||
if (!this.props.enableRoomSearchFocus) return;
|
||||
|
||||
switch (payload.action) {
|
||||
case 'view_room':
|
||||
case Action.ViewRoom:
|
||||
if (this.search.current && payload.clear_search) {
|
||||
this.clearSearch();
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
|||
private onScroll = (): void => {
|
||||
if (this.props.initialEvent && this.props.initialEventHighlighted) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: this.props.room.roomId,
|
||||
event_id: this.props.initialEvent?.getId(),
|
||||
highlighted: false,
|
||||
|
|
|
@ -32,6 +32,7 @@ import RoomContext from "../../contexts/RoomContext";
|
|||
import UserActivity from "../../UserActivity";
|
||||
import Modal from "../../Modal";
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
import { Action } from '../../dispatcher/actions';
|
||||
import { Key } from '../../Keyboard';
|
||||
import Timer from '../../utils/Timer';
|
||||
import shouldHideEvent from '../../shouldHideEvent';
|
||||
|
@ -1135,7 +1136,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
onFinished = () => {
|
||||
// go via the dispatcher so that the URL is updated
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: this.props.timelineSet.room.roomId,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -333,7 +333,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
const chat = CommunityPrototypeStore.instance.getSelectedCommunityGeneralChat();
|
||||
if (chat) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: chat.roomId,
|
||||
}, true);
|
||||
dis.dispatch({ action: Action.SetRightPanelPhase, phase: RightPanelPhases.RoomMemberList });
|
||||
|
|
|
@ -234,7 +234,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
|
||||
private viewInRoom = () => {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
event_id: this.props.mxEvent.getId(),
|
||||
highlighted: true,
|
||||
room_id: this.props.mxEvent.getRoomId(),
|
||||
|
|
|
@ -113,7 +113,7 @@ const SpaceContextMenu = ({ space, onFinished, ...props }: IProps) => {
|
|||
ev.stopPropagation();
|
||||
|
||||
defaultDispatcher.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: space.roomId,
|
||||
forceTimeline: true,
|
||||
});
|
||||
|
|
|
@ -18,6 +18,7 @@ import React, { useCallback, useEffect, useState } from "react";
|
|||
import { MatrixEvent } from "matrix-js-sdk/src";
|
||||
import { ButtonEvent } from "../elements/AccessibleButton";
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||
import { copyPlaintext } from "../../../utils/strings";
|
||||
import { ChevronFace, ContextMenuTooltipButton } from "../../structures/ContextMenu";
|
||||
|
@ -48,7 +49,7 @@ const ThreadListContextMenu: React.FC<IProps> = ({ mxEvent, permalinkCreator, on
|
|||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
event_id: mxEvent.getId(),
|
||||
highlighted: true,
|
||||
room_id: mxEvent.getRoomId(),
|
||||
|
|
|
@ -23,6 +23,7 @@ import AccessibleButton from "../elements/AccessibleButton";
|
|||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import InfoTooltip from "../elements/InfoTooltip";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import { Action } from '../../../dispatcher/actions';
|
||||
import { showCommunityRoomInviteDialog } from "../../../RoomInvite";
|
||||
import GroupStore from "../../../stores/GroupStore";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
|
@ -100,7 +101,7 @@ export default class CreateCommunityPrototypeDialog extends React.PureComponent<
|
|||
// Force the group store to update as it might have missed the general chat
|
||||
await GroupStore.refreshGroupRooms(result.group_id);
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: result.room_id,
|
||||
});
|
||||
showCommunityRoomInviteDialog(result.room_id, this.state.name);
|
||||
|
|
|
@ -677,7 +677,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
}
|
||||
if (existingRoom) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: existingRoom.roomId,
|
||||
should_peek: false,
|
||||
joining: false,
|
||||
|
|
|
@ -29,6 +29,7 @@ import MessageTimestamp from "../messages/MessageTimestamp";
|
|||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { formatFullDate } from "../../../DateUtils";
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import { Action } from '../../../dispatcher/actions';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
|
@ -332,7 +333,7 @@ export default class ImageView extends React.Component<IProps, IState> {
|
|||
// matrix.to, but also for it to enable routing within Element when clicked.
|
||||
ev.preventDefault();
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
event_id: this.props.mxEvent.getId(),
|
||||
highlighted: true,
|
||||
room_id: this.props.mxEvent.getRoomId(),
|
||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import { Action } from '../../../dispatcher/actions';
|
||||
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
|
@ -38,7 +39,7 @@ export default class RoomCreate extends React.Component<IProps> {
|
|||
const predecessor = this.props.mxEvent.getContent()['predecessor'];
|
||||
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
event_id: predecessor['event_id'],
|
||||
highlighted: true,
|
||||
room_id: predecessor['room_id'],
|
||||
|
|
|
@ -126,7 +126,7 @@ async function openDMForUser(matrixClient: MatrixClient, userId: string) {
|
|||
|
||||
if (lastActiveRoom) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: lastActiveRoom.roomId,
|
||||
});
|
||||
return;
|
||||
|
@ -368,7 +368,7 @@ const UserOptionsSection: React.FC<{
|
|||
const onReadReceiptButton = function() {
|
||||
const room = cli.getRoom(member.roomId);
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
highlighted: true,
|
||||
event_id: room.getEventReadUpTo(member.userId),
|
||||
room_id: member.roomId,
|
||||
|
|
|
@ -919,7 +919,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
// matrix.to, but also for it to enable routing within Element when clicked.
|
||||
e.preventDefault();
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
event_id: this.props.mxEvent.getId(),
|
||||
highlighted: true,
|
||||
room_id: this.props.mxEvent.getRoomId(),
|
||||
|
|
|
@ -380,7 +380,7 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
|
||||
const viaServers = [this.state.tombstone.getSender().split(':').slice(1).join(':')];
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
highlighted: true,
|
||||
event_id: createEventId,
|
||||
room_id: replacementRoomId,
|
||||
|
|
|
@ -20,6 +20,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
|
|||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import MessageEvent from "../messages/MessageEvent";
|
||||
import MemberAvatar from "../avatars/MemberAvatar";
|
||||
|
@ -45,7 +46,7 @@ export default class PinnedEventTile extends React.Component<IProps> {
|
|||
|
||||
private onTileClicked = () => {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
event_id: this.props.event.getId(),
|
||||
highlighted: true,
|
||||
room_id: this.props.event.getRoomId(),
|
||||
|
|
|
@ -18,6 +18,7 @@ import React, { createRef } from 'react';
|
|||
import classNames from 'classnames';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import { Action } from '../../../dispatcher/actions';
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
||||
import SenderProfile from "../messages/SenderProfile";
|
||||
|
@ -90,7 +91,7 @@ export default class ReplyTile extends React.PureComponent<IProps> {
|
|||
this.props.toggleExpandedQuote();
|
||||
} else {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
event_id: this.props.mxEvent.getId(),
|
||||
highlighted: true,
|
||||
room_id: this.props.mxEvent.getRoomId(),
|
||||
|
|
|
@ -18,6 +18,7 @@ import React from 'react';
|
|||
import { Room } from 'matrix-js-sdk/src';
|
||||
import classNames from 'classnames';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import { Action } from '../../../dispatcher/actions';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
|
@ -39,7 +40,7 @@ export default class RoomDetailList extends React.Component<IProps> {
|
|||
|
||||
private onDetailsClick = (ev: React.MouseEvent, room: Room): void => {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
room_alias: room.getCanonicalAlias() || (room.getAltAliases() || [])[0],
|
||||
});
|
||||
|
|
|
@ -297,7 +297,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
const room = this.getRoomDelta(currentRoomId, viewRoomDeltaPayload.delta, viewRoomDeltaPayload.unread);
|
||||
if (room) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
show_room_tile: true, // to make sure the room gets scrolled into view
|
||||
});
|
||||
|
|
|
@ -38,6 +38,7 @@ import { ListAlgorithm, SortAlgorithm } from "../../../stores/room-list/algorith
|
|||
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import NotificationBadge from "./NotificationBadge";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import { Key } from "../../../Keyboard";
|
||||
|
@ -444,7 +445,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
|
||||
if (room) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
show_room_tile: true, // to make sure the room gets scrolled into view
|
||||
});
|
||||
|
|
|
@ -22,6 +22,7 @@ import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex";
|
|||
import AccessibleButton, { ButtonEvent } from "../../views/elements/AccessibleButton";
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import defaultDispatcher from '../../../dispatcher/dispatcher';
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { Key } from "../../../Keyboard";
|
||||
import ActiveRoomObserver from "../../../ActiveRoomObserver";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
@ -235,7 +236,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
show_room_tile: true, // make sure the room is visible in the list
|
||||
room_id: this.props.room.roomId,
|
||||
clear_search: (ev && (ev.key === Key.ENTER || ev.key === Key.SPACE)),
|
||||
|
|
|
@ -24,6 +24,7 @@ import RoomUpgradeDialog from "../../../dialogs/RoomUpgradeDialog";
|
|||
import DevtoolsDialog from "../../../dialogs/DevtoolsDialog";
|
||||
import Modal from "../../../../../Modal";
|
||||
import dis from "../../../../../dispatcher/dispatcher";
|
||||
import { Action } from '../../../../../dispatcher/actions';
|
||||
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
|
||||
|
||||
interface IProps {
|
||||
|
@ -89,7 +90,7 @@ export default class AdvancedRoomSettingsTab extends React.Component<IProps, ISt
|
|||
e.stopPropagation();
|
||||
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: this.state.oldRoomId,
|
||||
event_id: this.state.oldEventId,
|
||||
});
|
||||
|
|
|
@ -112,7 +112,7 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
|||
try {
|
||||
if (request.channel.roomId) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: request.channel.roomId,
|
||||
should_peek: false,
|
||||
});
|
||||
|
|
|
@ -20,6 +20,7 @@ import React from 'react';
|
|||
import { _t, _td } from '../../../../languageHandler';
|
||||
import RoomAvatar from '../../avatars/RoomAvatar';
|
||||
import dis from '../../../../dispatcher/dispatcher';
|
||||
import { Action } from '../../../../dispatcher/actions';
|
||||
import classNames from 'classnames';
|
||||
import AccessibleTooltipButton from '../../elements/AccessibleTooltipButton';
|
||||
|
||||
|
@ -44,7 +45,7 @@ const onFullscreenClick = () => {
|
|||
|
||||
const onExpandClick = (roomId: string) => {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: roomId,
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue