bump matrix sdk to 0.40.0

This commit is contained in:
OfficialDakari 2025-05-10 12:42:34 +05:00
parent 442a5a5cdf
commit 9d7ed55d24
27 changed files with 358 additions and 64 deletions

View File

@ -1780,6 +1780,26 @@
"type": "String",
"placeholders": {}
},
"recoverMessage": "Recover message",
"@recoverMessage": {
"type": "String",
"placeholders": {}
},
"recoveredMessage": "Recovered message",
"@recoveredMessage": {
"type": "String",
"placeholders": {}
},
"errorRecoveringMessage": "An error has occured while recovering a message.",
"@errorRecoveringMessage": {
"type": "String",
"placeholders": {}
},
"errorRecoveringMessageNoAdmin": "This feature is available on Synapse homeservers only for adminstrators.",
"@errorRecoveringMessageNoAdmin": {
"type": "String",
"placeholders": {}
},
"requestPermission": "Request permission",
"@requestPermission": {
"type": "String",
@ -1800,6 +1820,11 @@
"type": "String",
"placeholders": {}
},
"retry": "Retry",
"@retry": {
"type": "String",
"placeholders": {}
},
"search": "Search",
"@search": {
"type": "String",
@ -2710,7 +2735,7 @@
"@openLinkInBrowser": {},
"reportErrorDescription": "😭 Oh no. Something went wrong. If you want, you can report this bug to the developers.",
"@reportErrorDescription": {},
"report": "report",
"report": "Report",
"@report": {},
"signInWithPassword": "Sign in with password",
"@signInWithPassword": {},

View File

@ -1529,11 +1529,21 @@
"type": "String",
"placeholders": {}
},
"reportMessage": "Сообщить о сообщении",
"reportMessage": "Пожаловаться",
"@reportMessage": {
"type": "String",
"placeholders": {}
},
"retry": "Повторить",
"@retry": {
"type": "String",
"placeholders": {}
},
"recoverMessage": "Восстановить",
"@recoverMessage": {
"type": "String",
"placeholders": {}
},
"requestPermission": "Запросить разрешение",
"@requestPermission": {
"type": "String",
@ -2631,7 +2641,7 @@
"@makeAdminDescription": {},
"archiveRoomDescription": "Чат переместится в архив. Другим пользователям будет видно, что вы вышли из чата.",
"@archiveRoomDescription": {},
"hasKnocked": "🚪 {user} постучался",
"hasKnocked": "🚪 {user} подал заявку на вступление",
"@hasKnocked": {
"placeholders": {
"user": {
@ -2869,11 +2879,11 @@
"@customEmojisAndStickersBody": {},
"hideMemberChangesInPublicChatsBody": "Для улучшения читаемости не показывать на временной шкале входы и выходы из чата.",
"@hideMemberChangesInPublicChatsBody": {},
"knocking": "Стучаться",
"knocking": "Подали заявку",
"@knocking": {},
"accessAndVisibility": "Доступность и видимость",
"@accessAndVisibility": {},
"publicChatAddresses": "Адресы публичного чата",
"publicChatAddresses": "Адреса публичного чата",
"@publicChatAddresses": {},
"accessAndVisibilityDescription": "Кому разрешено войти в этот чат и как этот чат может быть обнаружен.",
"@accessAndVisibilityDescription": {},
@ -2881,7 +2891,7 @@
"@userRole": {},
"noDatabaseEncryption": "Шифрование базы данных не поддерживается на этой платформе",
"@noDatabaseEncryption": {},
"appLockDescription": "Заблокировать приложение когда не используется пин код",
"appLockDescription": "Заблокировать приложение, когда не используется пин-код",
"@appLockDescription": {},
"calls": "Звонки",
"@calls": {},
@ -2889,7 +2899,7 @@
"@customEmojisAndStickers": {},
"hideRedactedMessages": "Скрыть редактированные сообщения",
"@hideRedactedMessages": {},
"hideInvalidOrUnknownMessageFormats": "Скрыть неправильные или неизвестные форматы сообщения",
"hideInvalidOrUnknownMessageFormats": "Скрыть неверные или неизвестные форматы сообщения",
"@hideInvalidOrUnknownMessageFormats": {},
"hideRedactedMessagesBody": "Если кто-то редактирует сообщение, оно будет скрыто в чате.",
"@hideRedactedMessagesBody": {},
@ -2909,9 +2919,9 @@
}
}
},
"knock": "Постучаться",
"knock": "Подать заявку",
"@knock": {},
"usersMustKnock": "Пользователи должны постучаться",
"usersMustKnock": "По заявке на вступление",
"@usersMustKnock": {},
"noOneCanJoin": "Никто не может присоединиться",
"@noOneCanJoin": {},
@ -2928,7 +2938,7 @@
},
"createNewAddress": "Создать новый адрес",
"@createNewAddress": {},
"minimumPowerLevel": "{level} является минимальным уровнем.",
"minimumPowerLevel": "{level} является минимальным уровнем прав.",
"@minimumPowerLevel": {
"type": "String",
"placeholders": {
@ -2937,9 +2947,9 @@
}
}
},
"commandHint_ignore": "Игнорировать данный matrix ID",
"commandHint_ignore": "Игнорировать данный Matrix ID",
"@commandHint_ignore": {},
"commandHint_unignore": "Не игнорировать данный matrix ID",
"commandHint_unignore": "Перестать игнорировать данный Matrix ID",
"@commandHint_unignore": {},
"unreadChatsInApp": "{appname}: {unread} непрочитанные чаты",
"@unreadChatsInApp": {

View File

@ -1,6 +1,8 @@
import 'dart:async';
import 'dart:io';
import 'package:fluffychat/pages/chat/recovered_event_dialog.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/synapse_admin_extension.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -271,6 +273,7 @@ class ChatController extends State<ChatPageWithRoom>
files: files,
room: room,
outerContext: context,
replyEvent: replyEvent
),
);
}
@ -552,8 +555,10 @@ class ChatController extends State<ChatPageWithRoom>
files: files,
room: room,
outerContext: context,
replyEvent: replyEvent,
),
);
replyEvent = null;
}
void sendImageFromClipBoard(Uint8List? image) async {
@ -712,6 +717,38 @@ class ChatController extends State<ChatPageWithRoom>
});
}
void recoverEventAction() async {
final mx = Matrix.of(context);
if (!await mx.client.isSynapseAdministrator()) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).errorRecoveringMessageNoAdmin)),
);
return;
}
final event = selectedEvents.single;
await mx.client.reportEvent(roomId, event.eventId,
reason: "Extera (Next) Redacted Event Recover");
final reports = await mx.client.getEventReports();
final report = reports.firstWhere(
(rep) => rep['room_id'] == roomId && rep['event_id'] == event.eventId);
final recoveredEvent = await mx.client.getReportedEvent(report['id']);
if (recoveredEvent == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).errorRecoveringMessage)),
);
return;
}
Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext ctx) {
return RecoveredEventDialog(event: recoveredEvent!, timeline: timeline!);
},
fullscreenDialog: true
));
}
void reportEventAction() async {
final event = selectedEvents.single;
final score = await showModalActionPopup<int>(
@ -757,9 +794,6 @@ class ChatController extends State<ChatPageWithRoom>
showEmojiPicker = false;
selectedEvents.clear();
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).contentHasBeenReported)),
);
}
void deleteErrorEventsAction() async {
@ -1087,20 +1121,18 @@ class ChatController extends State<ChatPageWithRoom>
}
void onSelectMessage(Event event) {
if (!event.redacted) {
if (selectedEvents.contains(event)) {
setState(
() => selectedEvents.remove(event),
);
} else {
setState(
() => selectedEvents.add(event),
);
}
selectedEvents.sort(
(a, b) => a.originServerTs.compareTo(b.originServerTs),
if (selectedEvents.contains(event)) {
setState(
() => selectedEvents.remove(event),
);
} else {
setState(
() => selectedEvents.add(event),
);
}
selectedEvents.sort(
(a, b) => a.originServerTs.compareTo(b.originServerTs),
);
}
int? findChildIndexCallback(Key key, Map<String, int> thisEventsKeyMap) {

View File

@ -35,7 +35,7 @@ class ChatEventList extends StatelessWidget {
];
final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0;
final events = timeline.events.filterByVisibleInGui();
final animateInEventIndex = controller.animateInEventIndex;
@ -43,6 +43,7 @@ class ChatEventList extends StatelessWidget {
// ListView's findChildIndexCallback
final thisEventsKeyMap = <String, int>{};
for (var i = 0; i < events.length; i++) {
//print('${events[i].roomId} ${events[i].senderId} ${events[i].body}');
thisEventsKeyMap[events[i].eventId] = i;
}

View File

@ -28,7 +28,7 @@ import '../../utils/stream_extension.dart';
import 'chat_emoji_picker.dart';
import 'chat_input_row.dart';
enum _EventContextAction { info, report }
enum _EventContextAction { info, recover, report }
class ChatView extends StatelessWidget {
final ChatController controller;
@ -81,6 +81,9 @@ class ChatView extends StatelessWidget {
case _EventContextAction.report:
controller.reportEventAction();
break;
case _EventContextAction.recover:
controller.recoverEventAction();
break;
}
},
itemBuilder: (context) => [
@ -95,6 +98,15 @@ class ChatView extends StatelessWidget {
],
),
),
if (controller.selectedEvents.single.redacted)
PopupMenuItem(
value: _EventContextAction.recover,
child: Row(mainAxisSize: MainAxisSize.min, children: [
const Icon(Icons.redo),
const SizedBox(width: 12),
Text(L10n.of(context).recoverMessage),
]),
),
if (controller.selectedEvents.single.status.isSent)
PopupMenuItem(
value: _EventContextAction.report,

View File

@ -38,7 +38,7 @@ class Message extends StatelessWidget {
final bool animateIn;
final void Function()? resetAnimateIn;
final bool wallpaperMode;
final ScrollController scrollController;
final ScrollController? scrollController;
final List<Color> colors;
const Message(
@ -58,7 +58,7 @@ class Message extends StatelessWidget {
this.resetAnimateIn,
this.wallpaperMode = false,
required this.onMention,
required this.scrollController,
this.scrollController,
required this.colors,
super.key,
});
@ -597,13 +597,13 @@ class Message extends StatelessWidget {
class BubbleBackground extends StatelessWidget {
const BubbleBackground({
super.key,
required this.scrollController,
required this.colors,
required this.ignore,
required this.child,
this.scrollController,
});
final ScrollController scrollController;
final ScrollController? scrollController;
final List<Color> colors;
final bool ignore;
final Widget child;
@ -635,6 +635,7 @@ class BubblePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
if (!(context.widget is ScrollView || context.widget is SingleChildScrollView)) return;
final scrollable = _scrollable ??= Scrollable.of(context);
final scrollableBox = scrollable.context.findRenderObject() as RenderBox;
final scrollableRect = Offset.zero & scrollableBox.size;

View File

@ -113,7 +113,7 @@ class MessageContent extends StatelessWidget {
case MessageTypes.Image:
case MessageTypes.Sticker:
if (event.redacted) continue textmessage;
const maxSize = 256.0;
final maxSize = event.messageType == MessageTypes.Image ? 512.0 : 256.0;
final w = event.content
.tryGetMap<String, Object?>('info')
?.tryGet<int>('w');

View File

@ -1,3 +1,4 @@
import 'package:fluffychat/pages/chat/events/html_message.dart';
import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
@ -79,7 +80,7 @@ class MessageDownloadContent extends StatelessWidget {
),
),
),
if (fileDescription != null) ...[
if (fileDescription != null && !event.isRichFileDescription) ...[
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
@ -103,6 +104,27 @@ class MessageDownloadContent extends StatelessWidget {
),
),
],
if (fileDescription != null && event.isRichFileDescription) ...[
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: HtmlMessage(
html: fileDescription,
textColor: textColor,
room: event.room,
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize,
linkStyle: TextStyle(
color: linkColor,
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize,
decoration: TextDecoration.underline,
decorationColor: linkColor,
),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
),
),
],
],
);
}

View File

@ -152,7 +152,7 @@ class InputBar extends StatelessWidget {
}
}
}
final userMatch = RegExp(r'(?:\s|^)@([-\w]+)$').firstMatch(searchText);
final userMatch = RegExp(r'(?:\s|^)@([^ \[\]]+)$', unicode: true).firstMatch(searchText);
if (userMatch != null) {
final userSearch = userMatch[1]!.toLowerCase();
for (final user in room.getParticipants()) {

View File

@ -0,0 +1,67 @@
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/events/message.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class RecoveredEventDialog extends StatefulWidget {
final Event event;
final Timeline timeline;
const RecoveredEventDialog({
required this.event,
required this.timeline,
super.key,
});
@override
RecoveredEventDialogState createState() =>
RecoveredEventDialogState(event, timeline);
}
class RecoveredEventDialogState extends State<RecoveredEventDialog> {
final Event event;
final Timeline timeline;
RecoveredEventDialogState(this.event, this.timeline);
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colors = [
theme.secondaryBubbleColor,
theme.bubbleColor,
];
final message = Message(
event,
colors: colors,
onInfoTab: (Event ev) => {},
onMention: () => {},
onSelect: (Event ev) => {},
onSwipe: () => {},
scrollToEventId: (String p0) => {},
timeline: timeline,
animateIn: false,
displayReadMarker: false,
highlightMarker: false,
longPressSelect: false,
selected: false,
wallpaperMode: false,
);
return Scaffold(
appBar: AppBar(
title: Text(L10n.of(context).recoveredMessage)
),
body: Container(
child: Column(
children: [
message,
],
),
),
);
}
}

View File

@ -1,3 +1,4 @@
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@ -15,16 +16,20 @@ import 'package:fluffychat/utils/size_string.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/dialog_text_field.dart';
import '../../utils/resize_video.dart';
import 'package:matrix/src/utils/markdown.dart';
import 'package:html_unescape/html_unescape.dart';
class SendFileDialog extends StatefulWidget {
final Room room;
final List<XFile> files;
final BuildContext outerContext;
final Event? replyEvent;
const SendFileDialog({
required this.room,
required this.files,
required this.outerContext,
this.replyEvent,
super.key,
});
@ -97,13 +102,40 @@ class SendFileDialogState extends State<SendFileDialog> {
}
final label = _labelTextController.text.trim();
final extraContent = <String, dynamic>{};
if (label.isNotEmpty) {
extraContent['body'] = label;
final html = markdown(
label,
getEmotePacks: () => widget.room.getImagePacksFlat(ImagePackUsage.emoticon),
getMention: widget.room.getMention,
convertLinebreaks: Matrix.of(context).client.convertLinebreaksInFormatting,
);
// if the decoded html is the same as the body, there is no need in sending a formatted message
if (HtmlUnescape()
.convert(html.replaceAll(RegExp(r'<br />\n?'), '\n')) !=
label) {
extraContent['format'] = 'org.matrix.custom.html';
extraContent['formatted_body'] = html;
}
}
if (widget.replyEvent != null) {
extraContent['m.relates_to'] = {
'm.in_reply_to': {
'event_id': widget.replyEvent!.eventId,
},
};
}
try {
await widget.room.sendFileEvent(
file,
thumbnail: thumbnail,
shrinkImageMaxDimension: compress ? 1600 : null,
extraContent: label.isEmpty ? null : {'body': label},
extraContent: extraContent,
);
} on MatrixException catch (e) {
final retryAfterMs = e.retryAfterMs;
@ -128,12 +160,13 @@ class SendFileDialogState extends State<SendFileDialog> {
file,
thumbnail: thumbnail,
shrinkImageMaxDimension: compress ? 1600 : null,
extraContent: label.isEmpty ? null : {'body': label},
extraContent: extraContent,
);
}
}
scaffoldMessenger.clearSnackBars();
} catch (e) {
print('error: ${e.toString()}');
scaffoldMessenger.clearSnackBars();
final theme = Theme.of(context);
scaffoldMessenger.showSnackBar(

View File

@ -37,6 +37,7 @@ class SendLocationDialogState extends State<SendLocationDialog> {
}
Future<void> requestLocation() async {
error = null;
if (!(await Geolocator.isLocationServiceEnabled())) {
setState(() => disabled = true);
return;
@ -58,15 +59,15 @@ class SendLocationDialogState extends State<SendLocationDialog> {
try {
position = await Geolocator.getCurrentPosition(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.best,
timeLimit: Duration(seconds: 30),
accuracy: LocationAccuracy.high,
timeLimit: Duration(seconds: 5),
),
);
} on TimeoutException {
position = await Geolocator.getCurrentPosition(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.medium,
timeLimit: Duration(seconds: 30),
timeLimit: Duration(seconds: 5),
),
);
}
@ -123,6 +124,8 @@ class SendLocationDialogState extends State<SendLocationDialog> {
onPressed: Navigator.of(context, rootNavigator: false).pop,
child: Text(L10n.of(context).cancel),
),
if (error != null)
AdaptiveDialogAction(onPressed: requestLocation, child: Text(L10n.of(context).retry)),
if (position != null)
AdaptiveDialogAction(
onPressed: isSending ? null : sendAction,

View File

@ -27,6 +27,7 @@ class NewGroupController extends State<NewGroup> {
bool publicGroup = false;
bool groupCanBeFound = false;
bool enableEncryption = false;
Uint8List? avatar;
@ -49,6 +50,8 @@ class NewGroupController extends State<NewGroup> {
void setGroupCanBeFound(bool b) => setState(() => groupCanBeFound = b);
void setEnableEncryption(bool b) => setState(() => enableEncryption = b);
void selectPhoto() async {
final photo = await selectFiles(
context,
@ -68,9 +71,7 @@ class NewGroupController extends State<NewGroup> {
final roomId = await Matrix.of(context).client.createGroupChat(
visibility:
groupCanBeFound ? sdk.Visibility.public : sdk.Visibility.private,
preset: publicGroup
? sdk.CreateRoomPreset.publicChat
: sdk.CreateRoomPreset.privateChat,
enableEncryption: enableEncryption,
groupName: nameController.text.isNotEmpty ? nameController.text : null,
initialState: [
if (avatar != null)
@ -78,8 +79,16 @@ class NewGroupController extends State<NewGroup> {
type: sdk.EventTypes.RoomAvatar,
content: {'url': avatarUrl.toString()},
),
sdk.StateEvent(
type: sdk.EventTypes.RoomJoinRules,
content: {'join_rule': publicGroup ? 'public' : 'invite'}
)
],
);
final room = Matrix.of(context).client.getRoomById(roomId);
// if (room != null) {
// room.setJoinRules(publicGroup ? JoinRules.public : JoinRules.invite);
// }
if (!mounted) return;
context.go('/rooms/$roomId/invite');
}

View File

@ -134,8 +134,10 @@ class NewGroupView extends StatelessWidget {
color: theme.colorScheme.onSurface,
),
),
value: !controller.publicGroup,
onChanged: null,
value: controller.enableEncryption,
onChanged: controller.loading
? null
: controller.setEnableEncryption,
),
),
AnimatedSize(

View File

@ -226,7 +226,7 @@ class SettingsStyleView extends StatelessWidget {
vertical: 8,
),
child: Text(
'What is Extera Next?',
'погнали в роблокс?',
style: TextStyle(
color: theme.onBubbleColor,
fontSize: AppConfig.messageFontSize *
@ -259,7 +259,7 @@ class SettingsStyleView extends StatelessWidget {
vertical: 8,
),
child: Text(
'It is another attempt at making Extera, this time it is a fork of FluffyChat, that eliminates a lot of cons of Cinny-based (Web) Extera, like: slow chats loading, laggy animations.',
'Го, в toilet tower defense',
style: TextStyle(
color: theme.colorScheme.onSurface,
fontSize: AppConfig.messageFontSize *

View File

@ -18,7 +18,6 @@ import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/utils/custom_http_client.dart';
import 'package:fluffychat/utils/custom_image_resizer.dart';
import 'package:fluffychat/utils/init_with_restore.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart';
@ -119,7 +118,7 @@ abstract class ClientManager {
},
logLevel: kReleaseMode ? Level.warning : Level.verbose,
databaseBuilder: flutterMatrixSdkDatabaseBuilder,
legacyDatabaseBuilder: FlutterHiveCollectionsDatabase.databaseBuilder,
//legacyDatabaseBuilder: FlutterHiveCollectionsDatabase.databaseBuilder,
supportedLoginTypes: {
AuthenticationTypes.password,
AuthenticationTypes.sso,

View File

@ -28,7 +28,8 @@ extension DateTimeExtension on DateTime {
/// Returns a simple time String.
String localizedTimeOfDay(BuildContext context) =>
L10n.of(context).alwaysUse24HourFormat == 'true'
(MediaQuery.alwaysUse24HourFormatOf(context) ||
L10n.of(context).alwaysUse24HourFormat == 'true')
? DateFormat('HH:mm', L10n.of(context).localeName).format(this)
: DateFormat('h:mm a', L10n.of(context).localeName).format(this);

View File

@ -55,9 +55,5 @@ extension IsStateExtension on Event {
content.tryGet<String>('membership') == 'ban' ||
stateKey != senderId);
bool get isState => !{
EventTypes.Message,
EventTypes.Sticker,
EventTypes.Encrypted,
}.contains(type);
bool get isState => this.stateKey != null;
}

View File

@ -11,7 +11,6 @@ import 'package:universal_html/html.dart' as html;
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/utils/client_manager.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'cipher.dart';
@ -55,7 +54,9 @@ Future<DatabaseApi> flutterMatrixSdkDatabaseBuilder(Client client) async {
Logs().e('Unable to send error notification', e, s);
}
return FlutterHiveCollectionsDatabase.databaseBuilder(client);
database = await _constructDatabase(client);
await database.open();
return database;
}
}

View File

@ -7,6 +7,11 @@ class MatrixLocals extends MatrixLocalizations {
MatrixLocals(this.l10n);
@override
String voiceMessage(String senderName, Duration? duration) {
return l10n.voiceMessage;
}
@override
String acceptedTheInvitation(String targetName) {
return l10n.acceptedTheInvitation(targetName);

View File

@ -0,0 +1,75 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:matrix/matrix.dart' as matrix;
extension SynapseAdmin on matrix.Client {
Future<List<dynamic>> getEventReports({int from = 0, int limit = 10}) async {
final requestUri = Uri(
path: '/_synapse/admin/v1/event_reports',
query: 'from=$from&limit=$limit&order_by=received_ts&dir=b'
);
if (baseUri == null) return [];
print(baseUri!.resolveUri(requestUri).toString());
final request = Request('GET', baseUri!.resolveUri(requestUri));
request.headers['authorization'] = 'Bearer $accessToken';
final response = await httpClient.send(request);
final responseBody = await response.stream.toBytes();
if (response.statusCode != 200) unexpectedResponse(response, responseBody);
final responseString = utf8.decode(responseBody);
final json = jsonDecode(responseString);
return json['event_reports'];
}
Future<matrix.Event?> getReportedEvent(int id) async {
final requestUri = Uri(
path: '/_synapse/admin/v1/event_reports/$id',
);
if (baseUri == null) return null;
final request = Request('GET', baseUri!.resolveUri(requestUri));
request.headers['authorization'] = 'Bearer $accessToken';
final response = await httpClient.send(request);
final responseBody = await response.stream.toBytes();
if (response.statusCode != 200) unexpectedResponse(response, responseBody);
final responseString = utf8.decode(responseBody);
final json = jsonDecode(responseString);
final room = getRoomById(json['room_id']);
if (room == null) return null;
print('Event content: ${jsonEncode(json['event_json'])}');
return new matrix.Event(
content: json['event_json']['content'],
type: json['event_json']['type'],
eventId: json['event_id'],
senderId: json['sender'],
originServerTs: DateTime.fromMillisecondsSinceEpoch(json['event_json']['origin_server_ts']),
room: room,
);
}
Future<bool> isSynapseAdministrator() async {
print('Checking if I am admin...');
print('User ID: $userID');
if (userID == null) return false;
final requestUri = Uri(
path: '/_synapse/admin/v1/users/$userID/admin',
);
print('Base URL: ${baseUri.toString()}');
if (baseUri == null) return false;
final request = Request('GET', baseUri!.resolveUri(requestUri));
request.headers['authorization'] = 'Bearer $accessToken';
final response = await httpClient.send(request);
final responseBody = await response.stream.toBytes();
if (response.statusCode != 200) unexpectedResponse(response, responseBody);
final responseString = utf8.decode(responseBody);
final json = jsonDecode(responseString);
print('Response from endpoint: $responseString');
return json['admin'];
}
}

View File

@ -53,11 +53,11 @@ static void my_application_activate(GApplication* application) {
if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "FluffyChat");
gtk_header_bar_set_title(header_bar, "Extera Next");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else {
gtk_window_set_title(window, "FluffyChat");
gtk_window_set_title(window, "Extera Next");
}
gtk_window_set_default_size(window, 864, 720);

View File

@ -1159,10 +1159,10 @@ packages:
dependency: "direct main"
description:
name: matrix
sha256: "829f225f74b2a8c83b6b1f960004ecf742215d9ee9d8cc9e0f1974ff8e281f0f"
sha256: "7d15fdbc760be7e40c58bb65e03baa8241b1e31db2bc67dab61883aabc083a85"
url: "https://pub.dev"
source: hosted
version: "0.39.1"
version: "0.40.0"
meta:
dependency: transitive
description:

View File

@ -62,7 +62,7 @@ dependencies:
latlong2: ^0.9.1
linkify: ^5.0.0
material: ^1.0.0+2
matrix: ^0.39.1
matrix: 0.40.0
mime: ^1.0.6
native_imaging: ^0.2.0
opus_caf_converter_dart: ^1.0.1

View File

@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"FluffyChat", origin, size)) {
if (!window.CreateAndShow(L"Extera Next", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);