Skip to content

Commit 263e304

Browse files
committed
message: Implement selfCanDeleteMessage
Related: #1548
1 parent 3cb7b90 commit 263e304

File tree

3 files changed

+630
-1
lines changed

3 files changed

+630
-1
lines changed

lib/model/message.dart

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart';
77

88
import '../api/exception.dart';
99
import '../api/model/events.dart';
10+
import '../api/model/initial_snapshot.dart';
1011
import '../api/model/model.dart';
1112
import '../api/route/messages.dart';
1213
import '../log.dart';
@@ -78,6 +79,115 @@ 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}) {
87+
// Compare web's message_delete.get_deletability.
88+
89+
final message = messages[messageId];
90+
if (message == null) return false; // TODO(log)
91+
92+
final ZulipStream? channel;
93+
if (message is StreamMessage) {
94+
channel = streams[message.streamId];
95+
// TODO(log) if channel null here?
96+
} else {
97+
channel = null;
98+
}
99+
100+
if (channel != null && channel.isArchived == true) {
101+
return false;
102+
}
103+
104+
if (realmCanDeleteAnyMessageGroup != null
105+
&& selfHasPermissionForGroupSetting(realmCanDeleteAnyMessageGroup!,
106+
GroupSettingType.realm, 'can_delete_any_message_group')) {
107+
return true;
108+
}
109+
110+
if (channel != null) {
111+
if (channel.canDeleteAnyMessageGroup != null
112+
&& selfHasPermissionForGroupSetting(channel.canDeleteAnyMessageGroup!,
113+
GroupSettingType.stream, 'can_delete_any_message_group')) {
114+
return true;
115+
}
116+
}
117+
118+
final sender = getUser(message.senderId);
119+
if (sender == null) return false;
120+
121+
if (
122+
sender.userId != selfUserId
123+
&& !(sender.isBot && sender.botOwnerId == selfUserId)
124+
) {
125+
return false;
126+
}
127+
128+
// Web returns false here for local-echoed message objects;
129+
// that's impossible here because `message` can't be an [OutboxMessage]
130+
// (it's a [Message] from [MessageStore.messages]).
131+
132+
if (realmCanDeleteOwnMessageGroup != null) {
133+
if (!selfHasPermissionForGroupSetting(realmCanDeleteOwnMessageGroup!,
134+
GroupSettingType.realm, 'can_delete_own_message_group')) {
135+
if (channel == null) {
136+
// i.e. this is a DM
137+
return false;
138+
}
139+
140+
if (
141+
channel.canDeleteOwnMessageGroup == null
142+
|| !selfHasPermissionForGroupSetting(channel.canDeleteOwnMessageGroup!,
143+
GroupSettingType.stream, 'can_delete_own_message_group')
144+
) {
145+
return false;
146+
}
147+
}
148+
}
149+
150+
if (
151+
realmDeleteOwnMessagePolicy != null
152+
&& !_selfPassesLegacyDeleteMessagePolicy(messageId, byDate: byDate)
153+
) {
154+
return false;
155+
}
156+
157+
if (realmMessageContentDeleteLimitSeconds == null) {
158+
// i.e., no limit
159+
return true;
160+
}
161+
162+
return byDate.millisecondsSinceEpoch ~/ 1000 - message.timestamp
163+
<= realmMessageContentDeleteLimitSeconds!;
164+
}
165+
166+
bool _selfPassesLegacyDeleteMessagePolicy(int messageId, {required DateTime byDate}) {
167+
assert(realmDeleteOwnMessagePolicy != null);
168+
final role = selfUser.role;
169+
170+
// (Could early-return true on [UserRole.unknown],
171+
// but pre-291 servers shouldn't be giving us an unknown role.)
172+
173+
switch (realmDeleteOwnMessagePolicy!) {
174+
case RealmDeleteOwnMessagePolicy.members:
175+
return true;
176+
case RealmDeleteOwnMessagePolicy.admins:
177+
return role.isAtLeast(UserRole.administrator);
178+
case RealmDeleteOwnMessagePolicy.fullMembers: {
179+
if (!role.isAtLeast(UserRole.member)) return false;
180+
if (role == UserRole.member) {
181+
return hasPassedWaitingPeriod(selfUser, byDate: byDate);
182+
}
183+
return true;
184+
}
185+
case RealmDeleteOwnMessagePolicy.moderators:
186+
return role.isAtLeast(UserRole.moderator);
187+
case RealmDeleteOwnMessagePolicy.everyone:
188+
return true;
189+
}
190+
}
81191
}
82192

83193
mixin ProxyMessageStore on MessageStore {

test/example_data.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ User user({
271271
String? dateJoined,
272272
bool? isActive,
273273
bool? isBot,
274+
int? botOwnerId,
274275
UserRole? role,
275276
String? avatarUrl,
276277
Map<int, ProfileFieldUserData>? profileData,
@@ -286,7 +287,7 @@ User user({
286287
isActive: isActive ?? true,
287288
isBot: isBot ?? false,
288289
botType: null,
289-
botOwnerId: null,
290+
botOwnerId: botOwnerId,
290291
role: role ?? UserRole.member,
291292
timezone: 'UTC',
292293
avatarUrl: avatarUrl,

0 commit comments

Comments
 (0)