@@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart';
7
7
8
8
import '../api/exception.dart' ;
9
9
import '../api/model/events.dart' ;
10
+ import '../api/model/initial_snapshot.dart' ;
10
11
import '../api/model/model.dart' ;
11
12
import '../api/route/messages.dart' ;
12
13
import '../log.dart' ;
@@ -78,6 +79,11 @@ mixin MessageStore on ChannelStore {
78
79
/// Should only be called when there is a failed request,
79
80
/// per [getEditMessageErrorStatus] .
80
81
({String originalRawContent, String newContent}) takeFailedMessageEdit (int messageId);
82
+
83
+ /// Whether the user has permission to delete a message, as of [byDate] .
84
+ ///
85
+ /// For a value of [byDate] , use [ZulipBinding.instance.utcNow] .
86
+ bool selfCanDeleteMessage (int messageId, {required DateTime byDate});
81
87
}
82
88
83
89
mixin ProxyMessageStore on MessageStore {
@@ -122,6 +128,9 @@ mixin ProxyMessageStore on MessageStore {
122
128
({String originalRawContent, String newContent}) takeFailedMessageEdit (int messageId) {
123
129
return messageStore.takeFailedMessageEdit (messageId);
124
130
}
131
+ @override
132
+ bool selfCanDeleteMessage (int messageId, {required DateTime byDate}) =>
133
+ messageStore.selfCanDeleteMessage (messageId, byDate: byDate);
125
134
126
135
@override
127
136
Set <MessageListView > get debugMessageListViews => messageStore.debugMessageListViews;
@@ -376,6 +385,113 @@ class MessageStoreImpl extends HasChannelStore with MessageStore, _OutboxMessage
376
385
);
377
386
}
378
387
388
+ @override
389
+ bool selfCanDeleteMessage (int messageId, {required DateTime byDate}) {
390
+ // Compare web's message_delete.get_deletability.
391
+
392
+ final message = messages[messageId];
393
+ if (message == null ) return false ; // TODO(log)
394
+
395
+ final ZulipStream ? channel;
396
+ if (message is StreamMessage ) {
397
+ channel = streams[message.streamId];
398
+ // TODO(log) if channel null here?
399
+ } else {
400
+ channel = null ;
401
+ }
402
+
403
+ if (channel != null && channel.isArchived == true ) {
404
+ return false ;
405
+ }
406
+
407
+ if (realmCanDeleteAnyMessageGroup != null
408
+ && selfHasPermissionForGroupSetting (realmCanDeleteAnyMessageGroup! ,
409
+ GroupSettingType .realm, 'can_delete_any_message_group' )) {
410
+ return true ;
411
+ }
412
+
413
+ if (channel != null ) {
414
+ if (channel.canDeleteAnyMessageGroup != null
415
+ && selfHasPermissionForGroupSetting (channel.canDeleteAnyMessageGroup! ,
416
+ GroupSettingType .stream, 'can_delete_any_message_group' )) {
417
+ return true ;
418
+ }
419
+ }
420
+
421
+ final sender = getUser (message.senderId);
422
+ if (sender == null ) return false ;
423
+
424
+ if (
425
+ sender.userId != selfUserId
426
+ && ! (sender.isBot && sender.botOwnerId == selfUserId)
427
+ ) {
428
+ return false ;
429
+ }
430
+
431
+ // Web returns false here for local-echoed message objects;
432
+ // that's impossible here because `message` can't be an [OutboxMessage]
433
+ // (it's a [Message] from [MessageStore.messages]).
434
+
435
+ if (realmCanDeleteOwnMessageGroup != null ) {
436
+ if (! selfHasPermissionForGroupSetting (realmCanDeleteOwnMessageGroup! ,
437
+ GroupSettingType .realm, 'can_delete_own_message_group' )) {
438
+ if (channel == null ) {
439
+ // i.e. this is a DM
440
+ return false ;
441
+ }
442
+
443
+ if (
444
+ channel.canDeleteOwnMessageGroup == null
445
+ || ! selfHasPermissionForGroupSetting (channel.canDeleteOwnMessageGroup! ,
446
+ GroupSettingType .stream, 'can_delete_own_message_group' )
447
+ ) {
448
+ return false ;
449
+ }
450
+ }
451
+ }
452
+
453
+ if (
454
+ realmDeleteOwnMessagePolicy != null
455
+ && ! _selfPassesLegacyDeleteMessagePolicy (messageId, byDate: byDate)
456
+ ) {
457
+ return false ;
458
+ }
459
+
460
+ if (realmMessageContentDeleteLimitSeconds == null ) {
461
+ // i.e., no limit
462
+ return true ;
463
+ }
464
+
465
+ return byDate.millisecondsSinceEpoch ~ / 1000 - message.timestamp
466
+ <= realmMessageContentDeleteLimitSeconds! ;
467
+ }
468
+
469
+ bool _selfPassesLegacyDeleteMessagePolicy (int messageId, {required DateTime byDate}) {
470
+ assert (realmDeleteOwnMessagePolicy != null );
471
+ final role = selfUser.role;
472
+
473
+ // (Could early-return true on [UserRole.unknown],
474
+ // but pre-291 servers shouldn't be giving us an unknown role.)
475
+
476
+ switch (realmDeleteOwnMessagePolicy! ) {
477
+ case RealmDeleteOwnMessagePolicy .members:
478
+ return true ;
479
+ case RealmDeleteOwnMessagePolicy .admins:
480
+ return role.isAtLeast (UserRole .administrator);
481
+ case RealmDeleteOwnMessagePolicy .fullMembers: {
482
+ if (! role.isAtLeast (UserRole .member)) return false ;
483
+ if (role == UserRole .member) {
484
+ return hasPassedWaitingPeriod (selfUser, byDate: byDate);
485
+ }
486
+ return true ;
487
+ }
488
+ case RealmDeleteOwnMessagePolicy .moderators:
489
+ return role.isAtLeast (UserRole .moderator);
490
+ case RealmDeleteOwnMessagePolicy .everyone:
491
+ return true ;
492
+ }
493
+ }
494
+
379
495
void handleUserTopicEvent (UserTopicEvent event) {
380
496
for (final view in _messageListViews) {
381
497
view.handleUserTopicEvent (event);
0 commit comments