Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/embed-link-previews.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@rocket.chat/meteor': minor
'@rocket.chat/core-typings': minor
'@rocket.chat/rest-typings': minor
'@rocket.chat/models': minor
'@rocket.chat/model-typings': minor
'@rocket.chat/i18n': minor
---

Adds per-channel setting to disable link previews.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type RoomSettings = {
favorite: boolean;
defaultValue: boolean;
};
disableLinkPreviews: boolean;
};

type RoomSettingsValidators = {
Expand Down Expand Up @@ -351,6 +352,9 @@ const settingSavers: RoomSettingsSavers = {
async roomAvatar({ value, rid, user }) {
await setRoomAvatar(rid, value, user);
},
async disableLinkPreviews({ value, rid }) {
await Rooms.saveDisableLinkPreviewsById(rid, value);
},
};

declare module '@rocket.chat/ddp-client' {
Expand Down Expand Up @@ -387,6 +391,7 @@ const fields: (keyof RoomSettings)[] = [
'retentionOverrideGlobal',
'encrypted',
'favorite',
'disableLinkPreviews',
];

const validate = <TRoomSetting extends keyof RoomSettings>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
archived,
joinCodeRequired,
hideSysMes,
disableLinkPreviews,
retentionEnabled,
retentionOverrideGlobal,
roomType: roomTypeP,
Expand Down Expand Up @@ -153,6 +154,7 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
const handleUpdateRoomData = useEffectEvent(
async ({
hideSysMes,
disableLinkPreviews,
joinCodeRequired,
retentionEnabled,
retentionOverrideGlobal,
Expand Down Expand Up @@ -223,6 +225,7 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
const archivedField = useId();
const joinCodeRequiredField = useId();
const hideSysMesField = useId();
const disableLinkPreviewsField = useId();
const retentionEnabledField = useId();
const retentionOverrideGlobalField = useId();
const retentionMaxAgeField = useId();
Expand Down Expand Up @@ -490,6 +493,20 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
</FieldRow>
</Field>
)}
{canViewHideSysMes && (
<Field>
<FieldRow>
<FieldLabel htmlFor={disableLinkPreviewsField}>{t('Disable_Link_Previews')}</FieldLabel>
<Controller
control={control}
name='disableLinkPreviews'
render={({ field: { value, ...field } }) => (
<ToggleSwitch id={disableLinkPreviewsField} {...field} checked={value} disabled={isFederated} />
)}
/>
</FieldRow>
</Field>
)}
</FieldGroup>
</AccordionItem>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type EditRoomInfoFormData = {
archived: boolean;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: disableLinkPreviews is added to form initial values but never sent to the server in the save handler. In EditRoomInfo.tsx, handleUpdateRoomData destructures disableLinkPreviews from formData and then calls saveAction({ ...data, ... }) without ever including disableLinkPreviews. The server endpoint /v1/rooms.saveRoomSettings does handle this field (see saveRoomSettings.ts:355), so the toggle will appear to work but changes will never be persisted.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/useEditRoomInitialValues.ts, line 56:

<comment>`disableLinkPreviews` is added to form initial values but never sent to the server in the save handler. In `EditRoomInfo.tsx`, `handleUpdateRoomData` destructures `disableLinkPreviews` from `formData` and then calls `saveAction({ ...data, ... })` without ever including `disableLinkPreviews`. The server endpoint `/v1/rooms.saveRoomSettings` does handle this field (see `saveRoomSettings.ts:355`), so the toggle will appear to work but changes will never be persisted.</comment>

<file context>
@@ -52,6 +53,7 @@ export const useEditRoomInitialValues = (room: IRoomWithRetentionPolicy): Partia
 			joinCodeRequired: !!joinCodeRequired,
 			systemMessages: Array.isArray(sysMes) ? sysMes : [],
 			hideSysMes: Array.isArray(sysMes) ? !!sysMes?.length : !!sysMes,
+			disableLinkPreviews: !!disableLinkPreviews,
 			encrypted,
 			...(canEditRoomRetentionPolicy &&
</file context>

joinCodeRequired: boolean;
hideSysMes: boolean;
disableLinkPreviews: boolean;
encrypted: boolean;
retentionEnabled: boolean;
retentionOverrideGlobal: boolean;
Expand All @@ -35,7 +36,7 @@ export const useEditRoomInitialValues = (room: IRoomWithRetentionPolicy): Partia
const retentionPolicy = useRetentionPolicy(room);
const canEditRoomRetentionPolicy = usePermission('edit-room-retention-policy', room._id);

const { t, ro, archived, topic, description, announcement, joinCodeRequired, sysMes, encrypted, retention, reactWhenReadOnly } = room;
const { t, ro, archived, topic, description, announcement, joinCodeRequired, sysMes, encrypted, retention, reactWhenReadOnly, disableLinkPreviews } = room;

return useMemo(
(): Partial<EditRoomInfoFormData> => ({
Expand All @@ -52,6 +53,7 @@ export const useEditRoomInitialValues = (room: IRoomWithRetentionPolicy): Partia
joinCodeRequired: !!joinCodeRequired,
systemMessages: Array.isArray(sysMes) ? sysMes : [],
hideSysMes: Array.isArray(sysMes) ? !!sysMes?.length : !!sysMes,
disableLinkPreviews: !!disableLinkPreviews,
encrypted,
...(canEditRoomRetentionPolicy &&
retentionPolicy?.enabled && {
Expand All @@ -77,6 +79,7 @@ export const useEditRoomInitialValues = (room: IRoomWithRetentionPolicy): Partia
topic,
encrypted,
reactWhenReadOnly,
disableLinkPreviews,
canEditRoomRetentionPolicy,
],
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
} from '@rocket.chat/core-typings';
import { isOEmbedUrlWithMetadata } from '@rocket.chat/core-typings';
import { Logger } from '@rocket.chat/logger';
import { OEmbedCache, Messages } from '@rocket.chat/models';
import { OEmbedCache, Messages, Rooms } from '@rocket.chat/models';
import { serverFetch as fetch } from '@rocket.chat/server-fetch';
import { isAbsoluteURL } from '@rocket.chat/tools';
import he from 'he';
Expand Down Expand Up @@ -336,6 +336,13 @@ const rocketUrlParser = async function (message: IMessage): Promise<IMessage> {
return message;
}

if (message.rid) {
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
const room = await Rooms.findOneById(message.rid, { projection: { disableLinkPreviews: 1 } });
if (room?.disableLinkPreviews) {
return message;
}
}

if (!Array.isArray(message.urls)) {
return message;
}
Expand Down
2 changes: 2 additions & 0 deletions packages/core-typings/src/IRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export interface IRoom extends IRocketChatRecord {
// TODO: this boolean might be an accident
sysMes?: MessageTypesValues[] | boolean;

disableLinkPreviews?: boolean;

u: Pick<IUser, '_id' | 'username' | 'name'>;
uids?: Array<string>;

Expand Down
1 change: 1 addition & 0 deletions packages/i18n/src/locales/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -2524,6 +2524,7 @@
"Hide_On_Workspace": "Hide on workspace",
"Hide_Private_Warning": "Are you sure you want to hide the discussion with \"{{roomName}}\"?",
"Hide_Room_Warning": "Are you sure you want to hide the channel \"{{roomName}}\"?",
"Disable_Link_Previews": "Disable link previews",
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
"Hide_System_Messages": "Hide system messages",
"Hide_Unread_Room_Status": "Hide Unread Room Status",
"Hide_additional_fields": "Hide additional fields",
Expand Down
1 change: 1 addition & 0 deletions packages/model-typings/src/models/IRoomsModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ export interface IRoomsModel extends IBaseModel<IRoom> {
saveFeaturedById(rid: string, featured: boolean): Promise<UpdateResult>;
saveDefaultById(rid: string, defaultValue: boolean): Promise<UpdateResult>;
saveFavoriteById(rid: string, favorite: boolean, defaultValue: boolean): Promise<UpdateResult>;
saveDisableLinkPreviewsById(rid: string, disableLinkPreviews: boolean): Promise<UpdateResult>;
saveRetentionEnabledById(rid: string, retentionEnabled: boolean): Promise<UpdateResult>;
saveRetentionMaxAgeById(rid: string, retentionMaxAge: number): Promise<UpdateResult>;
saveRetentionExcludePinnedById(rid: string, retentionExcludePinned: boolean): Promise<UpdateResult>;
Expand Down
14 changes: 14 additions & 0 deletions packages/models/src/models/Rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1897,6 +1897,20 @@ export class RoomsRaw extends BaseRaw<IRoom> implements IRoomsModel {
return this.updateOne(query, update);
}

saveDisableLinkPreviewsById(_id: IRoom['_id'], value: boolean): Promise<UpdateResult> {
const query: Filter<IRoom> = { _id };

const update: UpdateFilter<IRoom> = {};

if (value == null) {
update.$unset = { disableLinkPreviews: 1 };
} else {
update.$set = { disableLinkPreviews: value };
}

return this.updateOne(query, update);
}

saveRetentionEnabledById(_id: IRoom['_id'], value: boolean): Promise<UpdateResult> {
const query: Filter<IRoom> = { _id };

Expand Down
2 changes: 2 additions & 0 deletions packages/rest-typings/src/v1/rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ type RoomsSaveRoomSettingsProps = {
retentionFilesOnly?: boolean;
retentionIgnoreThreads?: boolean;
retentionOverrideGlobal?: boolean;
disableLinkPreviews?: boolean;
};

const RoomsSaveRoomSettingsSchema = {
Expand Down Expand Up @@ -466,6 +467,7 @@ const RoomsSaveRoomSettingsSchema = {
retentionFilesOnly: { type: 'boolean', nullable: true },
retentionIgnoreThreads: { type: 'boolean', nullable: true },
retentionOverrideGlobal: { type: 'boolean', nullable: true },
disableLinkPreviews: { type: 'boolean', nullable: true },
},
required: ['rid'],
additionalProperties: false,
Expand Down