2
2
//
3
3
// SPDX-License-Identifier: AGPL-3.0-or-later
4
4
5
- use anyhow:: Context ;
5
+ use anyhow:: { bail , Context } ;
6
6
use openmls:: storage:: OpenMlsProvider ;
7
7
use phnxtypes:: {
8
8
identifiers:: QualifiedUserName , messages:: client_ds_out:: SendMessageParamsOut , time:: TimeStamp ,
9
9
} ;
10
10
use rusqlite:: { Connection , Transaction } ;
11
+ use uuid:: Uuid ;
11
12
12
- use crate :: { Conversation , ConversationId , ConversationMessage , MimiContent } ;
13
+ use crate :: { Conversation , ConversationId , ConversationMessage , Message , MimiContent } ;
13
14
14
15
use super :: { ApiClients , CoreUser , Group , PhnxOpenMlsProvider , StoreNotifier } ;
15
16
@@ -25,7 +26,7 @@ impl CoreUser {
25
26
) -> anyhow:: Result < ConversationMessage > {
26
27
let unsent_group_message = self
27
28
. with_transaction ( |transaction| {
28
- InitialParams {
29
+ UnsentContent {
29
30
conversation_id,
30
31
content,
31
32
}
@@ -44,21 +45,43 @@ impl CoreUser {
44
45
} )
45
46
. await
46
47
}
48
+
49
+ /// Re-try sending a message, where sending previously failed.
50
+ pub async fn re_send_message ( & self , local_message_id : Uuid ) -> anyhow:: Result < ( ) > {
51
+ let unsent_group_message = self
52
+ . with_transaction ( |transaction| {
53
+ LocalMessage { local_message_id }
54
+ . load ( transaction) ?
55
+ . create_group_message ( & PhnxOpenMlsProvider :: new ( transaction) )
56
+ } )
57
+ . await ?;
58
+
59
+ let sent_message = unsent_group_message
60
+ . send_message_to_ds ( & self . inner . api_clients )
61
+ . await ?;
62
+
63
+ self . with_transaction ( |transaction| {
64
+ sent_message. mark_as_sent_and_read ( transaction, self . store_notifier ( ) )
65
+ } )
66
+ . await ?;
67
+
68
+ Ok ( ( ) )
69
+ }
47
70
}
48
71
49
- struct InitialParams {
72
+ struct UnsentContent {
50
73
conversation_id : ConversationId ,
51
74
content : MimiContent ,
52
75
}
53
76
54
- impl InitialParams {
77
+ impl UnsentContent {
55
78
fn store_unsent_message (
56
79
self ,
57
80
connection : & Connection ,
58
81
mut notifier : StoreNotifier ,
59
82
sender : & QualifiedUserName ,
60
- ) -> anyhow:: Result < UnsentMessage < WithContent > > {
61
- let InitialParams {
83
+ ) -> anyhow:: Result < UnsentMessage < WithContent , GroupUpdateNeeded > > {
84
+ let UnsentContent {
62
85
conversation_id,
63
86
content,
64
87
} = self ;
@@ -85,34 +108,80 @@ impl InitialParams {
85
108
conversation,
86
109
group,
87
110
conversation_message,
88
- state : WithContent ( content) ,
111
+ content : WithContent ( content) ,
112
+ group_update : GroupUpdateNeeded ,
89
113
} )
90
114
}
91
115
}
92
116
93
- // States of an unsent message
117
+ struct LocalMessage {
118
+ local_message_id : Uuid ,
119
+ }
94
120
121
+ impl LocalMessage {
122
+ fn load (
123
+ self ,
124
+ connection : & Connection ,
125
+ ) -> anyhow:: Result < UnsentMessage < WithContent , GroupUpdated > > {
126
+ let Self { local_message_id } = self ;
127
+
128
+ let conversation_message = ConversationMessage :: load ( connection, & local_message_id) ?
129
+ . with_context ( || format ! ( "Can't find unsent message with id {local_message_id}" ) ) ?;
130
+ let content = match conversation_message. message ( ) {
131
+ Message :: Content ( content_message) if !content_message. was_sent ( ) => {
132
+ content_message. content ( ) . clone ( )
133
+ }
134
+ Message :: Content ( _) => bail ! ( "Message with id {local_message_id} was already sent" ) ,
135
+ _ => bail ! ( "Message with id {local_message_id} is not a content message" ) ,
136
+ } ;
137
+ let conversation_id = conversation_message. conversation_id ( ) ;
138
+ let conversation = Conversation :: load ( connection, & conversation_id) ?
139
+ . with_context ( || format ! ( "Can't find conversation with id {conversation_id}" ) ) ?;
140
+ let group_id = conversation. group_id ( ) ;
141
+ let group = Group :: load ( connection, group_id) ?
142
+ . with_context ( || format ! ( "Can't find group with id {group_id:?}" ) ) ?;
143
+
144
+ let message = UnsentMessage {
145
+ conversation,
146
+ group,
147
+ conversation_message,
148
+ content : WithContent ( content) ,
149
+ group_update : GroupUpdated ,
150
+ } ;
151
+
152
+ Ok ( message)
153
+ }
154
+ }
155
+
156
+ /// Message type state: Message with MIMI content
95
157
struct WithContent ( MimiContent ) ;
158
+ /// Message type state: Message with prepared send parameters
96
159
struct WithParams ( SendMessageParamsOut ) ;
97
- struct StoredWithParams ( SendMessageParamsOut ) ;
98
160
99
- struct UnsentMessage < State > {
161
+ /// Message type state: Group update needed before sending the message
162
+ struct GroupUpdateNeeded ;
163
+ /// Message type state: Group already updated, message can be sent
164
+ struct GroupUpdated ;
165
+
166
+ struct UnsentMessage < State , GroupUpdate > {
100
167
conversation : Conversation ,
101
168
group : Group ,
102
169
conversation_message : ConversationMessage ,
103
- state : State ,
170
+ content : State ,
171
+ group_update : GroupUpdate ,
104
172
}
105
173
106
- impl UnsentMessage < WithContent > {
174
+ impl < GroupUpdate > UnsentMessage < WithContent , GroupUpdate > {
107
175
fn create_group_message (
108
176
self ,
109
177
provider : & impl OpenMlsProvider ,
110
- ) -> anyhow:: Result < UnsentMessage < WithParams > > {
178
+ ) -> anyhow:: Result < UnsentMessage < WithParams , GroupUpdate > > {
111
179
let Self {
112
180
conversation,
113
181
mut group,
114
182
conversation_message,
115
- state : WithContent ( content) ,
183
+ content : WithContent ( content) ,
184
+ group_update,
116
185
} = self ;
117
186
118
187
let params = group. create_message ( provider, content) ?;
@@ -121,22 +190,24 @@ impl UnsentMessage<WithContent> {
121
190
conversation,
122
191
conversation_message,
123
192
group,
124
- state : WithParams ( params) ,
193
+ content : WithParams ( params) ,
194
+ group_update,
125
195
} )
126
196
}
127
197
}
128
198
129
- impl UnsentMessage < WithParams > {
199
+ impl UnsentMessage < WithParams , GroupUpdateNeeded > {
130
200
fn store_group_update (
131
201
self ,
132
202
transaction : & Transaction ,
133
203
mut notifier : StoreNotifier ,
134
- ) -> anyhow:: Result < UnsentMessage < StoredWithParams > > {
204
+ ) -> anyhow:: Result < UnsentMessage < WithParams , GroupUpdated > > {
135
205
let Self {
136
206
conversation,
137
207
group,
138
208
conversation_message,
139
- state : WithParams ( params) ,
209
+ content : WithParams ( params) ,
210
+ group_update : GroupUpdateNeeded ,
140
211
} = self ;
141
212
142
213
// Immediately write the group back. No need to wait for the DS to
@@ -155,18 +226,20 @@ impl UnsentMessage<WithParams> {
155
226
conversation,
156
227
group,
157
228
conversation_message,
158
- state : StoredWithParams ( params) ,
229
+ content : WithParams ( params) ,
230
+ group_update : GroupUpdated ,
159
231
} )
160
232
}
161
233
}
162
234
163
- impl UnsentMessage < StoredWithParams > {
235
+ impl UnsentMessage < WithParams , GroupUpdated > {
164
236
async fn send_message_to_ds ( self , api_clients : & ApiClients ) -> anyhow:: Result < SentMessage > {
165
237
let Self {
166
238
conversation,
167
239
conversation_message,
168
240
group,
169
- state : StoredWithParams ( params) ,
241
+ content : WithParams ( params) ,
242
+ group_update : GroupUpdated ,
170
243
} = self ;
171
244
172
245
let ds_timestamp = api_clients
0 commit comments