Skip to content

Commit d4afee6

Browse files
committed
Edit messages on desktop
1 parent 4a4ff32 commit d4afee6

20 files changed

+210
-32
lines changed

src/Client/Im/Chat.purs

+58-19
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Shared.Im.Types
66

77
import Client.Common.Dom as CCD
88
import Client.Common.File as CCF
9-
import Client.Im.Flame (NextMessage, NoMessages, MoreMessages)
9+
import Client.Im.Flame (MoreMessages, NextMessage, NoMessages)
1010
import Client.Im.Record as CIR
1111
import Client.Im.Scroll as CIS
1212
import Client.Im.WebSocket as CIW
@@ -48,6 +48,7 @@ import Shared.Markdown as SM
4848
import Shared.Resource (maxImageSize)
4949
import Shared.Unsafe ((!@))
5050
import Shared.Unsafe as SU
51+
import Test.Client.Model (model)
5152
import Type.Proxy (Proxy(..))
5253
import Web.DOM (Element)
5354
import Web.DOM.Element as WDE
@@ -58,6 +59,7 @@ import Web.HTML.Event.DataTransfer as WHEDT
5859
import Web.HTML.Event.DragEvent as WHED
5960
import Web.HTML.HTMLElement as WHHE
6061
import Web.HTML.HTMLElement as WHHEL
62+
import Web.HTML.HTMLMetaElement (content)
6163
import Web.HTML.HTMLTextAreaElement as WHHTA
6264
import Web.Socket.WebSocket (WebSocket)
6365

@@ -134,7 +136,7 @@ beforeSendMessage
134136
sendMessage WebSocket MessageContent DateTimeWrapper ImModel NoMessages
135137
sendMessage
136138
webSocket
137-
content
139+
contentMessage
138140
date
139141
model@
140142
{ user
@@ -148,37 +150,63 @@ sendMessage
148150
WHHEL.focus <<< SU.fromJust $ WHHEL.fromElement input
149151
CCD.setValue input ""
150152
resizeTextarea input
151-
CIW.sendPayload webSocket $ OutgoingMessage
152-
{ id: newTemporaryId
153-
, userId: recipientId
154-
, content
155-
, turn
156-
}
153+
CIW.sendPayload webSocket $
154+
case model.editing of
155+
Nothing
156+
OutgoingMessage
157+
{ id: newTemporaryId
158+
, userId: recipientId
159+
, content: contentMessage
160+
, turn
161+
}
162+
Just messageId →
163+
EditedMessage
164+
{ id: messageId
165+
, userId: recipientId
166+
, content: contentMessage
167+
}
157168
pure Nothing
158169
]
159170
where
160171
index = SU.fromJust chatting
161172
recipient@{ user: { id: recipientId }, history } = contacts !@ index
162173
newTemporaryId = temporaryId + 1
163174

175+
content =
176+
case contentMessage of
177+
Text message → message
178+
Image caption base64File → asMarkdownImage caption base64File
179+
Audio base64 → asAudioMessage base64
180+
181+
updateEdited messageId history
182+
| history.id == messageId =
183+
history
184+
{ content = content
185+
, status = Sent
186+
}
187+
| otherwise = history
188+
164189
updatedContact = recipient
165190
{ lastMessageDate = date
166-
, history = DA.snoc history $
167-
{ id: newTemporaryId
168-
, status: Sent
169-
, sender: user.id
170-
, recipient: recipientId
171-
, date
172-
, content: case content of
173-
Text message → message
174-
Image caption base64File → asMarkdownImage caption base64File
175-
Audio base64 → asAudioMessage base64
176-
}
191+
, history =
192+
case model.editing of
193+
Nothing
194+
DA.snoc history $
195+
{ id: newTemporaryId
196+
, status: Sent
197+
, sender: user.id
198+
, edited: false
199+
, recipient: recipientId
200+
, date
201+
, content
202+
}
203+
Just messageId → map (updateEdited messageId) history
177204
}
178205
updatedModel = model
179206
{ temporaryId = newTemporaryId
180207
, imageCaption = Nothing
181208
, selectedImage = Nothing
209+
, editing = Nothing
182210
, contacts = SU.fromJust $ DA.updateAt index updatedContact contacts
183211
}
184212
turn = makeTurn user updatedContact
@@ -500,3 +528,14 @@ updateTyping userId status model@{ contacts } = model { contacts = upd <$> conta
500528
upd contact@{ user: { id } }
501529
| id == userId = contact { typing = status }
502530
| otherwise = contact
531+
532+
editMessage String Int ImModel NoMessages
533+
editMessage message id model =
534+
model
535+
{ editing = Just id
536+
, toggleContextMenu = HideContextMenu
537+
} /\ [ setIt *> pure Nothing ]
538+
where
539+
setIt = liftEffect do
540+
input ← chatInput model.chatting
541+
CCD.setValue input message

src/Client/Im/Contacts.purs

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ resumeChat userId model =
4444
, toggleChatModal = HideChatModal
4545
, initialScreen = false
4646
, selectedImage = Nothing
47+
, editing = Nothing
4748
, failedRequests = []
4849
} /\
4950
( smallScreenEffect <>

src/Client/Im/Main.purs

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ update st model =
145145
FocusCurrentSuggestionCIC.focusCurrentSuggestion model
146146
FocusInput elementId → focusInput elementId model
147147
QuoteMessage message et → CIC.quoteMessage message et model
148+
EditMessage message id → CIC.editMessage message id model
148149
CheckTyping text → CIC.checkTyping text (EU.unsafePerformEffect EN.nowDateTime) webSocket model
149150
NoTyping id → F.noMessages $ CIC.updateTyping id false model
150151
TypingId id → F.noMessages model { typingIds = DA.snoc model.typingIds $ SC.coerce id }

src/Client/Im/Suggestion.purs

+1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ resumeSuggesting model@{ suggestions, suggesting } = F.noMessages $ model
129129
{ chatting = Nothing
130130
, suggesting = if DA.length suggestions <= 1 then Just 0 else suggesting
131131
, toggleChatModal = HideChatModal
132+
, editing = Nothing
132133
}
133134

134135
toggleSuggestionsFromOnline ImModel MoreMessages

src/Client/Im/WebSocket/Events.purs

+26-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import Server.WebSocket (WebSocketConnection)
3838
import Shared.Availability (Availability(..))
3939
import Shared.DateTime (DateTimeWrapper(..))
4040
import Shared.Experiments.Types as SET
41-
import Shared.Im.Types (ClientMessagePayload, Contact, FullWebSocketPayloadClient(..), HistoryMessage, ImMessage(..), MessageStatus(..), RetryableRequest(..), TimeoutIdWrapper(..), WebSocketPayloadClient(..), WebSocketPayloadServer(..), ImModel)
41+
import Shared.Im.Types (ClientMessagePayload, Contact, FullWebSocketPayloadClient(..), HistoryMessage, ImMessage(..), MessageStatus(..), RetryableRequest(..), TimeoutIdWrapper(..), WebSocketPayloadClient(..), WebSocketPayloadServer(..), ImModel, EditedMessagePayload)
4242
import Shared.Json as SJ
4343
import Shared.Options.MountPoint (experimentsId, imId, profileId)
4444
import Shared.Profile.Types as SPT
@@ -169,6 +169,7 @@ receiveMessage webSocket isFocused payload model = case payload of
169169
ServerChangedStatus cs → receiveStatusChange cs model
170170
ServerReceivedMessage rm → receiveAcknowledgement rm model
171171
NewIncomingMessage ni → receiveIncomingMessage webSocket isFocused ni model
172+
NewEditedMessage ni → receiveEditedMessage webSocket isFocused ni model
172173
ContactTyping tp → receiveTyping tp model
173174
CurrentPrivileges kp → receivePrivileges kp model
174175
CurrentHash newHash → receiveHash newHash model
@@ -230,6 +231,29 @@ receiveIncomingMessage webSocket isFocused payload model =
230231

231232
withExtraMessage e (m /\ ms) = m /\ e : ms
232233

234+
receiveEditedMessage WebSocket Boolean EditedMessagePayload ImModel NextMessage
235+
receiveEditedMessage webSocket isFocused payload model =
236+
if DA.elem userId model.blockedUsers then
237+
F.noMessages model
238+
else if model.user.id == payload.senderId then
239+
updatedModel /\ [ liftEffect (CIUC.updateTabCount model.user.id updatedModel.contacts) *> pure Nothing ]
240+
else if isFocused && DM.isJust model.chatting && (updatedModel.contacts !@ (SU.fromJust updatedModel.chatting)).user.id == userId then
241+
CICN.setMessageStatus webSocket (SU.fromJust model.chatting) Read updatedModel
242+
else
243+
CICN.setMessageStatus webSocket (SU.fromJust $ DA.findIndex (findContact userId) updatedModel.contacts) Delivered updatedModel
244+
where
245+
updateContent history
246+
| history.id == payload.id = history { content = payload.content, edited = true }
247+
| otherwise = history
248+
249+
updatedModel = model { contacts = updateHistory model.contacts userId updateContent }
250+
251+
userId
252+
| payload.recipientId == model.user.id = payload.senderId
253+
| otherwise = payload.recipientId
254+
255+
withExtraMessage e (m /\ ms) = m /\ e : ms
256+
233257
-- | Set typing status and a timeout to clear it
234258
receiveTyping { id Int } ImModel MoreMessages
235259
receiveTyping received model = CIC.updateTyping received.id true model /\
@@ -350,6 +374,7 @@ fromIncomingMessage payload model = case updatedContacts of
350374
, sender: payload.senderId
351375
, recipient: payload.recipientId
352376
, id: payload.id
377+
, edited: false
353378
, content: payload.content
354379
, date: payload.date
355380
}

src/Client/css/im.css

+7-2
Original file line numberDiff line numberDiff line change
@@ -1711,6 +1711,10 @@ select {
17111711
align-items: center;
17121712
}
17131713

1714+
.editing-message {
1715+
background-color: #469bc5 !important;
1716+
}
1717+
17141718
.card-top-header {
17151719
color: #C4D367;
17161720
text-align: center;
@@ -1800,7 +1804,7 @@ select {
18001804
}
18011805

18021806
.confirmation.large {
1803-
top:30%;
1807+
top: 30%;
18041808
max-height: 80%;
18051809
height: fit-content;
18061810
align-items: center;
@@ -2779,6 +2783,7 @@ select {
27792783
}
27802784

27812785
@media (min-width:1920px) {
2786+
27822787
html,
27832788
body {
27842789
font-size: 18px;
@@ -2815,7 +2820,7 @@ select {
28152820
min-width: 70%;
28162821
min-height: 70%;
28172822
height: auto;
2818-
max-height: auto ;
2823+
max-height: auto;
28192824
}
28202825

28212826
.large-avatar-profile {

src/Server/Database/Messages.purs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
module Server.Database.Messages where
22

33
import Droplet.Language
4+
import Prim hiding (Constraint)
45
import Shared.DateTime
56
import Shared.Im.Types
6-
import Prim hiding (Constraint)
7+
78
import Data.DateTime (DateTime)
89
import Data.Maybe (Maybe(..))
910
import Data.Tuple.Nested (type (/\))
11+
import Server.Database.Types (Checked(..))
1012
import Server.Database.Users (UsersTable)
1113
import Type.Proxy (Proxy(..))
1214

@@ -16,6 +18,7 @@ type Messages =
1618
, recipientColumn Int (Constraint "to_user_message" (ForeignKey "id" UsersTable))
1719
, dateColumn DateTimeWrapper Default
1820
, contentString
21+
, editedColumn Checked Default
1922
, statusColumn MessageStatus Default
2023
, visualizedMaybe DateTime
2124
)
@@ -31,3 +34,7 @@ _status = Proxy
3134

3235
_visualized Proxy "visualized"
3336
_visualized = Proxy
37+
38+
_edited Proxy "edited"
39+
_edited = Proxy
40+

src/Server/Im/Action.purs

+15
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,21 @@ processMessage loggedUserId userId content = do
9494
else
9595
pure $ Left UserUnavailable
9696

97+
editMessage r. Int Int Int MessageContent BaseEffect { configuration Configuration, pool Pool | r } (Either MessageError String)
98+
editMessage loggedUserId userId messageId content = do
99+
isVisible ← SID.isRecipientVisible loggedUserId userId
100+
canEdit ← if isVisible then SID.canEditMessage loggedUserId messageId else pure false
101+
if canEdit then do
102+
privileges ← markdownPrivileges loggedUserId
103+
sanitized ← processMessageContent content privileges
104+
if DS.null sanitized then
105+
pure $ Left InvalidMessage
106+
else do
107+
id ← SID.updateMessage messageId sanitized
108+
pure $ Right sanitized
109+
else
110+
pure $ Left UserUnavailable
111+
97112
markdownPrivileges r. Int BaseEffect { pool Pool | r } (Set Privilege)
98113
markdownPrivileges loggedUserId = (DST.fromFoldable <<< map _.feature) <$> SID.markdownPrivileges loggedUserId
99114

src/Server/Im/Database.purs

+12-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import Server.Database.KarmaLeaderboard (_current_karma, _karma, _karmaPosition,
2929
import Server.Database.Languages (_languages, languages)
3030
import Server.Database.LanguagesUsers (_language, _speaker, languages_users)
3131
import Server.Database.LastSeen (_who, last_seen)
32-
import Server.Database.Messages (_content, _status, messages)
32+
import Server.Database.Messages (_content, _status, messages, _edited)
3333
import Server.Database.Privileges (_feature, _privileges, _quantity, privileges)
3434
import Server.Database.Reports (_comment, _reason, _reported, _reporter, reports)
3535
import Server.Database.Tags (_tags, tags)
@@ -161,6 +161,7 @@ presentMessageFields =
161161
, s.sender
162162
, s.recipient
163163
, s.date
164+
, s.edited
164165
, s.content
165166
, s.status """
166167

@@ -269,11 +270,21 @@ isRecipientVisible loggedUserId userId =
269270
(u ... _visibility .=. Everyone .||. u ... _visibility .=. NoTemporaryUsers .&&. exists (select (3 # as c) # from users # wher (_id .=. loggedUserId .&&. _temporary .=. Checked false)) .||. u ... _visibility .=. Contacts .&&. (isNotNull _first_message_date .&&. _visibility_last_updated .>=. _first_message_date))
270271
)
271272

273+
canEditMessage r. Int Int BaseEffect { pool Pool | r } Boolean
274+
canEditMessage loggedUserId messageId =
275+
map DM.isJust <<< SD.single $
276+
select (1 # as c)
277+
# from messages
278+
# wher (_id .=. messageId .&&. _sender .=. loggedUserId)
279+
272280
insertMessage r. Int Int String BaseEffect { pool Pool | r } Int
273281
insertMessage loggedUserId recipient content = SD.withTransaction $ \connection → do
274282
void $ SD.singleWith connection $ select (insert_history (loggedUserId /\ recipient) # as u)
275283
_.id <<< SU.fromJust <$> (SD.singleWith connection $ insert # into messages (_sender /\ _recipient /\ _content) # values (loggedUserId /\ recipient /\ content) # returning _id)
276284

285+
updateMessage r. Int String BaseEffect { pool Pool | r } Unit
286+
updateMessage messageId content = void <<< SD.execute $ update messages # set ((_content .=. content) /\ (_edited .=. Checked true)) # wher (_id .=. messageId)
287+
277288
insertKarma r. Int Int Tuple Int Int BaseEffect { pool Pool | r } Unit
278289
insertKarma loggedUserId userId (Tuple senderKarma recipientKarma)
279290
| senderKarma <= 0 && recipientKarma <= 0 = pure unit

src/Server/Im/Database/Flat.purs

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ fromFlatMessage fm =
105105
{ id: fm.messageId
106106
, sender: fm.sender
107107
, recipient: fm.recipient
108+
, edited: fm.edited
108109
, date: fm.date
109110
, content: fm.content
110111
, status: fm.status

src/Server/Im/Template.purs

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ template payload = do
5353
, temporaryPassword: Nothing
5454
, enableNotificationsVisible: false
5555
, messageEnter: true
56+
, editing: Nothing
5657
, imageCaption: Nothing
5758
, link: Nothing
5859
, linkText: Nothing

0 commit comments

Comments
 (0)