This commit is contained in:
OfficialDakari 2025-11-06 11:16:28 +05:00
commit 165d222726
85 changed files with 2966 additions and 474 deletions

View File

@ -6,6 +6,11 @@
"longPressToRecordVoiceMessage": "Long press to record voice message.",
"pause": "Pause",
"resume": "Resume",
"newSubSpace": "New sub space",
"moveToDifferentSpace": "Move to different space",
"moveUp": "Move up",
"moveDown": "Move down",
"removeFromSpaceDescription": "The chat will be removed from the space but still appear in your chat list.",
"endPoll": "End poll",
"anonymousPoll": "Anonymous",
"publicPoll": "Public",
@ -16,6 +21,7 @@
"vote": "Vote",
"changeVote": "Re-vote",
"choicesSelected": "{selected} of {max} selected",
"discuss": "Discuss",
"@choicesSelected": {
"type": "String",
"placeholders": {
@ -893,6 +899,9 @@
},
"chatPermissions": "Chat permissions",
"@chatPermissions": {},
"chatThreads": "Threads",
"@chatThreads": {},
"chatThreadsDescription": "See all threads in this room",
"editDisplayname": "Edit displayname",
"@editDisplayname": {
"type": "String",
@ -3145,6 +3154,24 @@
},
"restricted": "Restricted",
"@restricted": {},
"spaceMemberOf": "Space member of {spaces}",
"@spaceMemberOf": {
"type": "String",
"placeholders": {
"spaces": {
"type": "String"
}
}
},
"spaceMemberOfCanKnock": "Space member of {spaces} can knock",
"@spaceMemberOfCanKnock": {
"type": "String",
"placeholders": {
"spaces": {
"type": "String"
}
}
},
"knockRestricted": "Knock restricted",
"@knockRestricted": {},
"goToSpace": "Go to space: {space}",

View File

@ -6,6 +6,11 @@
"noMessagesYet": "Нет сообщений",
"longPressToRecordVoiceMessage": "Зажмите, чтобы записать голосовое сообщение.",
"pause": "Пауза",
"newSubSpace": "Новое подпространство",
"moveToDifferentSpace": "Перенести в другое пространство",
"moveUp": "Вверх",
"moveDown": "Вниз",
"removeFromSpaceDescription": "Эта комната будет удалена из пространства, но останется в Вашем списке чатов.",
"resume": "Продолжить",
"anonymousPoll": "Анонимный",
"publicPoll": "Открытый",
@ -16,6 +21,7 @@
"vote": "Голосовать",
"changeVote": "Изменить ответ",
"choicesSelected": "Выбрано {selected} из {max}",
"discuss": "Обсудить",
"@choicesSelected": {
"type": "String",
"placeholders": {
@ -41,7 +47,7 @@
"type": "String",
"placeholders": {}
},
"cleanExifDescription": "Удалять метаданные EXIF (модель камеры, геолокация, время) из изображений еред отправкой.",
"cleanExifDescription": "Удалять метаданные EXIF (модель камеры, геолокация, время) из изображений перед отправкой.",
"@cleanExifDescription": {
"type": "String",
"placeholders": {}
@ -892,6 +898,9 @@
},
"chatPermissions": "Права в чате",
"@chatPermissions": {},
"chatThreads": "Обсуждения",
"@chatThreads": {},
"chatThreadsDescription": "Список всех обсуждений в этой комнате",
"editDisplayname": "Отображаемое имя",
"@editDisplayname": {
"type": "String",
@ -936,7 +945,7 @@
"@globalChatId": {},
"accessAndVisibility": "Доступность и видимость",
"@accessAndVisibility": {},
"accessAndVisibilityDescription": "Кто может зайти и как найти этот чат.",
"accessAndVisibilityDescription": "Кто может зайти и как найти этот чат",
"@accessAndVisibilityDescription": {},
"calls": "Звонки",
"@calls": {},
@ -3145,6 +3154,25 @@
"space": {}
},
"markAsUnread": "Отметить как непрочитанное",
"spaceMemberOf": "Участники пространства {spaces}",
"@spaceMemberOf": {
"type": "String",
"placeholders": {
"spaces": {
"type": "String"
}
}
},
"spaceMemberOfCanKnock": "Участники пространства {spaces} (по запросу)",
"@spaceMemberOfCanKnock": {
"type": "String",
"placeholders": {
"spaces": {
"type": "String"
}
}
},
"userLevel": "{level} - Пользователь",
"@userLevel": {
"type": "String",

View File

@ -5,5 +5,4 @@ output-class: L10n
preferred-supported-locales: ["en"]
use-deferred-loading: true
nullable-getter: false
synthetic-package: false
output-dir: lib/generated/l10n

View File

@ -15,6 +15,8 @@ abstract class AppConfig {
static bool enableGradient = true;
static bool cleanExif = true;
static String? httpProxy;
static String get defaultHomeserver => _defaultHomeserver;
static double fontSizeFactor = 1;
static const Color chatColor = primaryColor;

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:extera_next/pages/chat_thread/thread.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
@ -148,6 +149,27 @@ abstract class AppRoutes {
),
),
redirect: loggedOutRedirect,
routes: [
GoRoute(
path: 'threads',
redirect: loggedOutRedirect,
routes: [
GoRoute(
path: ':threadroot',
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
ThreadPage(
roomId: state.pathParameters['roomid']!,
threadRootEventId:
state.pathParameters['threadroot']!,
eventId: state.uri.queryParameters['event'],
),
),
),
],
),
],
),
],
redirect: loggedOutRedirect,
@ -347,11 +369,31 @@ abstract class AppRoutes {
roomId: state.pathParameters['roomid']!,
shareItems: shareItems,
eventId: state.uri.queryParameters['event'],
showThreadRoots: state.uri.queryParameters['threads'] == 'true',
),
);
},
redirect: loggedOutRedirect,
routes: [
GoRoute(
path: 'threads',
redirect: loggedOutRedirect,
routes: [
GoRoute(
path: ':threadroot',
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
ThreadPage(
roomId: state.pathParameters['roomid']!,
threadRootEventId:
state.pathParameters['threadroot']!,
eventId: state.uri.queryParameters['event'],
),
),
),
],
),
GoRoute(
path: 'search',
pageBuilder: (context, state) => defaultPageBuilder(

View File

@ -1,6 +1,7 @@
import 'package:shared_preferences/shared_preferences.dart';
abstract class SettingKeys {
static const String httpProxy = 'xyz.extera.next.httpProxy';
static const String cleanExif = 'xyz.extera.next.cleanExif';
static const String displayNavigationRail = 'chat.fluffy.displayNavigationRail';
static const String hideAvatarsInInvites = 'xyz.extera.next.hideAvatarsInInvites';

View File

@ -223,6 +223,36 @@ abstract class L10n {
/// **'Resume'**
String get resume;
/// No description provided for @newSubSpace.
///
/// In en, this message translates to:
/// **'New sub space'**
String get newSubSpace;
/// No description provided for @moveToDifferentSpace.
///
/// In en, this message translates to:
/// **'Move to different space'**
String get moveToDifferentSpace;
/// No description provided for @moveUp.
///
/// In en, this message translates to:
/// **'Move up'**
String get moveUp;
/// No description provided for @moveDown.
///
/// In en, this message translates to:
/// **'Move down'**
String get moveDown;
/// No description provided for @removeFromSpaceDescription.
///
/// In en, this message translates to:
/// **'The chat will be removed from the space but still appear in your chat list.'**
String get removeFromSpaceDescription;
/// No description provided for @endPoll.
///
/// In en, this message translates to:
@ -283,6 +313,12 @@ abstract class L10n {
/// **'{selected} of {max} selected'**
String choicesSelected(int selected, int max);
/// No description provided for @discuss.
///
/// In en, this message translates to:
/// **'Discuss'**
String get discuss;
/// No description provided for @pollType.
///
/// In en, this message translates to:
@ -1311,6 +1347,18 @@ abstract class L10n {
/// **'Chat permissions'**
String get chatPermissions;
/// No description provided for @chatThreads.
///
/// In en, this message translates to:
/// **'Threads'**
String get chatThreads;
/// No description provided for @chatThreadsDescription.
///
/// In en, this message translates to:
/// **'See all threads in this room'**
String get chatThreadsDescription;
/// No description provided for @editDisplayname.
///
/// In en, this message translates to:
@ -4353,6 +4401,18 @@ abstract class L10n {
/// **'Restricted'**
String get restricted;
/// No description provided for @spaceMemberOf.
///
/// In en, this message translates to:
/// **'Space member of {spaces}'**
String spaceMemberOf(String spaces);
/// No description provided for @spaceMemberOfCanKnock.
///
/// In en, this message translates to:
/// **'Space member of {spaces} can knock'**
String spaceMemberOfCanKnock(String spaces);
/// No description provided for @knockRestricted.
///
/// In en, this message translates to:

View File

@ -24,6 +24,22 @@ class L10nAr extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nAr extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -656,6 +675,12 @@ class L10nAr extends L10n {
@override
String get chatPermissions => 'صلاحيات المحادثة';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'حرر الاسم العلني';
@ -2406,6 +2431,16 @@ class L10nAr extends L10n {
@override
String get restricted => 'مقيد';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'قيود النقر';

View File

@ -24,6 +24,22 @@ class L10nBe extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nBe extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -660,6 +679,12 @@ class L10nBe extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edit displayname';
@ -2421,6 +2446,16 @@ class L10nBe extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nBn extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nBn extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -660,6 +679,12 @@ class L10nBn extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edit displayname';
@ -2421,6 +2446,16 @@ class L10nBn extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nBo extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nBo extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -660,6 +679,12 @@ class L10nBo extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edit displayname';
@ -2421,6 +2446,16 @@ class L10nBo extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nCa extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nCa extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -665,6 +684,12 @@ class L10nCa extends L10n {
@override
String get chatPermissions => 'Permisos del xat';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edita l\'àlies';
@ -2445,6 +2470,16 @@ class L10nCa extends L10n {
@override
String get restricted => 'Restringit';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'No es pot picar a la porta';

View File

@ -24,6 +24,22 @@ class L10nCs extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nCs extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -664,6 +683,12 @@ class L10nCs extends L10n {
@override
String get chatPermissions => 'Oprávnění konverzace';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Změnit přezdívku';
@ -2425,6 +2450,16 @@ class L10nCs extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nDe extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nDe extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -666,6 +685,12 @@ class L10nDe extends L10n {
@override
String get chatPermissions => 'Chatberechtigungen';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Anzeigename ändern';
@ -2440,6 +2465,16 @@ class L10nDe extends L10n {
@override
String get restricted => 'Beschränkt';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Anklopfen beschränkt';

View File

@ -24,6 +24,22 @@ class L10nEl extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nEl extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -661,6 +680,12 @@ class L10nEl extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edit displayname';
@ -2422,6 +2447,16 @@ class L10nEl extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nEn extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nEn extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -660,6 +679,12 @@ class L10nEn extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edit displayname';
@ -2421,6 +2446,16 @@ class L10nEn extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nEo extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nEo extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -663,6 +682,12 @@ class L10nEo extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Redakti prezentan nomon';
@ -2428,6 +2453,16 @@ class L10nEo extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nEs extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nEs extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -664,6 +683,12 @@ class L10nEs extends L10n {
@override
String get chatPermissions => 'Permisos del chat';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Editar nombre visible';
@ -2444,6 +2469,16 @@ class L10nEs extends L10n {
@override
String get restricted => 'Restringido';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Aviso restringido';

View File

@ -24,6 +24,22 @@ class L10nEt extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nEt extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -662,6 +681,12 @@ class L10nEt extends L10n {
@override
String get chatPermissions => 'Vestluse õigused';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Muuda kuvatavat nime';
@ -2439,6 +2464,16 @@ class L10nEt extends L10n {
@override
String get restricted => 'Piiratud';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Koputa piiratud ligipääsuga jututoa uksele';

View File

@ -24,6 +24,22 @@ class L10nEu extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nEu extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -663,6 +682,12 @@ class L10nEu extends L10n {
@override
String get chatPermissions => 'Txataren baimenak';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Editatu ezizena';
@ -2431,6 +2456,16 @@ class L10nEu extends L10n {
@override
String get restricted => 'Mugatuta';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Eskatu baimena sarrera mugatua duen txatean';

View File

@ -24,6 +24,22 @@ class L10nFa extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nFa extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -658,6 +677,12 @@ class L10nFa extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'ویرایش نام نمایشی';
@ -2424,6 +2449,16 @@ class L10nFa extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nFi extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nFi extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -665,6 +684,12 @@ class L10nFi extends L10n {
@override
String get chatPermissions => 'Keskustelun oikeudet';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Muokkaa näyttönimeä';
@ -2430,6 +2455,16 @@ class L10nFi extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nFil extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nFil extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -666,6 +685,12 @@ class L10nFil extends L10n {
@override
String get chatPermissions => 'Mga pahintulot ng chat';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'I-edit ang display name';
@ -2427,6 +2452,16 @@ class L10nFil extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nFr extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nFr extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -669,6 +688,12 @@ class L10nFr extends L10n {
@override
String get chatPermissions => 'Permissions du salon';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Changer de nom d\'affichage';
@ -2465,6 +2490,16 @@ class L10nFr extends L10n {
@override
String get restricted => 'Limité';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Frapper à la porte limité';

View File

@ -24,6 +24,22 @@ class L10nGa extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nGa extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -668,6 +687,12 @@ class L10nGa extends L10n {
@override
String get chatPermissions => 'Ceadanna comhrá';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Cuir ainm taispeána in eagar';
@ -2450,6 +2475,16 @@ class L10nGa extends L10n {
@override
String get restricted => 'Srianta';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Cnoc Mhuire srianta';

View File

@ -24,6 +24,22 @@ class L10nGl extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nGl extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -663,6 +682,12 @@ class L10nGl extends L10n {
@override
String get chatPermissions => 'Permisos da conversa';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Editar nome público';
@ -2433,6 +2458,16 @@ class L10nGl extends L10n {
@override
String get restricted => 'Non accesible';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Peta á porta';

View File

@ -24,6 +24,22 @@ class L10nHe extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nHe extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -658,6 +677,12 @@ class L10nHe extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'ערוך את שם התצוגה';
@ -2416,6 +2441,16 @@ class L10nHe extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nHi extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nHi extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -660,6 +679,12 @@ class L10nHi extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edit displayname';
@ -2421,6 +2446,16 @@ class L10nHi extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nHr extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nHr extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -661,6 +680,12 @@ class L10nHr extends L10n {
@override
String get chatPermissions => 'Dozvole za razgovor';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Uredi prikazano ime';
@ -2430,6 +2455,16 @@ class L10nHr extends L10n {
@override
String get restricted => 'Ograničeni';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Pokucaj na ograničene sobe';

View File

@ -24,6 +24,22 @@ class L10nHu extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nHu extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -663,6 +682,12 @@ class L10nHu extends L10n {
@override
String get chatPermissions => 'Csevegés engedélyek';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Megjelenítési név szerkesztése';
@ -2431,6 +2456,16 @@ class L10nHu extends L10n {
@override
String get restricted => 'Korlátozott';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Kopogás korlátozva';

View File

@ -24,6 +24,22 @@ class L10nIa extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nIa extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -660,6 +679,12 @@ class L10nIa extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edit displayname';
@ -2421,6 +2446,16 @@ class L10nIa extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nId extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nId extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -667,6 +686,12 @@ class L10nId extends L10n {
@override
String get chatPermissions => 'Perizinan obrolan';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edit nama tampilan';
@ -2438,6 +2463,16 @@ class L10nId extends L10n {
@override
String get restricted => 'Dibatasi';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Ketukan dibatasi';

View File

@ -24,6 +24,22 @@ class L10nIe extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nIe extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -659,6 +678,12 @@ class L10nIe extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Redacter li visibil nómine';
@ -2420,6 +2445,16 @@ class L10nIe extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nIt extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nIt extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -664,6 +683,12 @@ class L10nIt extends L10n {
@override
String get chatPermissions => 'Permessi della chat';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Modifica il nominativo';
@ -2445,6 +2470,16 @@ class L10nIt extends L10n {
@override
String get restricted => 'Limitato';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Limitato al bussare';

View File

@ -24,6 +24,22 @@ class L10nJa extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nJa extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -651,6 +670,12 @@ class L10nJa extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => '表示名を編集';
@ -2390,6 +2415,16 @@ class L10nJa extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nKa extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nKa extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -662,6 +681,12 @@ class L10nKa extends L10n {
@override
String get chatPermissions => 'ჩატის უფლებები';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'ნაჩვენები სახელის შეცვლა';
@ -2423,6 +2448,16 @@ class L10nKa extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nKo extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nKo extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -649,6 +668,12 @@ class L10nKo extends L10n {
@override
String get chatPermissions => '채팅 권한';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => '표시 이름 수정';
@ -2372,6 +2397,16 @@ class L10nKo extends L10n {
@override
String get restricted => '스페이스 멤버로 제한';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => '스페이스 멤버만 참가 요청 가능';

View File

@ -24,6 +24,22 @@ class L10nLt extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nLt extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -662,6 +681,12 @@ class L10nLt extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Redaguoti rodomą vardą';
@ -2427,6 +2452,16 @@ class L10nLt extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nLv extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nLv extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -666,6 +685,12 @@ class L10nLv extends L10n {
@override
String get chatPermissions => 'Tērzēšanas atļaujas';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Labot attēlojamo vārdu';
@ -2438,6 +2463,16 @@ class L10nLv extends L10n {
@override
String get restricted => 'Ierobežots';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Pieklauvēt ierobežotajiem';

View File

@ -24,6 +24,22 @@ class L10nNb extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nNb extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -660,6 +679,12 @@ class L10nNb extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Rediger visningsnavn';
@ -2424,6 +2449,16 @@ class L10nNb extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nNl extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nNl extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -662,6 +681,12 @@ class L10nNl extends L10n {
@override
String get chatPermissions => 'Chat toestemmingen';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Naam wijzigen';
@ -2427,6 +2452,16 @@ class L10nNl extends L10n {
@override
String get restricted => 'Beperkt';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Kloppen is beperkt';

View File

@ -24,6 +24,22 @@ class L10nPl extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nPl extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -663,6 +682,12 @@ class L10nPl extends L10n {
@override
String get chatPermissions => 'Uprawnienia w czacie';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edytuj nazwę wyświetlaną';
@ -2437,6 +2462,16 @@ class L10nPl extends L10n {
@override
String get restricted => 'Ograniczone';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Pukanie jest ograniczone';

View File

@ -24,6 +24,22 @@ class L10nPt extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nPt extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -660,6 +679,12 @@ class L10nPt extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edit displayname';
@ -2421,6 +2446,16 @@ class L10nPt extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nRo extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nRo extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -665,6 +684,12 @@ class L10nRo extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Schimbați displayname';
@ -2438,6 +2463,16 @@ class L10nRo extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nRu extends L10n {
@override
String get resume => 'Продолжить';
@override
String get newSubSpace => 'Новое подпространство';
@override
String get moveToDifferentSpace => 'Перенести в другое пространство';
@override
String get moveUp => 'Вверх';
@override
String get moveDown => 'Вниз';
@override
String get removeFromSpaceDescription =>
'Эта комната будет удалена из пространства, но останется в Вашем списке чатов.';
@override
String get endPoll => 'Завершить опрос';
@ -56,6 +72,9 @@ class L10nRu extends L10n {
return 'Выбрано $selected из $max';
}
@override
String get discuss => 'Обсудить';
@override
String get pollType => 'Тип опроса';
@ -88,7 +107,7 @@ class L10nRu extends L10n {
@override
String get cleanExifDescription =>
'Удалять метаданные EXIF (модель камеры, геолокация, время) из изображений еред отправкой.';
'Удалять метаданные EXIF (модель камеры, геолокация, время) из изображений перед отправкой.';
@override
String get repeatPassword => 'Повторите пароль';
@ -662,6 +681,12 @@ class L10nRu extends L10n {
@override
String get chatPermissions => 'Права в чате';
@override
String get chatThreads => 'Обсуждения';
@override
String get chatThreadsDescription => 'Список всех обсуждений в этой комнате';
@override
String get editDisplayname => 'Отображаемое имя';
@ -695,7 +720,7 @@ class L10nRu extends L10n {
@override
String get accessAndVisibilityDescription =>
'Кто может зайти и как найти этот чат.';
'Кто может зайти и как найти этот чат';
@override
String get calls => 'Звонки';
@ -2426,6 +2451,16 @@ class L10nRu extends L10n {
@override
String get restricted => 'Ограничено';
@override
String spaceMemberOf(String spaces) {
return 'Участники пространства $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Участники пространства $spaces (по запросу)';
}
@override
String get knockRestricted => 'Ограничено + по запросу';

View File

@ -24,6 +24,22 @@ class L10nSk extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nSk extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -662,6 +681,12 @@ class L10nSk extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Zmeniť prezývku';
@ -2422,6 +2447,16 @@ class L10nSk extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nSl extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nSl extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -664,6 +683,12 @@ class L10nSl extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edit displayname';
@ -2425,6 +2450,16 @@ class L10nSl extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nSr extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nSr extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -660,6 +679,12 @@ class L10nSr extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Уреди име за приказ';
@ -2417,6 +2442,16 @@ class L10nSr extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nSv extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nSv extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -661,6 +680,12 @@ class L10nSv extends L10n {
@override
String get chatPermissions => 'Chatt-behörigheter';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Ändra visningsnamn';
@ -2431,6 +2456,16 @@ class L10nSv extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nTa extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nTa extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -670,6 +689,12 @@ class L10nTa extends L10n {
@override
String get chatPermissions => 'அரட்டை அனுமதிகள்';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'காட்சி பெயர் திருத்து';
@ -2452,6 +2477,16 @@ class L10nTa extends L10n {
@override
String get restricted => 'தடைசெய்யப்பட்டது';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'நாக் தடை';

View File

@ -24,6 +24,22 @@ class L10nTe extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nTe extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -660,6 +679,12 @@ class L10nTe extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edit displayname';
@ -2421,6 +2446,16 @@ class L10nTe extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nTh extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nTh extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -662,6 +681,12 @@ class L10nTh extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Edit displayname';
@ -2423,6 +2448,16 @@ class L10nTh extends L10n {
@override
String get restricted => 'Restricted';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nTr extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nTr extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -662,6 +681,12 @@ class L10nTr extends L10n {
@override
String get chatPermissions => 'Sohbet izinleri';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Görünen adı düzenle';
@ -2431,6 +2456,16 @@ class L10nTr extends L10n {
@override
String get restricted => 'Kısıtlı';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Tıklatma kısıtlı';

View File

@ -24,6 +24,22 @@ class L10nUk extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nUk extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -664,6 +683,12 @@ class L10nUk extends L10n {
@override
String get chatPermissions => 'Дозволи бесіди';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Змінити показуване ім\'я';
@ -2434,6 +2459,16 @@ class L10nUk extends L10n {
@override
String get restricted => 'Обмежено';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Стук обмежено';

View File

@ -24,6 +24,22 @@ class L10nVi extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nVi extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -660,6 +679,12 @@ class L10nVi extends L10n {
@override
String get chatPermissions => 'Chat permissions';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => 'Sửa tên hiển thị';
@ -2420,6 +2445,16 @@ class L10nVi extends L10n {
@override
String get restricted => 'Bị hạn chế';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => 'Knock restricted';

View File

@ -24,6 +24,22 @@ class L10nZh extends L10n {
@override
String get resume => 'Resume';
@override
String get newSubSpace => 'New sub space';
@override
String get moveToDifferentSpace => 'Move to different space';
@override
String get moveUp => 'Move up';
@override
String get moveDown => 'Move down';
@override
String get removeFromSpaceDescription =>
'The chat will be removed from the space but still appear in your chat list.';
@override
String get endPoll => 'End poll';
@ -56,6 +72,9 @@ class L10nZh extends L10n {
return '$selected of $max selected';
}
@override
String get discuss => 'Discuss';
@override
String get pollType => 'Poll type';
@ -644,6 +663,12 @@ class L10nZh extends L10n {
@override
String get chatPermissions => '聊天权限';
@override
String get chatThreads => 'Threads';
@override
String get chatThreadsDescription => 'See all threads in this room';
@override
String get editDisplayname => '编辑昵称';
@ -2346,6 +2371,16 @@ class L10nZh extends L10n {
@override
String get restricted => '受限';
@override
String spaceMemberOf(String spaces) {
return 'Space member of $spaces';
}
@override
String spaceMemberOfCanKnock(String spaces) {
return 'Space member of $spaces can knock';
}
@override
String get knockRestricted => '“请求加入”请求受限';

View File

@ -51,12 +51,14 @@ class ChatPage extends StatelessWidget {
final String roomId;
final List<ShareItem>? shareItems;
final String? eventId;
final bool? showThreadRoots;
const ChatPage({
super.key,
required this.roomId,
this.eventId,
this.shareItems,
this.showThreadRoots,
});
@override
@ -79,20 +81,25 @@ class ChatPage extends StatelessWidget {
room: room,
shareItems: shareItems,
eventId: eventId,
showThreadRoots: showThreadRoots,
);
}
}
class ChatPageWithRoom extends StatefulWidget {
final Room room;
final Thread? thread;
final List<ShareItem>? shareItems;
final String? eventId;
final bool? showThreadRoots;
const ChatPageWithRoom({
super.key,
required this.room,
this.thread,
this.shareItems,
this.eventId,
this.showThreadRoots,
});
@override
@ -102,6 +109,10 @@ class ChatPageWithRoom extends StatefulWidget {
class ChatController extends State<ChatPageWithRoom>
with WidgetsBindingObserver {
Room get room => sendingClient.getRoomById(roomId) ?? widget.room;
bool get showThreadRoots => (widget.showThreadRoots ?? false);
Thread? get thread =>
sendingClient.getRoomById(roomId)?.threads[threadRootEventId] ??
widget.room.threads[threadRootEventId];
late Client sendingClient;
@ -110,6 +121,7 @@ class ChatController extends State<ChatPageWithRoom>
late final String readMarkerEventId;
String get roomId => widget.room.id;
String? get threadRootEventId => widget.thread?.rootEvent.eventId;
final AutoScrollController scrollController = AutoScrollController();
@ -134,6 +146,7 @@ class ChatController extends State<ChatPageWithRoom>
builder: (c) => SendFileDialog(
files: details.files,
room: room,
thread: thread,
replyEvent: replyEvent,
outerContext: context,
),
@ -274,6 +287,7 @@ class ChatController extends State<ChatPageWithRoom>
builder: (c) => SendFileDialog(
files: files,
room: room,
thread: thread,
outerContext: context,
replyEvent: replyEvent,
),
@ -317,11 +331,14 @@ class ChatController extends State<ChatPageWithRoom>
if (kIsWeb) {
onFocusSub = html.window.onFocus.listen((_) => setReadMarker());
}
_getThreads();
}
void _tryLoadTimeline() async {
final initialEventId = widget.eventId;
loadTimelineFuture = _getTimeline();
Logs().v("Trying to load timeline...");
try {
await loadTimelineFuture;
if (initialEventId != null) scrollToEventId(initialEventId);
@ -369,13 +386,32 @@ class ChatController extends State<ChatPageWithRoom>
scrollUpBannerEventId = eventId;
});
void updateView() {
Future<void> updateView() async {
if (!mounted) return;
setReadMarker();
await updateThreads();
setState(() {});
}
Future<void> updateThreads() async {
if (timeline?.events == null) return;
final lastEvent = timeline?.events[timeline!.events.length - 1];
if (lastEvent == null) return;
if (lastEvent.relationshipType == RelationshipTypes.thread &&
lastEvent.relationshipEventId != null) {
final thread = await room.client.database
.getThread(room.id, lastEvent.relationshipEventId!, room.client);
if (thread != null) {
setState(() {
threads?[lastEvent.eventId] = thread;
});
}
}
}
Future<void>? loadTimelineFuture;
Map<String, Thread>? threads = {};
int? animateInEventIndex;
@ -384,15 +420,7 @@ class ChatController extends State<ChatPageWithRoom>
animateInEventIndex = i;
}
Future<void> _getTimeline({
String? eventContextId,
}) async {
await Matrix.of(context).client.roomsLoading;
await Matrix.of(context).client.accountDataLoading;
if (eventContextId != null &&
(!eventContextId.isValidMatrixId || eventContextId.sigil != '\$')) {
eventContextId = null;
}
Future<void> _loadRoomTimeline({String? eventContextId}) async {
try {
timeline?.cancelSubscriptions();
timeline = await room.getTimeline(
@ -412,12 +440,72 @@ class ChatController extends State<ChatPageWithRoom>
_showScrollUpMaterialBanner(eventContextId!);
}
}
}
Future<void> _loadThreadTimeline({String? eventContextId}) async {
if (thread == null) {
throw Exception(
"_loadThreadTimeline should not be called, thread == null",
);
}
try {
timeline?.cancelSubscriptions();
timeline = await thread!.getTimeline(
onUpdate: updateView,
eventContextId: eventContextId,
onInsert: onInsert,
);
Logs().v("Thread timeline loaded");
} catch (e, s) {
Logs().w(
'Unable to load timeline on event ID $eventContextId (in thread)',
e,
s,
);
if (!mounted) return;
timeline = await thread!.getTimeline(
onUpdate: updateView,
onInsert: onInsert,
);
if (!mounted) return;
if (e is TimeoutException || e is IOException) {
_showScrollUpMaterialBanner(eventContextId!);
}
}
if (timeline is ThreadTimeline) {
(timeline as ThreadTimeline).getThreadEvents();
}
}
Future<void> _getTimeline({
String? eventContextId,
}) async {
await Matrix.of(context).client.roomsLoading;
await Matrix.of(context).client.accountDataLoading;
if (eventContextId != null &&
(!eventContextId.isValidMatrixId || eventContextId.sigil != '\$')) {
eventContextId = null;
}
if (thread == null) {
await _loadRoomTimeline(eventContextId: eventContextId);
} else {
await _loadThreadTimeline(eventContextId: eventContextId);
}
timeline!.requestKeys(onlineKeyBackupOnly: false);
if (room.markedUnread) room.markUnread(false);
return;
}
Future<void> _getThreads() async {
try {
threads = await room.getThreads();
Logs().w('Thread amount: ${threads?.length}');
} catch (e, s) {
Logs().w('Unable to load threads in $roomId', e, s);
}
}
String? scrollToEventIdMarker;
@override
@ -459,10 +547,14 @@ class ChatController extends State<ChatPageWithRoom>
.then((_) {
_setReadMarkerFuture = null;
});
if (timeline is RoomTimeline) {
if (eventId == null || eventId == timeline.room.lastEvent?.eventId) {
Matrix.of(context).backgroundPush?.cancelNotification(roomId);
}
}
// TODO same for Threads
}
@override
void dispose() {
@ -533,6 +625,9 @@ class ChatController extends State<ChatPageWithRoom>
inReplyTo: replyEvent,
editEventId: editEvent?.eventId,
parseCommands: parseCommands,
threadRootEventId: thread?.rootEvent.eventId,
threadLastEventId:
thread?.lastEvent?.eventId ?? thread?.rootEvent.eventId,
);
sendController.value = TextEditingValue(
text: pendingText,
@ -551,7 +646,12 @@ class ChatController extends State<ChatPageWithRoom>
void sendPollAction() async {
await showAdaptiveDialog(
context: context,
builder: (c) => SendPollDialog(room: room, outerContext: context));
builder: (c) => SendPollDialog(
room: room,
thread: thread,
outerContext: context,
),
);
replyEvent = null;
}
@ -570,6 +670,7 @@ class ChatController extends State<ChatPageWithRoom>
builder: (c) => SendFileDialog(
files: files,
room: room,
thread: thread,
outerContext: context,
replyEvent: replyEvent,
),
@ -584,6 +685,7 @@ class ChatController extends State<ChatPageWithRoom>
builder: (c) => SendFileDialog(
files: [XFile.fromData(image)],
room: room,
thread: thread,
outerContext: context,
),
);
@ -600,6 +702,7 @@ class ChatController extends State<ChatPageWithRoom>
builder: (c) => SendFileDialog(
files: [file],
room: room,
thread: thread,
outerContext: context,
),
);
@ -619,6 +722,7 @@ class ChatController extends State<ChatPageWithRoom>
builder: (c) => SendFileDialog(
files: [file],
room: room,
thread: thread,
outerContext: context,
),
);
@ -645,7 +749,8 @@ class ChatController extends State<ChatPageWithRoom>
name: fileName ?? audioFile.path,
);
await room.sendFileEvent(
await room
.sendFileEvent(
file,
inReplyTo: replyEvent,
extraContent: {
@ -659,7 +764,10 @@ class ChatController extends State<ChatPageWithRoom>
'waveform': waveform,
},
},
).catchError((e) {
threadLastEventId: thread?.lastEvent?.eventId,
threadRootEventId: thread?.rootEvent.eventId,
)
.catchError((e) {
scaffoldMessenger.showSnackBar(
SnackBar(
content: Text(
@ -698,7 +806,10 @@ class ChatController extends State<ChatPageWithRoom>
void sendLocationAction() async {
await showAdaptiveDialog(
context: context,
builder: (c) => SendLocationDialog(room: room),
builder: (c) => SendLocationDialog(
room: room,
thread: thread,
),
);
}
@ -744,7 +855,8 @@ class ChatController extends State<ChatPageWithRoom>
final reports = await mx.client.getEventReports();
final report = reports.firstWhere(
(rep) => rep['room_id'] == roomId && rep['event_id'] == event.eventId);
(rep) => rep['room_id'] == roomId && rep['event_id'] == event.eventId,
);
final recoveredEvent = await mx.client.getReportedEvent(report['id']);
if (recoveredEvent == null) {
@ -754,7 +866,8 @@ class ChatController extends State<ChatPageWithRoom>
return;
}
Navigator.of(context).push(new MaterialPageRoute(
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext ctx) {
return RecoveredEventDialog(
event: recoveredEvent,
@ -762,7 +875,8 @@ class ChatController extends State<ChatPageWithRoom>
);
},
fullscreenDialog: true,
));
),
);
}
void translateEventAction() async {
@ -774,10 +888,12 @@ class ChatController extends State<ChatPageWithRoom>
}
final event = selectedEvents.single;
var text = event.isRichMessage ? event.formattedText : event.text;
var content = {...event.content};
final content = {...event.content};
try {
text = await Translator.translate(
text, PlatformDispatcher.instance.locale.languageCode);
text,
PlatformDispatcher.instance.locale.languageCode,
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).errorTranslatingMessage)),
@ -790,19 +906,24 @@ class ChatController extends State<ChatPageWithRoom>
content['body'] = text;
}
content['xyz.extera.translated'] = true;
Navigator.of(context).push(new MaterialPageRoute(
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext ctx) {
return TranslatedEventDialog(
event: new Event(
event: Event(
content: content,
type: 'm.room.message',
eventId: event.eventId,
senderId: event.senderId,
originServerTs: event.originServerTs,
room: room),
timeline: timeline!);
room: room,
),
timeline: timeline!,
);
},
fullscreenDialog: true));
fullscreenDialog: true,
),
);
}
void reportEventAction() async {
@ -871,20 +992,43 @@ class ChatController extends State<ChatPageWithRoom>
}
}
void discussAction() async {
final event = selectedEvents.first;
if (!room.threads.containsKey(event.eventId)) {
room.threads[event.eventId] = Thread(
room: room,
rootEvent: event,
client: room.client,
currentUserParticipated: false,
count: 0,
highlightCount: 0,
notificationCount: 0,
);
}
context.go('/rooms/$roomId/threads/${event.eventId}');
selectedEvents.clear();
}
void endPollAction() async {
final event = selectedEvents.first;
if (event == null) return;
final client = currentRoomBundle.firstWhere(
(cl) => selectedEvents.first.senderId == cl!.userID,
orElse: () => null,
);
if (client == null) return;
if (event.senderId != client!.userID) return;
await room.sendEvent({
if (event.senderId != client.userID) return;
await room.sendEvent(
{
'org.matrix.msc1767.text': 'Ended poll',
'm.relates_to': {'rel_type': 'm.reference', 'event_id': event.eventId},
'body': 'Ended poll'
}, type: 'org.matrix.msc3381.poll.end');
'm.relates_to': {
'rel_type': 'm.reference',
'event_id': event.eventId,
},
'body': 'Ended poll',
},
type: 'org.matrix.msc3381.poll.end',
);
}
void redactEventsAction() async {

View File

@ -31,21 +31,39 @@ class ChatAppBarTitle extends StatelessWidget {
hoverColor: Colors.transparent,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
onTap: controller.isArchived
? null
: () => FluffyThemes.isThreeColumnMode(context)
? controller.toggleDisplayChatDetailsColumn()
: context.go('/rooms/${room.id}/details'),
onTap: () {
if (controller.thread != null) {
if (context.canPop()) {
context.pop();
} else {
context.go('/rooms/${room.id}');
}
return;
}
if (!controller.isArchived) {
if (FluffyThemes.isThreeColumnMode(context)) {
controller.toggleDisplayChatDetailsColumn();
} else {
context.go('/rooms/${room.id}/details');
}
}
},
child: Row(
children: [
Hero(
tag: 'content_banner',
child: Avatar(
child: controller.thread == null
? Avatar(
mxContent: room.avatar,
name: room.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)),
),
size: 32,
)
: Icon(
Icons.chat_bubble_outline,
color: Colors.grey[200],
size: 20,
),
),
const SizedBox(width: 12),
@ -54,7 +72,10 @@ class ChatAppBarTitle extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
controller.thread == null
? room.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)))
: '${controller.thread!.rootEvent.senderFromMemoryOrFallback.displayName ?? controller.thread!.rootEvent.senderId}: ${controller.thread!.rootEvent.text}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(

View File

@ -15,10 +15,12 @@ import 'package:extera_next/utils/platform_infos.dart';
class ChatEventList extends StatelessWidget {
final ChatController controller;
final bool showThreadRoots;
const ChatEventList({
super.key,
required this.controller,
this.showThreadRoots = false,
});
@override
@ -37,8 +39,18 @@ class ChatEventList extends StatelessWidget {
final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0;
final events = timeline.events.filterByVisibleInGui();
var events = timeline.events;
if (showThreadRoots) {
events = events.filterThreadRoots();
} else {
events = events.filterByThreaded(controller.thread != null);
}
events = events.filterByVisibleInGui();
final animateInEventIndex = controller.animateInEventIndex;
final threads = controller.room.threads;
// create a map of eventId --> index to greatly improve performance of
// ListView's findChildIndexCallback
@ -121,6 +133,10 @@ class ChatEventList extends StatelessWidget {
timeline.events.length > animateInEventIndex &&
event == timeline.events[animateInEventIndex];
final thread = threads.containsKey(event.eventId)
? threads[event.eventId]
: null;
return AutoScrollTag(
key: ValueKey(event.eventId),
index: i,
@ -128,6 +144,7 @@ class ChatEventList extends StatelessWidget {
child: Message(
event,
animateIn: animateIn,
thread: thread,
resetAnimateIn: () {
controller.animateInEventIndex = null;
},

View File

@ -48,7 +48,15 @@ class ChatView extends StatelessWidget {
tooltip: L10n.of(context).copy,
onPressed: controller.copyEventsAction,
),
if (controller.selectedEvents.length == 1 && controller.selectedEvents.single.content['xyz.extera.translated'] == null)
if (controller.selectedEvents.length == 1)
IconButton(
onPressed: controller.discussAction,
icon: const Icon(Icons.chat_bubble_outline),
tooltip: L10n.of(context).discuss,
),
if (controller.selectedEvents.length == 1 &&
controller.selectedEvents.single.content['xyz.extera.translated'] ==
null)
IconButton(
icon: const Icon(Icons.translate_outlined),
tooltip: L10n.of(context).translateMessage,
@ -118,8 +126,10 @@ class ChatView extends StatelessWidget {
Text(L10n.of(context).recoverMessage),
]),
),
if (controller.selectedEvents.single.type == 'org.matrix.msc3381.poll.start' && controller.selectedEvents.single.senderId == Matrix.of(context).client.userID)
if (controller.selectedEvents.single.type ==
'org.matrix.msc3381.poll.start' &&
controller.selectedEvents.single.senderId ==
Matrix.of(context).client.userID)
PopupMenuItem(
value: _EventContextAction.endPoll,
child: Row(
@ -318,7 +328,10 @@ class ChatView extends StatelessWidget {
Expanded(
child: GestureDetector(
onTap: controller.clearSingleSelectedEvent,
child: ChatEventList(controller: controller),
child: ChatEventList(
controller: controller,
showThreadRoots: controller.showThreadRoots,
),
),
),
if (controller.showScrollDownButton)
@ -337,7 +350,8 @@ class ChatView extends StatelessWidget {
),
)
else if (controller.room.canSendDefaultMessages &&
controller.room.membership == Membership.join)
controller.room.membership == Membership.join &&
!controller.showThreadRoots)
Container(
margin: EdgeInsets.all(bottomSheetPadding),
constraints: const BoxConstraints(

View File

@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:swipe_to_action/swipe_to_action.dart';
@ -45,6 +46,7 @@ class Message extends StatelessWidget {
final List<Color> colors;
final bool gradient;
final bool singleSelected;
final Thread? thread;
const Message(
this.event, {
@ -54,6 +56,7 @@ class Message extends StatelessWidget {
this.longPressSelect = false,
this.gradient = false,
this.singleSelected = false,
this.thread,
required this.onSelect,
required this.onInfoTab,
required this.scrollToEventId,
@ -688,6 +691,59 @@ class Message extends StatelessWidget {
: const SizedBox.shrink(),
),
),
thread != null
? Align(
alignment: ownMessage
? Alignment.bottomRight
: Alignment.bottomLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
(thread?.hasNewMessages ?? false) ? Icons.mark_chat_unread_outlined : Icons.chat_bubble_outline,
color: Colors.grey[200],
size: 20,
),
const SizedBox(width: 16),
thread!.lastEvent != null
? FutureBuilder<User?>(
future: thread!.lastEvent!
.fetchSenderUser(),
builder:
(context, snapshot) {
final user = snapshot
.data ??
event
.senderFromMemoryOrFallback;
return Avatar(
mxContent:
user.avatarUrl,
name: user
.calcDisplayname(),
size: 24,
);
},
)
: const SizedBox.shrink(),
const SizedBox(width: 6),
thread!.lastEvent != null
? Text(
thread!.lastEvent!.text,
)
: const Text('Thread'),
],
),
onTap: () => context.go(
'/rooms/${event.roomId}/threads/${event.eventId}',
),
),
),
)
: const SizedBox.shrink(),
],
),
),

View File

@ -58,8 +58,12 @@ class PollWidgetState extends State<PollWidget> {
final rel = await Matrix.of(context)
.client
.getRelatingEventsWithRelTypeAndEventType(room.id, widget.event.eventId,
"m.reference", "org.matrix.msc3381.poll.response");
.getRelatingEventsWithRelTypeAndEventType(
room.id,
widget.event.eventId,
"m.reference",
"org.matrix.msc3381.poll.response",
);
// Get all poll response events for this poll
final responses = rel.chunk;
@ -90,8 +94,12 @@ class PollWidgetState extends State<PollWidget> {
final rel = await Matrix.of(context)
.client
.getRelatingEventsWithRelTypeAndEventType(room.id, pollEventId,
"m.reference", "org.matrix.msc3381.poll.response");
.getRelatingEventsWithRelTypeAndEventType(
room.id,
pollEventId,
"m.reference",
"org.matrix.msc3381.poll.response",
);
// Get all poll response events for this poll
final responses = rel.chunk;
@ -124,7 +132,8 @@ class PollWidgetState extends State<PollWidget> {
final room = widget.event.room;
// Send poll response event
await room.sendEvent({
await room.sendEvent(
{
'm.relates_to': {
'rel_type': 'm.reference',
'event_id': widget.event.eventId,
@ -132,7 +141,9 @@ class PollWidgetState extends State<PollWidget> {
'org.matrix.msc3381.poll.response': {
'answers': answers,
},
}, type: 'org.matrix.msc3381.poll.response');
},
type: 'org.matrix.msc3381.poll.response',
);
setState(() {
selectedAnswers = answers;
@ -369,9 +380,11 @@ class PollWidgetState extends State<PollWidget> {
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text(hasVoted
: Text(
hasVoted
? L10n.of(context).changeVote
: L10n.of(context).vote),
: L10n.of(context).vote,
),
),
const Spacer(),

View File

@ -1,28 +1,17 @@
import 'dart:io';
import 'package:extera_next/pages/chat/events/html_message.dart';
import 'package:extera_next/pages/image_viewer/image_viewer.dart';
import 'package:extera_next/widgets/mxc_image.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:chewie/chewie.dart';
import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
import 'package:path_provider/path_provider.dart';
import 'package:universal_html/html.dart' as html;
import 'package:video_player/video_player.dart';
import 'package:extera_next/config/app_config.dart';
import 'package:extera_next/pages/chat/events/image_bubble.dart';
import 'package:extera_next/utils/file_description.dart';
import 'package:extera_next/utils/localized_exception_extension.dart';
import 'package:extera_next/utils/matrix_sdk_extensions/event_extension.dart';
import 'package:extera_next/utils/platform_infos.dart';
import 'package:extera_next/utils/url_launcher.dart';
import 'package:extera_next/widgets/blur_hash.dart';
import '../../../utils/error_reporter.dart';
class EventVideoPlayer extends StatelessWidget {
final Event event;
@ -128,8 +117,6 @@ class EventVideoPlayer extends StatelessWidget {
),
),
if (fileDescription != null &&
textColor != null &&
linkColor != null &&
!event.isRichFileDescription)
SizedBox(
width: width,
@ -159,8 +146,6 @@ class EventVideoPlayer extends StatelessWidget {
),
),
if (fileDescription != null &&
textColor != null &&
linkColor != null &&
event.isRichFileDescription)
SizedBox(
width: width,

View File

@ -23,12 +23,14 @@ import 'package:html_unescape/html_unescape.dart';
class SendFileDialog extends StatefulWidget {
final Room room;
final Thread? thread;
final List<XFile> files;
final BuildContext outerContext;
final Event? replyEvent;
const SendFileDialog({
required this.room,
required this.thread,
required this.files,
required this.outerContext,
this.replyEvent,
@ -151,6 +153,8 @@ class SendFileDialogState extends State<SendFileDialog> {
thumbnail: thumbnail,
shrinkImageMaxDimension: compress ? 1600 : null,
extraContent: extraContent,
threadLastEventId: widget.thread?.lastEvent?.eventId ?? widget.thread?.rootEvent.eventId,
threadRootEventId: widget.thread?.rootEvent.eventId
);
} on MatrixException catch (e) {
final retryAfterMs = e.retryAfterMs;

View File

@ -13,9 +13,11 @@ import 'package:extera_next/widgets/future_loading_dialog.dart';
class SendLocationDialog extends StatefulWidget {
final Room room;
final Thread? thread;
const SendLocationDialog({
required this.room,
required this.thread,
super.key,
});
@ -85,7 +87,13 @@ class SendLocationDialogState extends State<SendLocationDialog> {
'geo:${position!.latitude},${position!.longitude};u=${position!.accuracy}';
await showFutureLoadingDialog(
context: context,
future: () => widget.room.sendLocation(body, uri),
future: () {
if (widget.thread != null) {
return widget.thread!.sendLocation(body, uri);
} else {
return widget.room.sendLocation(body, uri);
}
},
);
Navigator.of(context, rootNavigator: false).pop();
}

View File

@ -1,5 +1,4 @@
import 'package:uuid/uuid.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:matrix/matrix.dart';
@ -8,9 +7,11 @@ class SendPollDialog extends StatefulWidget {
final Room room;
final BuildContext outerContext;
final Event? replyEvent;
final Thread? thread;
const SendPollDialog({
required this.room,
required this.thread,
required this.outerContext,
this.replyEvent,
super.key,
@ -74,11 +75,13 @@ class SendPollDialogState extends State<SendPollDialog> {
'm.text': question,
},
'answers': answers
.map((answer) => {
.map(
(answer) => {
'id': const Uuid().v4(),
'org.matrix.msc1767.text': answer,
'm.text': answer,
})
},
)
.toList(),
'max_selections': _maxSelections,
'kind': _kind,
@ -86,7 +89,13 @@ class SendPollDialogState extends State<SendPollDialog> {
};
try {
await widget.room.sendEvent(pollContent, type: 'org.matrix.msc3381.poll.start');
await widget.room.sendEvent(
pollContent,
type: 'org.matrix.msc3381.poll.start',
threadLastEventId: widget.thread?.lastEvent?.eventId ??
widget.thread?.rootEvent.eventId,
threadRootEventId: widget.thread?.rootEvent.eventId,
);
// ignore: use_build_context_synchronously
Navigator.of(context).pop();
} catch (e) {
@ -154,7 +163,7 @@ class SendPollDialogState extends State<SendPollDialog> {
),
const SizedBox(height: 16),
DropdownButtonFormField<int>(
value: _maxSelections,
initialValue: _maxSelections,
decoration: InputDecoration(
labelText: L10n.of(context).maxSelections,
border: const OutlineInputBorder(),
@ -170,7 +179,7 @@ class SendPollDialogState extends State<SendPollDialog> {
),
const SizedBox(height: 16),
DropdownButtonFormField<String>(
value: _kind,
initialValue: _kind,
decoration: InputDecoration(
labelText: L10n.of(context).pollType,
border: const OutlineInputBorder(),

View File

@ -16,14 +16,13 @@ class TranslatedEventDialog extends StatefulWidget {
});
@override
TranslatedEventDialogState createState() =>
TranslatedEventDialogState(event, timeline);
TranslatedEventDialogState createState() => TranslatedEventDialogState();
}
class TranslatedEventDialogState extends State<TranslatedEventDialog> {
final Event event;
final Timeline timeline;
TranslatedEventDialogState(this.event, this.timeline);
Event get event => widget.event;
Timeline get timeline => widget.timeline;
TranslatedEventDialogState();
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
@ -48,20 +47,16 @@ class TranslatedEventDialogState extends State<TranslatedEventDialog> {
longPressSelect: false,
selected: false,
wallpaperMode: false,
gradient: false
gradient: false,
);
return Scaffold(
appBar: AppBar(
title: Text(L10n.of(context).translatedMessage)
),
body: Container(
child: Column(
appBar: AppBar(title: Text(L10n.of(context).translatedMessage)),
body: Column(
children: [
message,
],
),
),
);
}
}

View File

@ -26,6 +26,16 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
bool historyVisibilityLoading = false;
bool guestAccessLoading = false;
Room get room => Matrix.of(context).client.getRoomById(widget.roomId)!;
Set<Room> get knownSpaceParents => {
...room.client.rooms.where(
(space) =>
space.isSpace &&
space.spaceChildren.any((child) => child.roomId == room.id),
),
...room.spaceParents
.map((parent) => room.client.getRoomById(parent.roomId ?? ''))
.whereType<Room>(),
};
String get roomVersion =>
room
@ -46,9 +56,12 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
joinRules.remove(JoinRules.knock);
}
// Not yet supported in FluffyChat:
if (knownSpaceParents.isEmpty) {
joinRules.remove(JoinRules.restricted);
if (roomVersionInt != null && roomVersionInt <= 6) {
joinRules.remove(JoinRules.knockRestricted);
}
}
// If an unsupported join rule is the current join rule, display it:
final currentJoinRule = room.joinRules;
@ -64,7 +77,13 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
});
try {
await room.setJoinRules(newJoinRules);
await room.setJoinRules(
newJoinRules,
allowConditionRoomId: {JoinRules.restricted, JoinRules.knockRestricted}
.contains(newJoinRules)
? knownSpaceParents.first.id
: null,
);
} catch (e, s) {
Logs().w('Unable to change join rules', e, s);
if (mounted) {

View File

@ -45,18 +45,26 @@ class ChatAccessSettingsPageView extends StatelessWidget {
),
),
),
for (final historyVisibility in HistoryVisibility.values)
RadioListTile<HistoryVisibility>.adaptive(
title: Text(
historyVisibility
.getLocalizedString(MatrixLocals(L10n.of(context))),
),
value: historyVisibility,
RadioGroup<HistoryVisibility>(
groupValue: room.historyVisibility,
onChanged: controller.historyVisibilityLoading ||
!room.canChangeHistoryVisibility
? null
? (_) {}
: controller.setHistoryVisibility,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
for (final historyVisibility in HistoryVisibility.values)
RadioListTile<HistoryVisibility>.adaptive(
title: Text(
historyVisibility.getLocalizedString(
MatrixLocals(L10n.of(context)),
),
),
value: historyVisibility,
),
],
),
),
Divider(color: theme.dividerColor),
ListTile(
@ -68,18 +76,27 @@ class ChatAccessSettingsPageView extends StatelessWidget {
),
),
),
RadioGroup(
groupValue: room.joinRules,
onChanged: controller.setJoinRule,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
for (final joinRule in controller.availableJoinRules)
if (joinRule != JoinRules.private)
RadioListTile<JoinRules>.adaptive(
enabled: !controller.joinRulesLoading &&
room.canChangeJoinRules,
title: Text(
joinRule.localizedString(L10n.of(context)),
joinRule.localizedString(
L10n.of(context),
controller.knownSpaceParents,
),
),
value: joinRule,
groupValue: room.joinRules,
onChanged: controller.joinRulesLoading ||
!room.canChangeJoinRules
? null
: controller.setJoinRule,
),
],
),
),
Divider(color: theme.dividerColor),
if ({JoinRules.public, JoinRules.knock}
@ -93,19 +110,25 @@ class ChatAccessSettingsPageView extends StatelessWidget {
),
),
),
RadioGroup(
groupValue: room.guestAccess,
onChanged: controller.setGuestAccess,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
for (final guestAccess in GuestAccess.values)
RadioListTile<GuestAccess>.adaptive(
enabled: !controller.guestAccessLoading &&
room.canChangeGuestAccess,
title: Text(
guestAccess.getLocalizedString(
MatrixLocals(L10n.of(context)),
),
),
value: guestAccess,
groupValue: room.guestAccess,
onChanged: controller.guestAccessLoading ||
!room.canChangeGuestAccess
? null
: controller.setGuestAccess,
),
],
),
),
Divider(color: theme.dividerColor),
ListTile(
@ -260,7 +283,7 @@ class _AliasListTile extends StatelessWidget {
}
extension JoinRulesDisplayString on JoinRules {
String localizedString(L10n l10n) {
String localizedString(L10n l10n, Set<Room> spaceParents) {
switch (this) {
case JoinRules.public:
return l10n.anyoneCanJoin;
@ -271,9 +294,17 @@ extension JoinRulesDisplayString on JoinRules {
case JoinRules.private:
return l10n.noOneCanJoin;
case JoinRules.restricted:
return l10n.restricted;
return l10n.spaceMemberOf(
spaceParents
.map((space) => space.getLocalizedDisplayname(MatrixLocals(l10n)))
.join(', '),
);
case JoinRules.knockRestricted:
return l10n.knockRestricted;
return l10n.spaceMemberOfCanKnock(
spaceParents
.map((space) => space.getLocalizedDisplayname(MatrixLocals(l10n)))
.join(', '),
);
}
}
}

View File

@ -259,6 +259,21 @@ class ChatDetailsView extends StatelessWidget {
const SizedBox(height: 16),
],
Divider(color: theme.dividerColor),
ListTile(
leading: CircleAvatar(
backgroundColor: theme.scaffoldBackgroundColor,
foregroundColor: iconColor,
child: const Icon(
Icons.chat_bubble_outline,
),
),
title: Text(L10n.of(context).chatThreads),
subtitle:
Text(L10n.of(context).chatThreadsDescription),
onTap: () =>
context.push('/rooms/${room.id}?threads=true'),
trailing: const Icon(Icons.chevron_right_outlined),
),
ListTile(
leading: CircleAvatar(
backgroundColor: theme.scaffoldBackgroundColor,

View File

@ -1,9 +1,10 @@
import 'package:flutter/material.dart';
import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:extera_next/config/app_config.dart';
import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:extera_next/pages/chat_list/unread_bubble.dart';
import 'package:extera_next/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:extera_next/utils/room_status_extension.dart';
import 'package:extera_next/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
@ -35,32 +36,6 @@ class ChatListItem extends StatelessWidget {
super.key,
});
Future<bool> archiveAction(BuildContext context) async {
{
if ([Membership.leave, Membership.ban].contains(room.membership)) {
final forgetResult = await showFutureLoadingDialog(
context: context,
future: () => room.forget(),
);
return forgetResult.isValue;
}
final confirmed = await showOkCancelAlertDialog(
context: context,
title: L10n.of(context).areYouSure,
okLabel: L10n.of(context).leave,
cancelLabel: L10n.of(context).cancel,
message: L10n.of(context).archiveRoomDescription,
isDestructive: true,
);
if (confirmed != OkCancelResult.ok) return false;
final leaveResult = await showFutureLoadingDialog(
context: context,
future: () => room.leave(),
);
return leaveResult.isValue;
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
@ -69,14 +44,9 @@ class ChatListItem extends StatelessWidget {
final typingText = room.getLocalizedTypingText(context);
final lastEvent = room.lastEvent;
final ownMessage = lastEvent?.senderId == room.client.userID;
final unread = room.isUnread || room.membership == Membership.invite;
final unread = room.isUnread;
final directChatMatrixId = room.directChatMatrixID;
final isDirectChat = directChatMatrixId != null;
final unreadBubbleSize = unread || room.hasNewMessages
? room.notificationCount > 0
? 20.0
: 14.0
: 0.0;
final hasNotifications = room.notificationCount > 0;
final backgroundColor =
activeChat ? theme.colorScheme.secondaryContainer : null;
@ -93,8 +63,6 @@ class ChatListItem extends StatelessWidget {
: room.getState(EventTypes.RoomMember, lastEvent.senderId) == null;
final space = this.space;
final inviterMxId = room.getState(EventTypes.RoomMember, room.client.userID!)?.senderId;
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8,
@ -116,9 +84,7 @@ class ChatListItem extends StatelessWidget {
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
scale: hovered ? 1.1 : 1.0,
child:
(!AppConfig.hideAvatarsInInvites || room.membership != Membership.invite)
? SizedBox(
child: SizedBox(
width: Avatar.defaultSize,
height: Avatar.defaultSize,
child: Stack(
@ -195,7 +161,7 @@ class ChatListItem extends StatelessWidget {
),
],
),
) : null,
),
),
),
title: Row(
@ -232,13 +198,12 @@ class ChatListItem extends StatelessWidget {
color: theme.colorScheme.primary,
),
),
if (!room.isSpace &&
lastEvent != null &&
room.membership != Membership.invite)
if (!room.isSpace && room.membership != Membership.invite)
Padding(
padding: const EdgeInsets.only(left: 4.0),
child: Text(
lastEvent.originServerTs.localizedTimeShort(context),
room.latestEventReceivedTime
.localizedTimeShort(context),
style: TextStyle(
fontSize: 12,
color: theme.colorScheme.outline,
@ -253,7 +218,7 @@ class ChatListItem extends StatelessWidget {
children: <Widget>[
if (typingText.isEmpty &&
ownMessage &&
room.lastEvent!.status.isSending) ...[
room.lastEvent?.status.isSending == true) ...[
const SizedBox(
width: 16,
height: 16,
@ -321,7 +286,7 @@ class ChatListItem extends StatelessWidget {
),
builder: (context, snapshot) => Text(
room.membership == Membership.invite
? "${room
? room
.getState(
EventTypes.RoomMember,
room.client.userID!,
@ -331,9 +296,9 @@ class ChatListItem extends StatelessWidget {
(isDirectChat
? L10n.of(context).newChatRequest
: L10n.of(context)
.inviteGroupChat)}${inviterMxId != null ? " ($inviterMxId)" : ""}"
.inviteGroupChat)
: snapshot.data ??
L10n.of(context).emptyChat,
L10n.of(context).noMessagesYet,
softWrap: false,
maxLines: room.notificationCount >= 1 ? 2 : 1,
overflow: TextOverflow.ellipsis,
@ -349,48 +314,33 @@ class ChatListItem extends StatelessWidget {
),
),
const SizedBox(width: 8),
AnimatedContainer(
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 7),
height: unreadBubbleSize,
width: !hasNotifications && !unread && !room.hasNewMessages
? 0
: (unreadBubbleSize - 9) *
room.notificationCount.toString().length +
9,
decoration: BoxDecoration(
color: room.highlightCount > 0 ||
room.membership == Membership.invite
? theme.colorScheme.error
: hasNotifications || room.markedUnread
? theme.colorScheme.primary
: theme.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(7),
),
child: hasNotifications
? Text(
room.notificationCount.toString(),
style: TextStyle(
color: room.highlightCount > 0 ||
room.membership == Membership.invite
? theme.colorScheme.onError
: hasNotifications
? theme.colorScheme.onPrimary
: theme.colorScheme.onPrimaryContainer,
fontSize: 13,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
)
: const SizedBox.shrink(),
),
UnreadBubble(room: room),
],
),
onTap: onTap,
trailing: onForget == null
? null
? room.membership == Membership.invite
? IconButton(
tooltip: L10n.of(context).decline,
icon: const Icon(Icons.delete_forever_outlined),
color: theme.colorScheme.error,
onPressed: () async {
final consent = await showOkCancelAlertDialog(
context: context,
title: L10n.of(context).decline,
message: L10n.of(context).areYouSure,
okLabel: L10n.of(context).yes,
isDestructive: true,
);
if (consent != OkCancelResult.ok) return;
if (!context.mounted) return;
await showFutureLoadingDialog(
context: context,
future: room.leave,
);
},
)
: null
: IconButton(
icon: const Icon(Icons.delete_outlined),
onPressed: onForget,

View File

@ -1,27 +1,40 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart' as sdk;
import 'package:matrix/matrix.dart';
import 'package:extera_next/config/app_config.dart';
import 'package:extera_next/config/themes.dart';
import 'package:extera_next/pages/chat_list/chat_list_item.dart';
import 'package:extera_next/pages/chat_list/search_title.dart';
import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:extera_next/pages/chat_list/unread_bubble.dart';
import 'package:extera_next/utils/localized_exception_extension.dart';
import 'package:extera_next/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:extera_next/utils/stream_extension.dart';
import 'package:extera_next/utils/string_color.dart';
import 'package:extera_next/widgets/adaptive_dialogs/public_room_dialog.dart';
import 'package:extera_next/widgets/adaptive_dialogs/show_modal_action_popup.dart';
import 'package:extera_next/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
import 'package:extera_next/widgets/adaptive_dialogs/show_text_input_dialog.dart';
import 'package:extera_next/widgets/avatar.dart';
import 'package:extera_next/widgets/future_loading_dialog.dart';
import 'package:extera_next/widgets/hover_builder.dart';
import 'package:extera_next/widgets/matrix.dart';
enum AddRoomType { chat, subspace }
enum SpaceChildAction { edit, moveToSpace, removeFromSpace }
enum SpaceActions {
settings,
invite,
members,
leave,
}
class SpaceView extends StatefulWidget {
final String spaceId;
final void Function() onBack;
@ -45,7 +58,7 @@ class SpaceView extends StatefulWidget {
}
class _SpaceViewState extends State<SpaceView> {
final List<SpaceRoomsChunk> _discoveredChildren = [];
final List<SpaceRoomsChunk$2> _discoveredChildren = [];
final TextEditingController _filterController = TextEditingController();
String? _nextBatch;
bool _noMoreRooms = false;
@ -58,9 +71,28 @@ class _SpaceViewState extends State<SpaceView> {
}
void _loadHierarchy() async {
final room = Matrix.of(context).client.getRoomById(widget.spaceId);
final matrix = Matrix.of(context);
final room = matrix.client.getRoomById(widget.spaceId);
if (room == null) return;
final cacheKey = 'spaces_history_cache${room.id}';
if (_discoveredChildren.isEmpty) {
final cachedChildren = matrix.store.getStringList(cacheKey);
if (cachedChildren != null) {
try {
_discoveredChildren.addAll(
cachedChildren.map(
(jsonString) =>
SpaceRoomsChunk$2.fromJson(jsonDecode(jsonString)),
),
);
} catch (e, s) {
Logs().e('Unable to json decode spaces hierarchy cache!', e, s);
matrix.store.remove(cacheKey);
}
}
}
setState(() {
_isLoading = true;
});
@ -74,16 +106,25 @@ class _SpaceViewState extends State<SpaceView> {
);
if (!mounted) return;
setState(() {
if (_nextBatch == null) _discoveredChildren.clear();
_nextBatch = hierarchy.nextBatch;
if (hierarchy.nextBatch == null) {
_noMoreRooms = true;
}
_discoveredChildren.addAll(
hierarchy.rooms
.where((c) => room.client.getRoomById(c.roomId) == null),
hierarchy.rooms.where((room) => room.roomId != widget.spaceId),
);
_isLoading = false;
});
if (_nextBatch == null) {
matrix.store.setStringList(
cacheKey,
_discoveredChildren
.map((child) => jsonEncode(child.toJson()))
.toList(),
);
}
} catch (e, s) {
Logs().w('Unable to load hierarchy', e, s);
if (!mounted) return;
@ -95,7 +136,7 @@ class _SpaceViewState extends State<SpaceView> {
}
}
void _joinChildRoom(SpaceRoomsChunk item) async {
void _joinChildRoom(SpaceRoomsChunk$2 item) async {
final client = Matrix.of(context).client;
final space = client.getRoomById(widget.spaceId);
@ -111,9 +152,7 @@ class _SpaceViewState extends State<SpaceView> {
),
);
if (mounted && joined == true) {
setState(() {
_discoveredChildren.remove(item);
});
setState(() {});
}
}
@ -129,6 +168,10 @@ class _SpaceViewState extends State<SpaceView> {
await space?.postLoad();
context.push('/rooms/${widget.spaceId}/invite');
break;
case SpaceActions.members:
await space?.postLoad();
context.push('/rooms/${widget.spaceId}/details/members');
break;
case SpaceActions.leave:
final confirmed = await showOkCancelAlertDialog(
context: context,
@ -151,27 +194,11 @@ class _SpaceViewState extends State<SpaceView> {
}
}
void _addChatOrSubspace() async {
final roomType = await showModalActionPopup(
context: context,
title: L10n.of(context).addChatOrSubSpace,
actions: [
AdaptiveModalAction(
value: AddRoomType.subspace,
label: L10n.of(context).createNewSpace,
),
AdaptiveModalAction(
value: AddRoomType.chat,
label: L10n.of(context).createGroup,
),
],
);
if (roomType == null) return;
void _addChatOrSubspace(AddRoomType roomType) async {
final names = await showTextInputDialog(
context: context,
title: roomType == AddRoomType.subspace
? L10n.of(context).createNewSpace
? L10n.of(context).newSubSpace
: L10n.of(context).createGroup,
hintText: roomType == AddRoomType.subspace
? L10n.of(context).spaceName
@ -196,29 +223,169 @@ class _SpaceViewState extends State<SpaceView> {
late final String roomId;
final activeSpace = client.getRoomById(widget.spaceId)!;
await activeSpace.postLoad();
final isPublicSpace = activeSpace.joinRules == JoinRules.public;
if (roomType == AddRoomType.subspace) {
roomId = await client.createSpace(
name: names,
visibility: activeSpace.joinRules == JoinRules.public
? sdk.Visibility.public
: sdk.Visibility.private,
visibility:
isPublicSpace ? sdk.Visibility.public : sdk.Visibility.private,
);
} else {
roomId = await client.createGroupChat(
enableEncryption: !isPublicSpace,
groupName: names,
preset: activeSpace.joinRules == JoinRules.public
preset: isPublicSpace
? CreateRoomPreset.publicChat
: CreateRoomPreset.privateChat,
visibility: activeSpace.joinRules == JoinRules.public
? sdk.Visibility.public
: sdk.Visibility.private,
visibility:
isPublicSpace ? sdk.Visibility.public : sdk.Visibility.private,
initialState: isPublicSpace
? null
: [
StateEvent(
content: {
'join_rule': 'restricted',
'allow': [
{
'room_id': widget.spaceId,
'type': 'm.room_membership',
},
],
},
type: EventTypes.RoomJoinRules,
),
],
);
}
await activeSpace.setSpaceChild(roomId);
},
);
if (result.error != null) return;
setState(() {
_nextBatch = null;
_discoveredChildren.clear();
});
_loadHierarchy();
}
void _showSpaceChildEditMenu(BuildContext posContext, String roomId) async {
final overlay =
Overlay.of(posContext).context.findRenderObject() as RenderBox;
final button = posContext.findRenderObject() as RenderBox;
final position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(const Offset(0, -65), ancestor: overlay),
button.localToGlobal(
button.size.bottomRight(Offset.zero) + const Offset(-50, 0),
ancestor: overlay,
),
),
Offset.zero & overlay.size,
);
final action = await showMenu<SpaceChildAction>(
context: posContext,
position: position,
items: [
PopupMenuItem(
value: SpaceChildAction.moveToSpace,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.move_down_outlined),
const SizedBox(width: 12),
Text(L10n.of(context).moveToDifferentSpace),
],
),
),
PopupMenuItem(
value: SpaceChildAction.edit,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.edit_outlined),
const SizedBox(width: 12),
Text(L10n.of(context).edit),
],
),
),
PopupMenuItem(
value: SpaceChildAction.removeFromSpace,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.group_remove_outlined),
const SizedBox(width: 12),
Text(L10n.of(context).removeFromSpace),
],
),
),
],
);
if (action == null) return;
if (!mounted) return;
final space = Matrix.of(context).client.getRoomById(widget.spaceId);
if (space == null) return;
switch (action) {
case SpaceChildAction.edit:
context.push('/rooms/${widget.spaceId}/details');
case SpaceChildAction.moveToSpace:
final spacesWithPowerLevels = space.client.rooms
.where(
(room) =>
room.isSpace &&
room.canChangeStateEvent(EventTypes.SpaceChild) &&
room.id != widget.spaceId,
)
.toList();
final newSpace = await showModalActionPopup(
context: context,
title: L10n.of(context).space,
actions: spacesWithPowerLevels
.map(
(space) => AdaptiveModalAction(
value: space,
label: space
.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
),
)
.toList(),
);
if (newSpace == null) return;
final result = await showFutureLoadingDialog(
context: context,
future: () async {
await newSpace.setSpaceChild(newSpace.id);
await space.removeSpaceChild(roomId);
},
);
if (result.isError) return;
if (!mounted) return;
_nextBatch = null;
_loadHierarchy();
return;
case SpaceChildAction.removeFromSpace:
final consent = await showOkCancelAlertDialog(
context: context,
title: L10n.of(context).removeFromSpace,
message: L10n.of(context).removeFromSpaceDescription,
);
if (consent != OkCancelResult.ok) return;
if (!mounted) return;
final result = await showFutureLoadingDialog(
context: context,
future: () => space.removeSpaceChild(roomId),
);
if (result.isError) return;
if (!mounted) return;
_nextBatch = null;
_loadHierarchy();
return;
}
}
@override
@ -228,6 +395,11 @@ class _SpaceViewState extends State<SpaceView> {
final room = Matrix.of(context).client.getRoomById(widget.spaceId);
final displayname =
room?.getLocalizedDisplayname() ?? L10n.of(context).nothingFound;
const avatarSize = Avatar.defaultSize / 1.5;
final isAdmin = room?.canChangeStateEvent(
EventTypes.SpaceChild,
) ==
true;
return Scaffold(
appBar: AppBar(
leading: FluffyThemes.isColumnMode(context)
@ -242,6 +414,7 @@ class _SpaceViewState extends State<SpaceView> {
title: ListTile(
contentPadding: EdgeInsets.zero,
leading: Avatar(
size: avatarSize,
mxContent: room?.avatar,
name: displayname,
border: BorderSide(width: 1, color: theme.dividerColor),
@ -252,19 +425,40 @@ class _SpaceViewState extends State<SpaceView> {
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
subtitle: room == null
? null
: Text(
L10n.of(context).countChatsAndCountParticipants(
room.spaceChildren.length,
room.summary.mJoinedMemberCount ?? 1,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
actions: [
if (isAdmin)
PopupMenuButton<AddRoomType>(
icon: const Icon(Icons.add_outlined),
onSelected: _addChatOrSubspace,
tooltip: L10n.of(context).addChatOrSubSpace,
itemBuilder: (context) => [
PopupMenuItem(
value: AddRoomType.chat,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.group_add_outlined),
const SizedBox(width: 12),
Text(L10n.of(context).newGroup),
],
),
),
PopupMenuItem(
value: AddRoomType.subspace,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.workspaces_outlined),
const SizedBox(width: 12),
Text(L10n.of(context).newSubSpace),
],
),
),
],
),
PopupMenuButton<SpaceActions>(
useRootNavigator: true,
onSelected: _onSpaceAction,
itemBuilder: (context) => [
PopupMenuItem(
@ -289,6 +483,21 @@ class _SpaceViewState extends State<SpaceView> {
],
),
),
PopupMenuItem(
value: SpaceActions.members,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.group_outlined),
const SizedBox(width: 12),
Text(
L10n.of(context).countParticipants(
room?.summary.mJoinedMemberCount ?? 1,
),
),
],
),
),
PopupMenuItem(
value: SpaceActions.leave,
child: Row(
@ -304,16 +513,6 @@ class _SpaceViewState extends State<SpaceView> {
),
],
),
floatingActionButton: room?.canChangeStateEvent(
EventTypes.SpaceChild,
) ==
true
? FloatingActionButton.extended(
onPressed: _addChatOrSubspace,
label: Text(L10n.of(context).group),
icon: const Icon(Icons.group_add_outlined),
)
: null,
body: room == null
? const Center(
child: Icon(
@ -331,9 +530,11 @@ class _SpaceViewState extends State<SpaceView> {
.whereType<String>()
.toSet();
final joinedRooms = room.client.rooms
final joinedRooms = Map.fromEntries(
room.client.rooms
.where((room) => childrenIds.remove(room.id))
.toList();
.map((room) => MapEntry(room.id, room)),
);
final joinedParents = room.spaceParents
.map((parent) {
@ -348,7 +549,6 @@ class _SpaceViewState extends State<SpaceView> {
slivers: [
SliverAppBar(
floating: true,
toolbarHeight: 72,
scrolledUnderElevation: 0,
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
@ -358,11 +558,6 @@ class _SpaceViewState extends State<SpaceView> {
textInputAction: TextInputAction.search,
decoration: InputDecoration(
filled: true,
fillColor: theme.colorScheme.secondaryContainer,
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(99),
),
contentPadding: EdgeInsets.zero,
hintText: L10n.of(context).search,
hintStyle: TextStyle(
@ -422,42 +617,11 @@ class _SpaceViewState extends State<SpaceView> {
},
),
SliverList.builder(
itemCount: joinedRooms.length,
itemCount: _discoveredChildren.length + 1,
itemBuilder: (context, i) {
final joinedRoom = joinedRooms[i];
return ChatListItem(
joinedRoom,
filter: filter,
onTap: () => widget.onChatTab(joinedRoom),
onLongPress: (context) => widget.onChatContext(
joinedRoom,
context,
),
activeChat: widget.activeChat == joinedRoom.id,
);
},
),
SliverList.builder(
itemCount: _discoveredChildren.length + 2,
itemBuilder: (context, i) {
if (i == 0) {
return SearchTitle(
title: L10n.of(context).discover,
icon: const Icon(Icons.explore_outlined),
);
}
i--;
if (i == _discoveredChildren.length) {
if (_noMoreRooms) {
return Padding(
padding: const EdgeInsets.all(12.0),
child: Center(
child: Text(
L10n.of(context).noMoreChatsFound,
style: const TextStyle(fontSize: 13),
),
),
);
return const SizedBox.shrink();
}
return Padding(
padding: const EdgeInsets.symmetric(
@ -467,11 +631,7 @@ class _SpaceViewState extends State<SpaceView> {
child: TextButton(
onPressed: _isLoading ? null : _loadHierarchy,
child: _isLoading
? LinearProgressIndicator(
borderRadius: BorderRadius.circular(
AppConfig.borderRadius,
),
)
? const CircularProgressIndicator.adaptive()
: Text(L10n.of(context).loadMore),
),
);
@ -483,6 +643,7 @@ class _SpaceViewState extends State<SpaceView> {
if (!displayname.toLowerCase().contains(filter)) {
return const SizedBox.shrink();
}
final joinedRoom = joinedRooms[item.roomId];
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8,
@ -492,51 +653,83 @@ class _SpaceViewState extends State<SpaceView> {
borderRadius:
BorderRadius.circular(AppConfig.borderRadius),
clipBehavior: Clip.hardEdge,
child: ListTile(
color: joinedRoom != null &&
widget.activeChat == joinedRoom.id
? theme.colorScheme.secondaryContainer
: Colors.transparent,
child: HoverBuilder(
builder: (context, hovered) => ListTile(
visualDensity:
const VisualDensity(vertical: -0.5),
contentPadding:
const EdgeInsets.symmetric(horizontal: 8),
onTap: () => _joinChildRoom(item),
leading: Avatar(
onTap: joinedRoom != null
? () => widget.onChatTab(joinedRoom)
: () => _joinChildRoom(item),
onLongPress: isAdmin
? () => _showSpaceChildEditMenu(
context,
item.roomId,
)
: null,
leading: hovered && isAdmin
? SizedBox.square(
dimension: avatarSize,
child: IconButton(
splashRadius: avatarSize,
iconSize: 14,
style: IconButton.styleFrom(
foregroundColor: theme.colorScheme
.onTertiaryContainer,
backgroundColor: theme
.colorScheme.tertiaryContainer,
),
onPressed: () =>
_showSpaceChildEditMenu(
context,
item.roomId,
),
icon: const Icon(Icons.edit_outlined),
),
)
: Avatar(
size: avatarSize,
mxContent: item.avatarUrl,
name: displayname,
name: '#',
backgroundColor:
theme.colorScheme.surfaceContainer,
textColor: item.name?.darkColor ??
theme.colorScheme.onSurface,
border: item.roomType == 'm.space'
? BorderSide(
color: theme.colorScheme
.surfaceContainerHighest,
)
: null,
borderRadius: item.roomType == 'm.space'
? BorderRadius.circular(
AppConfig.borderRadius / 2,
AppConfig.borderRadius / 4,
)
: null,
),
title: Row(
children: [
Expanded(
child: Opacity(
opacity: joinedRoom == null ? 0.5 : 1,
child: Text(
displayname,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Text(
item.numJoinedMembers.toString(),
style: TextStyle(
fontSize: 13,
color: theme.textTheme.bodyMedium!.color,
),
),
const SizedBox(width: 4),
const Icon(
Icons.people_outlined,
size: 14,
),
if (joinedRoom != null)
UnreadBubble(room: joinedRoom)
else
const Icon(Icons.chevron_right_outlined),
],
),
subtitle: Text(
item.topic ??
L10n.of(context).countParticipants(
item.numJoinedMembers,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
),
@ -551,9 +744,3 @@ class _SpaceViewState extends State<SpaceView> {
);
}
}
enum SpaceActions {
settings,
invite,
leave,
}

View File

@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:extera_next/config/themes.dart';
class UnreadBubble extends StatelessWidget {
final Room room;
const UnreadBubble({required this.room, super.key});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final unread = room.isUnread;
final hasNotifications = room.notificationCount > 0;
final unreadBubbleSize = unread || room.hasNewMessages
? room.notificationCount > 0
? 20.0
: 14.0
: 0.0;
return AnimatedContainer(
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 7),
height: unreadBubbleSize,
width: !hasNotifications && !unread && !room.hasNewMessages
? 0
: (unreadBubbleSize - 9) * room.notificationCount.toString().length +
9,
decoration: BoxDecoration(
color: room.highlightCount > 0
? theme.colorScheme.error
: hasNotifications || room.markedUnread
? theme.colorScheme.primary
: theme.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(7),
),
child: hasNotifications
? Text(
room.notificationCount.toString(),
style: TextStyle(
color: room.highlightCount > 0
? theme.colorScheme.onError
: hasNotifications
? theme.colorScheme.onPrimary
: theme.colorScheme.onPrimaryContainer,
fontSize: 13,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
)
: const SizedBox.shrink(),
);
}
}

View File

@ -0,0 +1,47 @@
import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:extera_next/pages/chat/chat.dart';
import 'package:extera_next/widgets/matrix.dart';
import 'package:extera_next/widgets/share_scaffold_dialog.dart';
import 'package:flutter/material.dart';
class ThreadPage extends StatelessWidget {
final String roomId;
final List<ShareItem>? shareItems;
final String? threadRootEventId;
final String? eventId;
const ThreadPage({
super.key,
required this.roomId,
required this.threadRootEventId,
this.eventId,
this.shareItems,
});
@override
Widget build(BuildContext context) {
final client = Matrix.of(context).client;
final room = client.getRoomById(roomId);
if (room == null) {
return Scaffold(
appBar: AppBar(title: Text(L10n.of(context).oopsSomethingWentWrong)),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat),
),
),
);
}
final thread = room.threads[threadRootEventId];
return ChatPageWithRoom(
key: Key('chat_page_${roomId}_${threadRootEventId}_$eventId'),
room: room,
thread: thread,
shareItems: shareItems,
eventId: eventId,
);
}
}

View File

@ -88,7 +88,7 @@ class ImageViewerView extends StatelessWidget {
child: GestureDetector(
// Ignore taps to not go back here:
onTap: () {},
child: EventVideoPlayer(event),
child: EventVideoPlayer(event, controller),
),
),
);

View File

@ -1,5 +1,6 @@
import 'dart:io';
import 'package:extera_next/pages/image_viewer/image_viewer.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -18,9 +19,11 @@ import '../../widgets/mxc_image.dart';
class EventVideoPlayer extends StatefulWidget {
final Event event;
final ImageViewerController ivController;
const EventVideoPlayer(
this.event, {
this.event,
this.ivController, {
super.key,
});
@ -69,6 +72,11 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
await videoPlayerController.initialize();
if (widget.ivController.currentEvent.eventId != widget.event.eventId) {
dispose();
return;
}
// Create a ChewieController on top.
setState(() {
_chewieController = ChewieController(

View File

@ -115,7 +115,7 @@ abstract class ClientManager {
return Client(
clientName,
httpClient:
PlatformInfos.isAndroid ? CustomHttpClient.createHTTPClient() : null,
CustomHttpClient.createHTTPClient(),
verificationMethods: {
KeyVerificationMethod.numbers,
if (kIsWeb || PlatformInfos.isMobile || PlatformInfos.isLinux)

View File

@ -1,15 +1,20 @@
import 'dart:convert';
import 'dart:io';
import 'package:extera_next/config/app_config.dart';
import 'package:extera_next/utils/platform_infos.dart';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart';
import 'package:extera_next/config/isrg_x1.dart';
class CustomHttpClient {
static HttpClient customHttpClient(String? cert) {
static HttpClient? customHttpClient(String? cert) {
if (PlatformInfos.isWeb) return null;
final context = SecurityContext.defaultContext;
if (PlatformInfos.isAndroid) {
try {
if (cert != null) {
final bytes = utf8.encode(cert);
@ -22,8 +27,17 @@ class CustomHttpClient {
rethrow;
}
}
}
return HttpClient(context: context);
final client = HttpClient(context: context);
if (AppConfig.httpProxy != null) {
client.findProxy = (uri) {
return "PROXY ${AppConfig.httpProxy};";
};
}
return client;
}
static http.Client createHTTPClient() => IOClient(customHttpClient(ISRG_X1));

View File

@ -4,6 +4,14 @@ import 'package:matrix/matrix.dart';
import '../../config/app_config.dart';
extension VisibleInGuiExtension on List<Event> {
List<Event> filterByThreaded(bool threaded) {
return where((e) => e.isThreaded == threaded || e.relationshipType == RelationshipTypes.edit || e.relationshipType == RelationshipTypes.reaction).toList();
}
List<Event> filterThreadRoots() {
return where((e) => e.room.threads.containsKey(e.eventId)).toList();
}
List<Event> filterByVisibleInGui({String? exceptionEventId}) {
final visibleEvents =
where((e) => e.isVisibleInGui || e.eventId == exceptionEventId)
@ -46,7 +54,9 @@ extension IsStateExtension on Event {
// if we enabled to hide all redacted events, don't show those
(!AppConfig.hideRedactedEvents || !redacted) &&
// if we enabled to hide all unknown events, don't show those
(!AppConfig.hideUnknownEvents || isEventTypeKnown || type == PollEvents.PollStart) &&
(!AppConfig.hideUnknownEvents ||
isEventTypeKnown ||
type == PollEvents.PollStart) &&
// remove state events that we don't want to render
(isState || !AppConfig.hideAllStateEvents) &&
// hide simple join/leave member events in public rooms
@ -56,5 +66,9 @@ extension IsStateExtension on Event {
content.tryGet<String>('membership') == 'ban' ||
stateKey != senderId);
bool get isThreaded =>
relationshipEventId != null &&
relationshipType == RelationshipTypes.thread;
bool get isState => this.stateKey != null;
}

View File

@ -10,7 +10,6 @@ import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:universal_html/html.dart' as html;
import 'package:extera_next/config/app_config.dart';
import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:extera_next/utils/client_manager.dart';
import 'package:extera_next/utils/platform_infos.dart';
import 'cipher.dart';
@ -69,7 +68,12 @@ Future<MatrixSdkDatabase> _constructDatabase(String clientName) async {
Directory? fileStorageLocation;
try {
fileStorageLocation = await getTemporaryDirectory();
final tmpDir = await getTemporaryDirectory();
final appTmpDir = Directory(join(tmpDir.path, clientName));
if (!await appTmpDir.exists()) {
await appTmpDir.create(recursive: true);
}
fileStorageLocation = appTmpDir;
} on MissingPlatformDirectoryException catch (_) {
Logs().w(
'No temporary directory for file cache available on this platform.',
@ -109,8 +113,7 @@ Future<MatrixSdkDatabase> _constructDatabase(String clientName) async {
version: 1,
// most important : apply encryption when opening the DB
onConfigure: helper?.applyPragmaKey,
singleInstance: false
),
singleInstance: false),
);
return await MatrixSdkDatabase.init(

View File

@ -7,11 +7,11 @@ 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'
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);
@ -40,14 +40,13 @@ extension SynapseAdmin on matrix.Client {
if (room == null) return null;
print('Event content: ${jsonEncode(json['event_json'])}');
return new matrix.Event(
return 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']),
originServerTs: DateTime.fromMillisecondsSinceEpoch(
json['event_json']['origin_server_ts']),
room: room,
);
}

View File

@ -30,7 +30,9 @@ class PublicRoomDialog extends StatelessWidget {
final result = await showFutureLoadingDialog<String>(
context: context,
future: () async {
if (chunk != null && client.getRoomById(chunk.roomId) != null) {
if (chunk != null &&
client.getRoomById(chunk.roomId) != null &&
client.getRoomById(chunk.roomId)?.membership != Membership.leave) {
return chunk.roomId;
}
final roomId = chunk != null && knock

View File

@ -18,6 +18,8 @@ class Avatar extends StatelessWidget {
final BorderRadius? borderRadius;
final IconData? icon;
final BorderSide? border;
final Color? backgroundColor;
final Color? textColor;
const Avatar({
this.mxContent,
@ -30,6 +32,8 @@ class Avatar extends StatelessWidget {
this.borderRadius,
this.border,
this.icon,
this.backgroundColor,
this.textColor,
super.key,
});
@ -71,14 +75,16 @@ class Avatar extends StatelessWidget {
height: size,
placeholder: (_) => noPic
? Container(
decoration: BoxDecoration(color: name?.lightColorAvatar),
decoration: BoxDecoration(
color: backgroundColor ?? name?.lightColorAvatar,
),
alignment: Alignment.center,
child: Text(
fallbackLetters,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'RobotoMono',
color: Colors.white,
color: textColor ?? Colors.white,
fontWeight: FontWeight.bold,
fontSize: (size / 2.5).roundToDouble(),
),

View File

@ -1203,10 +1203,10 @@ packages:
description:
path: "."
ref: main
resolved-ref: "58c4cf19d010d9ae193e9df10bd1f8fdf02277b0"
resolved-ref: f678376e3a4825a6e1a00d89585d6bae0843950c
url: "https://git.extera.xyz/OfficialDakari/matrix-dart-sdk.git"
source: git
version: "2.0.1"
version: "3.0.1"
meta:
dependency: transitive
description:
@ -2173,7 +2173,7 @@ packages:
source: hosted
version: "3.1.4"
uuid:
dependency: transitive
dependency: "direct main"
description:
name: uuid
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff

View File

@ -101,6 +101,7 @@ dependencies:
wakelock_plus: ^1.2.2
webrtc_interface: ^1.0.13
dio: ^5.9.0
uuid: ^4.5.1
dev_dependencies:
flutter_lints: ^3.0.0