Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🌟 Added remove link preview #2238

Merged
merged 2 commits into from
Jun 9, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 9 additions & 0 deletions twake/backend/node/src/services/messages/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ export interface MessageThreadMessagesServiceAPI
context: CompanyExecutionContext,
pagination: Pagination,
): Promise<ListResult<Message>>;

deleteLinkPreview(
item: {
message_id: string;
thread_id: string;
encoded_link: string;
},
context: ThreadExecutionContext,
): Promise<SaveResult<Message>>;
}

export interface MessageViewsServiceAPI extends TwakeServiceProvider, Initializable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,5 @@ export type MessageLinks = {
favicon: string | null;
img_width: number | null;
img_height: number | null;
url: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { logger, TwakeContext } from "../../../core/platform/framework";
import { Message, TYPE as MessageTableName } from "../entities/messages";
import {
BookmarkOperation,
DeleteLinkOperation,
MessageFileDownloadEvent,
PinOperation,
ReactionOperation,
Expand Down Expand Up @@ -153,4 +154,43 @@ export class ThreadMessagesOperationsService {
},
} as MessageFileDownloadEvent);
}

/**
* Delete a link preview from a message
*
* @param {DeleteLinkOperation} operation - params of the operation
* @param {ThreadExecutionContext} context - Thread execution context
* @returns {Promise<SaveResult<Message>>} - Result of the operation
*/
async deleteLinkPreview(
operation: DeleteLinkOperation,
context: ThreadExecutionContext,
): Promise<SaveResult<Message>> {
if (
!context?.user?.server_request &&
!gr.services.messages.threads.checkAccessToThread(context)
) {
logger.error(`no access ${context.thread.id}`);
throw Error("can't remove link preview from message.");
}

const message = await this.repository.findOne({
thread_id: context.thread.id,
id: operation.message_id,
});

if (!message) {
logger.error("This message doesn't exists");
throw Error("Can't edit message links previews.");
}

const decoded_url = decodeURIComponent(operation.encoded_link);

message.links = message.links.filter(({ url }: { url: string }) => url !== decoded_url);

await this.repository.save(message);
this.threadMessagesService.onSaved(message, { created: false }, context);

return new SaveResult<Message>("message", message, OperationType.UPDATE);
}
}
47 changes: 33 additions & 14 deletions twake/backend/node/src/services/messages/services/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { MessageFile, TYPE as MsgFileTableName } from "../entities/message-files
import {
BookmarkOperation,
CompanyExecutionContext,
DeleteLinkOperation,
MessagesGetThreadOptions,
MessagesSaveOptions,
MessageWithReplies,
Expand Down Expand Up @@ -464,7 +465,7 @@ export class ThreadMessagesService implements MessageThreadMessagesServiceAPI {
});
}

let extendedList = [];
const extendedList = [];
for (const m of list.getEntities()) {
extendedList.push(await this.completeMessage(m));
}
Expand Down Expand Up @@ -545,21 +546,25 @@ export class ThreadMessagesService implements MessageThreadMessagesServiceAPI {
path: getThreadMessagePath(context as ThreadExecutionContext) + "/" + message.id,
},
])
async onSaved(message: Message, options: { created?: boolean }, context: ThreadExecutionContext) {
const messageLinks = getLinks(message);

gr.platformServices.pubsub.publish<LinkPreviewPubsubRequest>("services:preview:links", {
data: {
links: messageLinks,
message: {
context,
resource: message,
created: options?.created,
async onSaved(
message: Message,
options: { created?: boolean },
context: ThreadExecutionContext,
): Promise<SaveResult<Message>> {
if (options.created && !message.ephemeral) {
const messageLinks = getLinks(message);

gr.platformServices.pubsub.publish<LinkPreviewPubsubRequest>("services:preview:links", {
data: {
links: messageLinks,
message: {
context,
resource: message,
created: options?.created,
},
},
},
});
});

if (options.created && !message.ephemeral) {
await gr.services.messages.threads.addReply(message.thread_id);
}

Expand Down Expand Up @@ -826,4 +831,18 @@ export class ThreadMessagesService implements MessageThreadMessagesServiceAPI {
const msgPromises = threadsIds.map(id => this.repository.findOne({ thread_id: id, id }));
return new ListResult<Message>("message", await Promise.all(msgPromises), nextPage);
}

/**
* Deletes a link preview from message operation
*
* @param {DeleteLinkOperation} operation - The delete link operation
* @param {ThreadExecutionContext} context - The thread execution context
* @returns
*/
async deleteLinkPreview(
operation: DeleteLinkOperation,
context: ThreadExecutionContext,
): Promise<SaveResult<Message>> {
return this.operations.deleteLinkPreview(operation, context);
}
}
6 changes: 6 additions & 0 deletions twake/backend/node/src/services/messages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,9 @@ export type FlatPinnedFromMessage = {
message: any;
thread: any;
};

export interface DeleteLinkOperation {
message_id: string;
thread_id: string;
encoded_link: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,43 @@ export class MessagesController
handleError(reply, err);
}
}

/**
* Delete link preview handler
*
* @param {FastifyRequest} request - The request object
* @param {FastifyReply} reply - The reply object
* @returns {Promise<ResourceUpdateResponse<Message>>} - The response object
*/
async deleteLinkPreview(
request: FastifyRequest<{
Params: {
company_id: string;
thread_id: string;
message_id: string;
encoded_url: string;
};
}>,
reply: FastifyReply,
): Promise<ResourceUpdateResponse<Message>> {
const context = getThreadExecutionContext(request);
try {
const result = await gr.services.messages.messages.deleteLinkPreview(
{
message_id: request.params.message_id,
thread_id: request.params.thread_id,
encoded_link: request.params.encoded_url,
},
context,
);

return {
resource: result.entity,
};
} catch (err) {
handleError(reply, err);
}
}
}

function getThreadExecutionContext(
Expand Down
7 changes: 7 additions & 0 deletions twake/backend/node/src/services/messages/web/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ const routes: FastifyPluginCallback = (fastify: FastifyInstance, options, next)
handler: messagesController.delete.bind(messagesController),
});

fastify.route({
method: "DELETE",
url: "/companies/:company_id/threads/:thread_id/messages/:message_id/links/:encoded_url",
preValidation: [fastify.authenticate],
handler: messagesController.deleteLinkPreview.bind(messagesController),
});

/**
* Views routes
*/
Expand Down
18 changes: 18 additions & 0 deletions twake/frontend/src/app/features/messages/api/message-api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,24 @@ class MessageAPIClient {
);
}

/**
* Delete a link preview from a message
*
* @param {String} companyId - The company id
* @param {String} threadId - The thread id
* @param {String} messageId - The message id
* @param {String} url - The url to delete from the previews
* @returns {Promise<void>}
*/
async deleteLinkPreview(companyId: string, threadId: string, messageId: string, url: string) {
const encoded_link = encodeURIComponent(url);
const response = await Api.delete<{ resource: NodeMessage }>(
`${this.prefixUrl}/companies/${companyId}/threads/${threadId}/messages/${messageId}/links/${encoded_link}`,
);

return response.resource;
}

async search(searchString: string, options?: BaseSearchOptions) {
let companyId = options?.company_id ? options.company_id : Workspace.currentGroupId;
let query = `/internal/services/messages/v1/companies/${companyId}/search?q=${searchString}`;
Expand Down
19 changes: 18 additions & 1 deletion twake/frontend/src/app/features/messages/hooks/use-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,24 @@ export const useMessage = (partialKey: AtomMessageKey) => {
if (updated) setValue(messageToMessageWithReplies(updated));
};

return { message, get, react, pin, remove, bookmark, save, move };
/**
* Delete a preview for given url
*
* @param {String} url - the url corresponding to the preview to delete
* @returns {Promise<void>}
*/
const deleteLinkPreview = async (url: string): Promise<void> => {
const updated = await MessageAPIClient.deleteLinkPreview(
partialKey.companyId,
partialKey.threadId,
partialKey.id || '',
url,
);

if (updated) setValue(messageToMessageWithReplies(updated));
};

return { message, get, react, pin, remove, bookmark, save, move, deleteLinkPreview };
};

//Function to recompute reactions after a frontend operation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,9 @@
}
}
}

.delete-link-preview {
margin-right: 10px;
padding-top: 15px;
color: grey !important;
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,53 @@
import React from 'react';
import React, { useContext } from 'react';
import { MessageLinkType } from 'app/features/messages/types/message';
import './LinkPreview.scss';
import { useMessage } from 'app/features/messages/hooks/use-message';
import { MessageContext } from '../message-with-replies';
import { CloseOutlined } from '@ant-design/icons';
import { Col, Row } from 'antd';

type PropsType = {
preview: MessageLinkType;
};

export default ({ preview }: PropsType): React.ReactElement => (
<div className="ant-card ant-card-bordered ant-card-small ant-card-type-inner link-preview">
<div className="ant-card-body">
<div className="ant-card-meta">
<div className="ant-card-meta-detail">
<div className="ant-card-meta-avatar">
<span className="ant-avatar ant-avatar-circle ant-avatar-image">
<img alt={preview.domain} src={preview.favicon} />
</span>
<span className="link-preview-domain">{preview.domain}</span>
</div>
<div className="preview-title">
<a href={preview.url} target="_blank" rel="noreferrer">
{preview.title}
</a>
export default ({ preview }: PropsType): React.ReactElement => {
const context = useContext(MessageContext);
const { deleteLinkPreview } = useMessage(context);

return (
<Row>
<Col>
<CloseOutlined onClick={() => deleteLinkPreview(preview.url)} className="delete-link-preview" />
</Col>
<Col>
<div className="ant-card ant-card-bordered ant-card-small ant-card-type-inner link-preview">
<div className="ant-card-body">
<div className="ant-card-meta">
<div className="ant-card-meta-detail">
<div className="ant-card-meta-avatar">
<span className="ant-avatar ant-avatar-circle ant-avatar-image">
<img alt={preview.domain} src={preview.favicon} />
</span>
<span className="link-preview-domain">{preview.domain}</span>
</div>
<div className="preview-title">
<a href={preview.url} target="_blank" rel="noreferrer">
{preview.title}
</a>
</div>
<div className="ant-card-meta-description">{preview.description}</div>
</div>
</div>
<div className="ant-card-cover">
<img
alt={preview.title}
src={preview.img}
onClick={() => window.open(preview.url, '_blank')}
/>
</div>
</div>
<div className="ant-card-meta-description">{preview.description}</div>
</div>
</div>
<div className="ant-card-cover">
<img
alt={preview.title}
src={preview.img}
onClick={() => window.open(preview.url, '_blank')}
/>
</div>
</div>
</div>
);
</Col>
</Row>
);
};