Frontend: Consistent Message Edited Indicator
This commit is contained in:
parent
0641cf2e53
commit
a415224678
@ -1,14 +1,16 @@
|
||||
import MessageBox from '../message-box';
|
||||
import defaultPatterns from '../../../types/patterns';
|
||||
import { FunctionComponent } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import striptags from 'striptags';
|
||||
import { previewImage } from '../../../store/ui';
|
||||
|
||||
interface MessageContentProps {
|
||||
message: Entity.Message;
|
||||
}
|
||||
|
||||
const MessageContent: FunctionComponent<MessageContentProps> = ({ message }) => {
|
||||
const dispatch = useDispatch();
|
||||
const editingMessageId = useSelector((s: Store.AppState) => s.ui.editingMessageId);
|
||||
|
||||
const patterns = {
|
||||
@ -48,13 +50,23 @@ const MessageContent: FunctionComponent<MessageContentProps> = ({ message }) =>
|
||||
`<img src="${process.env.REACT_APP_CDN_URL}${imageURL}"`).join(''));
|
||||
|
||||
const messageHTML =
|
||||
`${message.content && format(striptags(message.content))}` +
|
||||
`${message.attachmentURLs?.map(imageURL =>
|
||||
`<img
|
||||
style="max-width: 512px"
|
||||
class="my-2 cursor-pointer"
|
||||
src="${process.env.REACT_APP_CDN_URL}${imageURL}" />`).join('')
|
||||
}`;
|
||||
((message.content) ? format(striptags(message.content)) : '') +
|
||||
((message.updatedAt && message.content) ?
|
||||
`<span
|
||||
class="select-none muted edited text-xs ml-1"
|
||||
title="${message.updatedAt}">(edited)</span>` : '');
|
||||
|
||||
const Attachments = () => (
|
||||
<>
|
||||
{message.attachmentURLs?.map(imageURL =>
|
||||
<img
|
||||
key={imageURL}
|
||||
style={{ maxWidth: '512px' }}
|
||||
className="my-2 cursor-pointer"
|
||||
onClick={() => dispatch(previewImage(imageURL))}
|
||||
src={process.env.REACT_APP_CDN_URL + imageURL} />)}
|
||||
</>
|
||||
);
|
||||
|
||||
return (editingMessageId === message.id)
|
||||
? <MessageBox
|
||||
@ -66,9 +78,9 @@ const MessageContent: FunctionComponent<MessageContentProps> = ({ message }) =>
|
||||
className="normal whitespace-pre-wrap">
|
||||
<div
|
||||
dangerouslySetInnerHTML={{ __html: messageHTML }}
|
||||
className="float-left overflow-auto"
|
||||
className="overflow-auto"
|
||||
style={{ maxWidth: '100%' }} />
|
||||
{message.updatedAt && <span className="select-none muted edited text-xs ml-1">(edited)</span>}
|
||||
<Attachments />
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
15
frontend/src/components/modals/image-preview.tsx
Normal file
15
frontend/src/components/modals/image-preview.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
import Modal from './modal';
|
||||
|
||||
const CreateInvite: React.FunctionComponent = () => {
|
||||
const resource = useSelector((s: Store.AppState) => s.ui.activeResource);
|
||||
|
||||
return (resource) ? (
|
||||
<Modal typeName={'ImagePreview'} className="p-5">
|
||||
<img src={resource} />
|
||||
<a className="-mt-2" href={resource} target="_blank">View Original</a>
|
||||
</Modal>
|
||||
) : null;
|
||||
}
|
||||
|
||||
export default CreateInvite;
|
@ -28,7 +28,7 @@ const Modal: React.FunctionComponent<ModalProps> = ({ className, typeName, size,
|
||||
className={classNames(
|
||||
`bg-bg-primary overflow-auto fixed outline-none`,
|
||||
className,
|
||||
sizeClass[size ?? 'sm']
|
||||
sizeClass[size ?? 'sm'],
|
||||
)}
|
||||
appElement={document.querySelector('#root')!}
|
||||
isOpen={openModal === typeName}
|
||||
|
@ -13,6 +13,9 @@ const slice = createSlice({
|
||||
delete state.editingMessageId;
|
||||
},
|
||||
// only 1 invite is created -> to save data, and stop spam
|
||||
focusedResource: (state, { payload }) => {
|
||||
state.activeResource = payload;
|
||||
},
|
||||
focusedInvite: (state, { payload }) => {
|
||||
state.activeInvite = payload;
|
||||
},
|
||||
@ -66,6 +69,10 @@ export const openUserProfile = (user: Entity.User) => (dispatch) => {
|
||||
dispatch(actions.focusedUser(user));
|
||||
dispatch(actions.openedModal('UserProfile'));
|
||||
}
|
||||
export const previewImage = (url: string) => (dispatch) => {
|
||||
dispatch(actions.focusedResource(url));
|
||||
dispatch(actions.openedModal('ImagePreview'));
|
||||
}
|
||||
|
||||
export const openDialog = (dialog: Dialog) => () => events.emit('dialog', dialog);
|
||||
|
||||
|
1
types/store.d.ts
vendored
1
types/store.d.ts
vendored
@ -39,6 +39,7 @@ declare namespace Store {
|
||||
openDropdown?: string;
|
||||
openModal?: string;
|
||||
activeChannel?: Entity.Channel;
|
||||
activeResource?: string;
|
||||
activeGuild?: Entity.Guild;
|
||||
activeInvite?: Entity.Invite;
|
||||
activeUser?: Entity.User;
|
||||
|
Loading…
x
Reference in New Issue
Block a user