@@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart';
77
88import '../api/exception.dart' ;
99import '../api/model/events.dart' ;
10+ import '../api/model/initial_snapshot.dart' ;
1011import '../api/model/model.dart' ;
1112import '../api/route/messages.dart' ;
1213import '../log.dart' ;
@@ -78,6 +79,11 @@ mixin MessageStore on ChannelStore {
7879 /// Should only be called when there is a failed request,
7980 /// per [getEditMessageErrorStatus] .
8081 ({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});
8187}
8288
8389mixin ProxyMessageStore on MessageStore {
@@ -122,6 +128,9 @@ mixin ProxyMessageStore on MessageStore {
122128 ({String originalRawContent, String newContent}) takeFailedMessageEdit (int messageId) {
123129 return messageStore.takeFailedMessageEdit (messageId);
124130 }
131+ @override
132+ bool selfCanDeleteMessage (int messageId, {required DateTime byDate}) =>
133+ messageStore.selfCanDeleteMessage (messageId, byDate: byDate);
125134
126135 @override
127136 Set <MessageListView > get debugMessageListViews => messageStore.debugMessageListViews;
@@ -376,6 +385,113 @@ class MessageStoreImpl extends HasChannelStore with MessageStore, _OutboxMessage
376385 );
377386 }
378387
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+
379495 void handleUserTopicEvent (UserTopicEvent event) {
380496 for (final view in _messageListViews) {
381497 view.handleUserTopicEvent (event);
0 commit comments