@@ -253,7 +253,10 @@ class MessageComposerPresenter @Inject constructor(
253253 )
254254
255255 LaunchedEffect (Unit ) {
256- loadDraft(markdownTextEditorState, richTextEditorState)
256+ val draft = draftService.loadDraft(room.roomId, isVolatile = false )
257+ if (draft != null ) {
258+ applyDraft(draft, markdownTextEditorState, richTextEditorState)
259+ }
257260 }
258261
259262 val mentionSpanProvider = LocalMentionSpanProvider .current
@@ -264,26 +267,16 @@ class MessageComposerPresenter @Inject constructor(
264267 MessageComposerEvents .CloseSpecialMode -> {
265268 if (messageComposerContext.composerMode is MessageComposerMode .Edit ) {
266269 localCoroutineScope.launch {
267- textEditorState.reset( )
270+ resetComposer(markdownTextEditorState, richTextEditorState, fromEdit = true )
268271 }
272+ } else {
273+ messageComposerContext.composerMode = MessageComposerMode .Normal
269274 }
270- messageComposerContext.composerMode = MessageComposerMode .Normal
271275 }
272276 is MessageComposerEvents .SendMessage -> {
273- val html = if (showTextFormatting) {
274- richTextEditorState.messageHtml
275- } else {
276- null
277- }
278- val markdown = if (showTextFormatting) {
279- richTextEditorState.messageMarkdown
280- } else {
281- markdownTextEditorState.getMessageMarkdown(permalinkBuilder)
282- }
283277 appCoroutineScope.sendMessage(
284- message = Message (html = html, markdown = markdown),
285- updateComposerMode = { messageComposerContext.composerMode = it },
286- textEditorState = textEditorState,
278+ markdownTextEditorState = markdownTextEditorState,
279+ richTextEditorState = richTextEditorState,
287280 )
288281 }
289282 is MessageComposerEvents .SendUri -> appCoroutineScope.sendAttachment(
@@ -386,7 +379,8 @@ class MessageComposerPresenter @Inject constructor(
386379 }
387380 }
388381 MessageComposerEvents .SaveDraft -> {
389- appCoroutineScope.saveDraft(textEditorState)
382+ val draft = createDraftFromState(markdownTextEditorState, richTextEditorState)
383+ appCoroutineScope.updateDraft(draft, isVolatile = false )
390384 }
391385 }
392386 }
@@ -407,42 +401,26 @@ class MessageComposerPresenter @Inject constructor(
407401 }
408402
409403 private fun CoroutineScope.sendMessage (
410- message : Message ,
411- updateComposerMode : (newComposerMode: MessageComposerMode ) -> Unit ,
412- textEditorState : TextEditorState ,
404+ markdownTextEditorState : MarkdownTextEditorState ,
405+ richTextEditorState : RichTextEditorState ,
413406 ) = launch {
407+ val message = currentComposerMessage(markdownTextEditorState, richTextEditorState, withMentions = true )
414408 val capturedMode = messageComposerContext.composerMode
415- val mentions = when (textEditorState) {
416- is TextEditorState .Rich -> {
417- textEditorState.richTextEditorState.mentionsState?.let { state ->
418- buildList {
419- if (state.hasAtRoomMention) {
420- add(Mention .AtRoom )
421- }
422- for (userId in state.userIds) {
423- add(Mention .User (UserId (userId)))
424- }
425- }
426- }.orEmpty()
427- }
428- is TextEditorState .Markdown -> textEditorState.state.getMentions()
429- }
430409 // Reset composer right away
431- textEditorState.reset()
432- updateComposerMode(MessageComposerMode .Normal )
410+ resetComposer(markdownTextEditorState, richTextEditorState, fromEdit = capturedMode is MessageComposerMode .Edit )
433411 when (capturedMode) {
434- is MessageComposerMode .Normal -> room.sendMessage(body = message.markdown, htmlBody = message.html, mentions = mentions)
412+ is MessageComposerMode .Normal -> room.sendMessage(body = message.markdown, htmlBody = message.html, mentions = message. mentions)
435413 is MessageComposerMode .Edit -> {
436414 val eventId = capturedMode.eventId
437415 val transactionId = capturedMode.transactionId
438416 timelineController.invokeOnCurrentTimeline {
439- editMessage(eventId, transactionId, message.markdown, message.html, mentions)
417+ editMessage(eventId, transactionId, message.markdown, message.html, message. mentions)
440418 }
441419 }
442420
443421 is MessageComposerMode .Reply -> {
444422 timelineController.invokeOnCurrentTimeline {
445- replyMessage(capturedMode.eventId, message.markdown, message.html, mentions)
423+ replyMessage(capturedMode.eventId, message.markdown, message.html, message. mentions)
446424 }
447425 }
448426 }
@@ -537,21 +515,30 @@ class MessageComposerPresenter @Inject constructor(
537515 }
538516 }
539517
540- private fun CoroutineScope.loadDraft (
518+ private fun CoroutineScope.updateDraft (
519+ draft : ComposerDraft ? ,
520+ isVolatile : Boolean ,
521+ ) = launch {
522+ draftService.updateDraft(
523+ roomId = room.roomId,
524+ draft = draft,
525+ isVolatile = isVolatile
526+ )
527+ }
528+
529+ private suspend fun applyDraft (
530+ draft : ComposerDraft ,
541531 markdownTextEditorState : MarkdownTextEditorState ,
542532 richTextEditorState : RichTextEditorState ,
543- ) = launch {
544- val draft = draftService.loadDraft(room.roomId) ? : return @launch
533+ ) {
545534 val htmlText = draft.htmlText
546535 val markdownText = draft.plainText
547536 if (htmlText != null ) {
548537 showTextFormatting = true
549- richTextEditorState.setHtml(htmlText)
550- richTextEditorState.requestFocus()
538+ setText(htmlText, markdownTextEditorState, richTextEditorState, requestFocus = true )
551539 } else {
552540 showTextFormatting = false
553- markdownTextEditorState.text.update(markdownText, true )
554- markdownTextEditorState.requestFocusAction()
541+ setText(markdownText, markdownTextEditorState, richTextEditorState, requestFocus = true )
555542 }
556543 when (val draftType = draft.draftType) {
557544 ComposerDraftType .NewMessage -> messageComposerContext.composerMode = MessageComposerMode .Normal
@@ -570,34 +557,66 @@ class MessageComposerPresenter @Inject constructor(
570557 }
571558 }
572559
573- private fun CoroutineScope. saveDraft (
574- textEditorState : TextEditorState ,
575- ) = launch {
576- val html = textEditorState.messageHtml()
577- val markdown = textEditorState.messageMarkdown(permalinkBuilder )
560+ private fun createDraftFromState (
561+ markdownTextEditorState : MarkdownTextEditorState ,
562+ richTextEditorState : RichTextEditorState ,
563+ ): ComposerDraft ? {
564+ val message = currentComposerMessage(markdownTextEditorState, richTextEditorState, withMentions = false )
578565 val draftType = when (val mode = messageComposerContext.composerMode) {
579566 is MessageComposerMode .Normal -> ComposerDraftType .NewMessage
580567 is MessageComposerMode .Edit -> {
581568 mode.eventId?.let { eventId -> ComposerDraftType .Edit (eventId) }
582569 }
583570 is MessageComposerMode .Reply -> ComposerDraftType .Reply (mode.eventId)
584571 }
585- val composerDraft = if (draftType == null || markdown.isBlank()) {
572+ return if (draftType == null || message. markdown.isBlank()) {
586573 null
587574 } else {
588575 ComposerDraft (
589576 draftType = draftType,
590- htmlText = html,
591- plainText = markdown,
577+ htmlText = message. html,
578+ plainText = message. markdown,
592579 )
593580 }
594- draftService.updateDraft(room.roomId, composerDraft)
581+ }
582+
583+ private fun currentComposerMessage (
584+ markdownTextEditorState : MarkdownTextEditorState ,
585+ richTextEditorState : RichTextEditorState ,
586+ withMentions : Boolean ,
587+ ): Message {
588+ return if (showTextFormatting) {
589+ val html = richTextEditorState.messageHtml
590+ val markdown = richTextEditorState.messageMarkdown
591+ val mentions = richTextEditorState.mentionsState
592+ .takeIf { withMentions }
593+ ?.let { state ->
594+ buildList {
595+ if (state.hasAtRoomMention) {
596+ add(Mention .AtRoom )
597+ }
598+ for (userId in state.userIds) {
599+ add(Mention .User (UserId (userId)))
600+ }
601+ }
602+ }
603+ .orEmpty()
604+ Message (html = html, markdown = markdown, mentions = mentions)
605+ } else {
606+ val markdown = markdownTextEditorState.getMessageMarkdown(permalinkBuilder)
607+ val mentions = if (withMentions) {
608+ markdownTextEditorState.getMentions()
609+ } else {
610+ emptyList()
611+ }
612+ Message (html = null , markdown = markdown, mentions = mentions)
613+ }
595614 }
596615
597616 private fun CoroutineScope.toggleTextFormatting (
598617 enabled : Boolean ,
599618 markdownTextEditorState : MarkdownTextEditorState ,
600- richTextEditorState : RichTextEditorState ,
619+ richTextEditorState : RichTextEditorState
601620 ) = launch {
602621 showTextFormatting = enabled
603622 if (showTextFormatting) {
@@ -615,24 +634,63 @@ class MessageComposerPresenter @Inject constructor(
615634 }
616635
617636 private fun CoroutineScope.setMode (
618- composerMode : MessageComposerMode ,
637+ newComposerMode : MessageComposerMode ,
619638 markdownTextEditorState : MarkdownTextEditorState ,
620- richTextEditorState : RichTextEditorState
639+ richTextEditorState : RichTextEditorState ,
621640 ) = launch {
622- messageComposerContext.composerMode = composerMode
623- when (composerMode ) {
641+ val currentComposerMode = messageComposerContext. composerMode
642+ when (newComposerMode ) {
624643 is MessageComposerMode .Edit -> {
625- setText(composerMode.content, markdownTextEditorState, richTextEditorState)
644+ if (currentComposerMode !is MessageComposerMode .Edit ) {
645+ val draft = createDraftFromState(markdownTextEditorState, richTextEditorState)
646+ updateDraft(draft, isVolatile = true ).join()
647+ }
648+ setText(newComposerMode.content, markdownTextEditorState, richTextEditorState)
649+ }
650+ else -> {
651+ // When coming from edit, just clear the composer as it'd be weird to reset a volatile draft in this scenario.
652+ if (currentComposerMode is MessageComposerMode .Edit ) {
653+ setText(" " , markdownTextEditorState, richTextEditorState)
654+ }
626655 }
627- else -> Unit
628656 }
657+ messageComposerContext.composerMode = newComposerMode
629658 }
630659
631- private suspend fun setText (content : String , markdownTextEditorState : MarkdownTextEditorState , richTextEditorState : RichTextEditorState ) {
660+ private suspend fun resetComposer (
661+ markdownTextEditorState : MarkdownTextEditorState ,
662+ richTextEditorState : RichTextEditorState ,
663+ fromEdit : Boolean ,
664+ ) {
665+ // Use the volatile draft only when coming from edit mode otherwise.
666+ val draft = draftService.loadDraft(room.roomId, isVolatile = true ).takeIf { fromEdit }
667+ if (draft != null ) {
668+ applyDraft(draft, markdownTextEditorState, richTextEditorState)
669+ } else {
670+ setText(" " , markdownTextEditorState, richTextEditorState)
671+ messageComposerContext.composerMode = MessageComposerMode .Normal
672+ }
673+ }
674+
675+ private suspend fun setText (
676+ content : String ,
677+ markdownTextEditorState : MarkdownTextEditorState ,
678+ richTextEditorState : RichTextEditorState ,
679+ requestFocus : Boolean = false,
680+ ) {
632681 if (showTextFormatting) {
633682 richTextEditorState.setHtml(content)
683+ if (requestFocus) {
684+ richTextEditorState.requestFocus()
685+ }
634686 } else {
687+ if (content.isEmpty()) {
688+ markdownTextEditorState.selection = IntRange .EMPTY
689+ }
635690 markdownTextEditorState.text.update(content, true )
691+ if (requestFocus) {
692+ markdownTextEditorState.requestFocusAction()
693+ }
636694 }
637695 }
638696}
0 commit comments