1 import { Component, linkEvent } from 'inferno';
2 import { Link } from 'inferno-router';
4 PrivateMessage as PrivateMessageI,
5 DeletePrivateMessageForm,
6 MarkPrivateMessageAsReadForm,
7 } from 'lemmy-js-client';
8 import { WebSocketService, UserService } from '../services';
9 import { mdToHtml, pictrsAvatarThumbnail, showAvatars, toast } from '../utils';
10 import { MomentTime } from './moment-time';
11 import { PrivateMessageForm } from './private-message-form';
12 import { UserListing, UserOther } from './user-listing';
13 import { i18n } from '../i18next';
15 interface PrivateMessageState {
22 interface PrivateMessageProps {
23 privateMessage: PrivateMessageI;
26 export class PrivateMessage extends Component<
30 private emptyState: PrivateMessageState = {
37 constructor(props: any, context: any) {
38 super(props, context);
40 this.state = this.emptyState;
41 this.handleReplyCancel = this.handleReplyCancel.bind(this);
42 this.handlePrivateMessageCreate = this.handlePrivateMessageCreate.bind(
45 this.handlePrivateMessageEdit = this.handlePrivateMessageEdit.bind(this);
50 UserService.Instance.user &&
51 UserService.Instance.user.id == this.props.privateMessage.creator_id
56 let message = this.props.privateMessage;
57 let userOther: UserOther = this.mine
59 name: message.recipient_name,
60 preferred_username: message.recipient_preferred_username,
62 avatar: message.recipient_avatar,
63 local: message.recipient_local,
64 actor_id: message.recipient_actor_id,
65 published: message.published,
68 name: message.creator_name,
69 preferred_username: message.creator_preferred_username,
71 avatar: message.creator_avatar,
72 local: message.creator_local,
73 actor_id: message.creator_actor_id,
74 published: message.published,
78 <div class="border-top border-light">
80 <ul class="list-inline mb-0 text-muted small">
81 {/* TODO refactor this */}
82 <li className="list-inline-item">
83 {this.mine ? i18n.t('to') : i18n.t('from')}
85 <li className="list-inline-item">
86 <UserListing user={userOther} />
88 <li className="list-inline-item">
90 <MomentTime data={message} />
93 <li className="list-inline-item">
95 className="pointer text-monospace"
96 onClick={linkEvent(this, this.handleMessageCollapse)}
98 {this.state.collapsed ? (
99 <svg class="icon icon-inline">
100 <use xlinkHref="#icon-plus-square"></use>
103 <svg class="icon icon-inline">
104 <use xlinkHref="#icon-minus-square"></use>
110 {this.state.showEdit && (
112 privateMessage={message}
113 onEdit={this.handlePrivateMessageEdit}
114 onCreate={this.handlePrivateMessageCreate}
115 onCancel={this.handleReplyCancel}
118 {!this.state.showEdit && !this.state.collapsed && (
120 {this.state.viewSource ? (
121 <pre>{this.messageUnlessRemoved}</pre>
125 dangerouslySetInnerHTML={mdToHtml(this.messageUnlessRemoved)}
128 <ul class="list-inline mb-0 text-muted font-weight-bold">
131 <li className="list-inline-item">
133 class="btn btn-link btn-animate text-muted"
134 onClick={linkEvent(this, this.handleMarkRead)}
137 ? i18n.t('mark_as_unread')
138 : i18n.t('mark_as_read')
142 class={`icon icon-inline ${
143 message.read && 'text-success'
146 <use xlinkHref="#icon-check"></use>
150 <li className="list-inline-item">
152 class="btn btn-link btn-animate text-muted"
153 onClick={linkEvent(this, this.handleReplyClick)}
154 data-tippy-content={i18n.t('reply')}
156 <svg class="icon icon-inline">
157 <use xlinkHref="#icon-reply1"></use>
165 <li className="list-inline-item">
167 class="btn btn-link btn-animate text-muted"
168 onClick={linkEvent(this, this.handleEditClick)}
169 data-tippy-content={i18n.t('edit')}
171 <svg class="icon icon-inline">
172 <use xlinkHref="#icon-edit"></use>
176 <li className="list-inline-item">
178 class="btn btn-link btn-animate text-muted"
179 onClick={linkEvent(this, this.handleDeleteClick)}
187 class={`icon icon-inline ${
188 message.deleted && 'text-danger'
191 <use xlinkHref="#icon-trash"></use>
197 <li className="list-inline-item">
199 class="btn btn-link btn-animate text-muted"
200 onClick={linkEvent(this, this.handleViewSource)}
201 data-tippy-content={i18n.t('view_source')}
204 class={`icon icon-inline ${
205 this.state.viewSource && 'text-success'
208 <use xlinkHref="#icon-file-text"></use>
216 {this.state.showReply && (
219 recipient_id: this.props.privateMessage.creator_id,
221 onCreate={this.handlePrivateMessageCreate}
224 {/* A collapsed clearfix */}
225 {this.state.collapsed && <div class="row col-12"></div>}
230 get messageUnlessRemoved(): string {
231 let message = this.props.privateMessage;
232 return message.deleted ? `*${i18n.t('deleted')}*` : message.content;
235 handleReplyClick(i: PrivateMessage) {
236 i.state.showReply = true;
240 handleEditClick(i: PrivateMessage) {
241 i.state.showEdit = true;
245 handleDeleteClick(i: PrivateMessage) {
246 let form: DeletePrivateMessageForm = {
247 edit_id: i.props.privateMessage.id,
248 deleted: !i.props.privateMessage.deleted,
250 WebSocketService.Instance.deletePrivateMessage(form);
253 handleReplyCancel() {
254 this.state.showReply = false;
255 this.state.showEdit = false;
256 this.setState(this.state);
259 handleMarkRead(i: PrivateMessage) {
260 let form: MarkPrivateMessageAsReadForm = {
261 edit_id: i.props.privateMessage.id,
262 read: !i.props.privateMessage.read,
264 WebSocketService.Instance.markPrivateMessageAsRead(form);
267 handleMessageCollapse(i: PrivateMessage) {
268 i.state.collapsed = !i.state.collapsed;
272 handleViewSource(i: PrivateMessage) {
273 i.state.viewSource = !i.state.viewSource;
277 handlePrivateMessageEdit() {
278 this.state.showEdit = false;
279 this.setState(this.state);
282 handlePrivateMessageCreate(message: PrivateMessageI) {
284 UserService.Instance.user &&
285 message.creator_id == UserService.Instance.user.id
287 this.state.showReply = false;
288 this.setState(this.state);
289 toast(i18n.t('message_sent'));