1689-make-separate-room-extensions (#1727)

* fix(room extensions): Made first use of room extensions the original definition

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: ggurdin <46800240+ggurdin@users.noreply.github.com>
This commit is contained in:
sienna-sterling 2025-02-07 12:51:00 -05:00 committed by GitHub
parent 98e66abd75
commit f4ab6f7458
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 51 additions and 225 deletions

View file

@ -174,14 +174,14 @@ extension AnalyticsRoomExtension on Room {
}
}
Future<DateTime?> _analyticsLastUpdated(String userId) async {
Future<DateTime?> analyticsLastUpdated(String userId) async {
final List<Event> events =
await getRoomAnalyticsEvents(count: 1, userID: userId);
if (events.isEmpty) return null;
return events.first.originServerTs;
}
Future<List<ConstructAnalyticsEvent>?> _getAnalyticsEvents({
Future<List<ConstructAnalyticsEvent>?> getAnalyticsEvents({
required String userId,
DateTime? since,
}) async {
@ -194,13 +194,13 @@ extension AnalyticsRoomExtension on Room {
return analyticsEvents;
}
String? get _madeForLang {
String? get madeForLang {
final creationContent = getState(EventTypes.RoomCreate)?.content;
return creationContent?.tryGet<String>(ModelKey.langCode) ??
creationContent?.tryGet<String>(ModelKey.oldLangCode);
}
bool _isMadeForLang(String langCode) {
bool isMadeForLang(String langCode) {
final creationContent = getState(EventTypes.RoomCreate)?.content;
return creationContent?.tryGet<String>(ModelKey.langCode) == langCode ||
creationContent?.tryGet<String>(ModelKey.oldLangCode) == langCode;
@ -211,7 +211,7 @@ extension AnalyticsRoomExtension on Room {
/// The [uses] parameter is a list of [OneConstructUse] objects representing the
/// constructs to be sent. To prevent hitting the maximum event size, the events
/// are chunked into smaller lists. Each chunk is sent as a separate event.
Future<void> _sendConstructsEvent(
Future<void> sendConstructsEvent(
List<OneConstructUse> uses,
) async {
// It's possible that the user has no info to send yet, but to prevent trying

View file

@ -33,7 +33,7 @@ class RoomCapacityButtonState extends State<RoomCapacityButton> {
@override
void initState() {
super.initState();
capacity = widget.room?.capacity;
capacity = RoomSettingsRoomExtension(widget.room)?.capacity;
widget.room?.numNonAdmins.then(
(value) => setState(() {
nonAdmins = value.toString();
@ -46,7 +46,7 @@ class RoomCapacityButtonState extends State<RoomCapacityButton> {
void didUpdateWidget(RoomCapacityButton oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.room != widget.room) {
capacity = widget.room?.capacity;
capacity = RoomSettingsRoomExtension(widget.room)?.capacity;
widget.room?.numNonAdmins.then(
(value) => setState(() {
nonAdmins = value.toString();

View file

@ -24,6 +24,7 @@ import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart';
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/spaces/constants/space_constants.dart';
import 'package:fluffychat/pangea/spaces/models/space_model.dart';
import '../choreographer/models/choreo_record.dart';
@ -40,180 +41,4 @@ part "room_user_permissions_extension.dart";
extension PangeaRoom on Room {
// analytics
Future<DateTime?> analyticsLastUpdated(String userId) async {
return await _analyticsLastUpdated(userId);
}
Future<List<ConstructAnalyticsEvent>?> getAnalyticsEvents({
required String userId,
DateTime? since,
}) async =>
await _getAnalyticsEvents(since: since, userId: userId);
String? get madeForLang => _madeForLang;
bool isMadeForLang(String langCode) => _isMadeForLang(langCode);
/// Sends construct events to the server.
///
/// The [uses] parameter is a list of [OneConstructUse] objects representing the
/// constructs to be sent. To prevent hitting the maximum event size, the events
/// are chunked into smaller lists. Each chunk is sent as a separate event.
Future<void> sendConstructsEvent(
List<OneConstructUse> uses,
) async =>
await _sendConstructsEvent(uses);
// children_and_parents
List<Room> get joinedChildren => _joinedChildren;
Future<List<Room>> getChildRooms() async => await _getChildRooms();
Room? firstParentWithState(String stateType) =>
_firstParentWithState(stateType);
List<Room> get pangeaSpaceParents => _pangeaSpaceParents;
Future<void> pangeaSetSpaceChild(
String roomId, {
bool? suggested,
}) async =>
await _pangeaSetSpaceChild(roomId, suggested: suggested);
/// Returns a map of child suggestion status for a space.
///
/// If the current object is not a space, an empty map is returned.
/// Otherwise, it iterates through each child in the `spaceChildren` list
/// and adds their suggestion status to the `suggestionStatus` map.
/// The suggestion status is determined by the `suggested` property of each child.
/// If the `suggested` property is `null`, it defaults to `true`.
Map<String, bool> get spaceChildSuggestionStatus =>
_spaceChildSuggestionStatus;
// class_and_exchange_settings
String classCode(BuildContext context) => _classCode(context);
void checkClass() => _checkClass();
Future<List<User>> get teachers async => await _teachers;
Event? get pangeaRoomRulesStateEvent => _pangeaRoomRulesStateEvent;
// events
Future<bool> leaveIfFull() async => await _leaveIfFull();
Future<void> leaveSpace() async => await _leaveSpace();
Future<Event?> sendPangeaEvent({
required Map<String, dynamic> content,
required String parentEventId,
required String type,
}) async =>
await _sendPangeaEvent(
content: content,
parentEventId: parentEventId,
type: type,
);
Future<String?> pangeaSendTextEvent(
String message, {
String? txid,
Event? inReplyTo,
String? editEventId,
bool parseMarkdown = true,
bool parseCommands = false,
String msgtype = MessageTypes.Text,
String? threadRootEventId,
String? threadLastEventId,
PangeaRepresentation? originalSent,
PangeaRepresentation? originalWritten,
PangeaMessageTokens? tokensSent,
PangeaMessageTokens? tokensWritten,
ChoreoRecord? choreo,
String? messageTag,
}) =>
_pangeaSendTextEvent(
message,
txid: txid,
inReplyTo: inReplyTo,
editEventId: editEventId,
parseMarkdown: parseMarkdown,
parseCommands: parseCommands,
msgtype: msgtype,
threadRootEventId: threadRootEventId,
threadLastEventId: threadLastEventId,
originalSent: originalSent,
originalWritten: originalWritten,
tokensSent: tokensSent,
tokensWritten: tokensWritten,
choreo: choreo,
messageTag: messageTag,
);
String sendFakeMessage({
required String text,
Event? inReplyTo,
String? editEventId,
}) =>
_sendFakeMessage(
text: text,
inReplyTo: inReplyTo,
editEventId: editEventId,
);
// room_information
Future<int> get numNonAdmins async => await _numNonAdmins;
DateTime? get creationTime => _creationTime;
String? get creatorId => _creatorId;
bool isFirstOrSecondChild(String roomId) => _isFirstOrSecondChild(roomId);
// bool isMadeForLang(String langCode) => _isMadeForLang(langCode);
Future<bool> get botIsInRoom async => await _botIsInRoom;
bool get isBotDM => _isBotDM;
// bool get isLocked => _isLocked;
bool isAnalyticsRoomOfUser(String userId) => _isAnalyticsRoomOfUser(userId);
bool get isAnalyticsRoom => _isAnalyticsRoom;
// room_settings
Future<void> updateRoomCapacity(int newCapacity) =>
_updateRoomCapacity(newCapacity);
int? get capacity => _capacity;
PangeaRoomRules? get pangeaRoomRules => _pangeaRoomRules;
IconData? get roomTypeIcon => _roomTypeIcon;
Text nameAndRoomTypeIcon([TextStyle? textStyle]) =>
_nameAndRoomTypeIcon(textStyle);
BotOptionsModel? get botOptions => _botOptions;
// user_permissions
Future<bool> isOnlyAdmin() async => await _isOnlyAdmin();
bool isMadeByUser(String userId) => _isMadeByUser(userId);
bool get isSpaceAdmin => _isSpaceAdmin;
bool isUserRoomAdmin(String userId) => _isUserRoomAdmin(userId);
bool get isRoomAdmin => _isRoomAdmin;
bool pangeaCanSendEvent(String eventType) => _pangeaCanSendEvent(eventType);
}

View file

@ -2,7 +2,7 @@ part of "pangea_room_extension.dart";
extension ChildrenAndParentsRoomExtension on Room {
//note this only will return rooms that the user has joined or been invited to
List<Room> get _joinedChildren {
List<Room> get joinedChildren {
if (!isSpace) return [];
return spaceChildren
.where((child) => child.roomId != null)
@ -17,7 +17,7 @@ extension ChildrenAndParentsRoomExtension on Room {
.toList();
}
Future<List<Room>> _getChildRooms() async {
Future<List<Room>> getChildRooms() async {
final List<Room> children = [];
for (final child in spaceChildren) {
if (child.roomId == null) continue;
@ -31,7 +31,7 @@ extension ChildrenAndParentsRoomExtension on Room {
//resolve somehow if multiple rooms have the state?
//check logic
Room? _firstParentWithState(String stateType) {
Room? firstParentWithState(String stateType) {
if (![PangeaEventTypes.languageSettings, PangeaEventTypes.rules]
.contains(stateType)) {
return null;
@ -49,7 +49,7 @@ extension ChildrenAndParentsRoomExtension on Room {
return null;
}
List<Room> get _pangeaSpaceParents => client.rooms
List<Room> get pangeaSpaceParents => client.rooms
.where(
(r) => r.isSpace,
)
@ -63,7 +63,7 @@ extension ChildrenAndParentsRoomExtension on Room {
/// Wrapper around call to setSpaceChild with added functionality
/// to prevent adding one room to multiple spaces, and resets the
/// subspace's JoinRules and Visibility to defaults.
Future<void> _pangeaSetSpaceChild(
Future<void> pangeaSetSpaceChild(
String roomId, {
bool? suggested,
}) async {
@ -73,7 +73,8 @@ extension ChildrenAndParentsRoomExtension on Room {
throw NestedSpaceError();
}
final List<Room> spaceParents = child.pangeaSpaceParents;
final List<Room> spaceParents =
ChildrenAndParentsRoomExtension(child).pangeaSpaceParents;
for (final Room parent in spaceParents) {
try {
await parent.removeSpaceChild(roomId);
@ -110,7 +111,7 @@ extension ChildrenAndParentsRoomExtension on Room {
}
/// A map of child suggestion status for a space.
Map<String, bool> get _spaceChildSuggestionStatus {
Map<String, bool> get spaceChildSuggestionStatus {
if (!isSpace) return {};
final Map<String, bool> suggestionStatus = {};
for (final child in spaceChildren) {

View file

@ -1,10 +1,10 @@
part of "pangea_room_extension.dart";
extension EventsRoomExtension on Room {
Future<bool> _leaveIfFull() async {
Future<bool> leaveIfFull() async {
if (!isRoomAdmin &&
(_capacity != null) &&
(await _numNonAdmins) > (_capacity!)) {
(capacity != null) &&
(await numNonAdmins) > (capacity!)) {
if (!isSpace) {
markUnread(false);
}
@ -14,7 +14,7 @@ extension EventsRoomExtension on Room {
return false;
}
Future<void> _leaveSpace() async {
Future<void> leaveSpace() async {
for (final child in spaceChildren) {
if (child.roomId == null) continue;
final Room? room = client.getRoomById(child.roomId!);
@ -45,7 +45,7 @@ extension EventsRoomExtension on Room {
}
}
Future<Event?> _sendPangeaEvent({
Future<Event?> sendPangeaEvent({
required Map<String, dynamic> content,
required String parentEventId,
required String type,
@ -97,7 +97,7 @@ extension EventsRoomExtension on Room {
}
}
String _sendFakeMessage({
String sendFakeMessage({
required String text,
Event? inReplyTo,
String? editEventId,
@ -185,7 +185,7 @@ extension EventsRoomExtension on Room {
return messageID;
}
Future<String?> _pangeaSendTextEvent(
Future<String?> pangeaSendTextEvent(
String message, {
String? txid,
Event? inReplyTo,

View file

@ -1,7 +1,7 @@
part of "pangea_room_extension.dart";
extension RoomInformationRoomExtension on Room {
Future<int> get _numNonAdmins async {
Future<int> get numNonAdmins async {
return (await requestParticipants())
.where(
(e) =>
@ -12,15 +12,15 @@ extension RoomInformationRoomExtension on Room {
.length;
}
DateTime? get _creationTime {
DateTime? get creationTime {
final dynamic roomCreate = getState(EventTypes.RoomCreate);
if (roomCreate is! Event) return null;
return roomCreate.originServerTs;
}
String? get _creatorId => getState(EventTypes.RoomCreate)?.senderId;
String? get creatorId => getState(EventTypes.RoomCreate)?.senderId;
bool _isFirstOrSecondChild(String roomId) {
bool isFirstOrSecondChild(String roomId) {
return isSpace &&
(spaceChildren.any((room) => room.roomId == roomId) ||
spaceChildren
@ -34,19 +34,19 @@ extension RoomInformationRoomExtension on Room {
));
}
Future<bool> get _botIsInRoom async {
Future<bool> get botIsInRoom async {
final List<User> participants = await requestParticipants();
return participants.any(
(User user) => user.id == BotName.byEnvironment,
);
}
bool get _isBotDM => botOptions?.mode == BotMode.directChat;
bool get isBotDM => botOptions?.mode == BotMode.directChat;
bool _isAnalyticsRoomOfUser(String userId) =>
bool isAnalyticsRoomOfUser(String userId) =>
isAnalyticsRoom && isMadeByUser(userId);
bool get _isAnalyticsRoom =>
bool get isAnalyticsRoom =>
getState(EventTypes.RoomCreate)?.content.tryGet<String>('type') ==
PangeaRoomTypes.analytics;
}

View file

@ -1,7 +1,7 @@
part of "pangea_room_extension.dart";
extension RoomSettingsRoomExtension on Room {
Future<void> _updateRoomCapacity(int newCapacity) =>
Future<void> updateRoomCapacity(int newCapacity) =>
client.setRoomStateWithKey(
id,
PangeaEventTypes.capacity,
@ -9,12 +9,12 @@ extension RoomSettingsRoomExtension on Room {
{'capacity': newCapacity},
);
int? get _capacity {
int? get capacity {
final t = getState(PangeaEventTypes.capacity)?.content['capacity'];
return t is int ? t : null;
}
PangeaRoomRules? get _pangeaRoomRules {
PangeaRoomRules? get pangeaRoomRules {
try {
final Map<String, dynamic>? content = pangeaRoomRulesStateEvent?.content;
if (content != null) {
@ -40,7 +40,7 @@ extension RoomSettingsRoomExtension on Room {
}
}
IconData? get _roomTypeIcon {
IconData? get roomTypeIcon {
if (membership == Membership.invite) return Icons.add;
if (isSpace) return Icons.school;
if (isAnalyticsRoom) return Icons.analytics;
@ -48,7 +48,7 @@ extension RoomSettingsRoomExtension on Room {
return Icons.group;
}
Text _nameAndRoomTypeIcon([TextStyle? textStyle]) => Text.rich(
Text nameAndRoomTypeIcon([TextStyle? textStyle]) => Text.rich(
style: textStyle,
TextSpan(
children: [
@ -62,7 +62,7 @@ extension RoomSettingsRoomExtension on Room {
),
);
BotOptionsModel? get _botOptions {
BotOptionsModel? get botOptions {
if (isSpace) return null;
final stateEvent = getState(PangeaEventTypes.botOptions);
if (stateEvent == null) return null;

View file

@ -1,11 +1,11 @@
part of "pangea_room_extension.dart";
extension SpaceRoomExtension on Room {
String _classCode(BuildContext context) {
String classCode(BuildContext context) {
if (!isSpace) {
for (final Room potentialClassRoom in pangeaSpaceParents) {
if (potentialClassRoom.isSpace) {
return potentialClassRoom.classCode(context);
return SpaceRoomExtension(potentialClassRoom).classCode(context);
}
}
return L10n.of(context).notInClass;
@ -20,7 +20,7 @@ extension SpaceRoomExtension on Room {
return L10n.of(context).noClassCode;
}
void _checkClass() {
void checkClass() {
if (!isSpace) {
debugger(when: kDebugMode);
Sentry.addBreadcrumb(
@ -29,7 +29,7 @@ extension SpaceRoomExtension on Room {
}
}
Future<List<User>> get _teachers async {
Future<List<User>> get teachers async {
checkClass();
final List<User> participants = await requestParticipants();
return isSpace
@ -43,7 +43,7 @@ extension SpaceRoomExtension on Room {
: participants;
}
Event? get _pangeaRoomRulesStateEvent {
Event? get pangeaRoomRulesStateEvent {
final dynamic roomRules = getState(PangeaEventTypes.rules);
if (roomRules is Event) {
return roomRules;

View file

@ -2,7 +2,7 @@ part of "pangea_room_extension.dart";
extension UserPermissionsRoomExtension on Room {
// If there are no other admins, and at least one non-admin, return true
Future<bool> _isOnlyAdmin() async {
Future<bool> isOnlyAdmin() async {
if (!isRoomAdmin) {
return false;
}
@ -27,23 +27,23 @@ extension UserPermissionsRoomExtension on Room {
.isNotEmpty;
}
bool _isMadeByUser(String userId) =>
bool isMadeByUser(String userId) =>
getState(EventTypes.RoomCreate)?.senderId == userId;
//if the user is an admin of the room or any immediate parent of the room
//Question: check parents of parents?
//check logic
bool get _isSpaceAdmin {
if (isSpace) return _isRoomAdmin;
bool get isSpaceAdmin {
if (isSpace) return isRoomAdmin;
for (final parent in pangeaSpaceParents) {
if (parent._isRoomAdmin) {
if (parent.isRoomAdmin) {
return true;
}
}
for (final parent in pangeaSpaceParents) {
for (final parent2 in parent.pangeaSpaceParents) {
if (parent2._isRoomAdmin) {
if (parent2.isRoomAdmin) {
return true;
}
}
@ -51,15 +51,15 @@ extension UserPermissionsRoomExtension on Room {
return false;
}
bool _isUserRoomAdmin(String userId) => getParticipants().any(
bool isUserRoomAdmin(String userId) => getParticipants().any(
(e) =>
e.id == userId && e.powerLevel == SpaceConstants.powerLevelOfAdmin,
);
bool get _isRoomAdmin => ownPowerLevel == SpaceConstants.powerLevelOfAdmin;
bool get isRoomAdmin => ownPowerLevel == SpaceConstants.powerLevelOfAdmin;
// Overriding the default canSendEvent to check power levels
bool _pangeaCanSendEvent(String eventType) {
bool pangeaCanSendEvent(String eventType) {
final powerLevelsMap = getState(EventTypes.RoomPowerLevels)?.content;
if (powerLevelsMap == null) return 0 <= ownPowerLevel;
final pl = powerLevelsMap