Improved UX for spaces

This commit is contained in:
OfficialDakari 2025-11-02 18:13:45 +05:00
parent 66ad301045
commit 0f65f3e7f5
58 changed files with 1272 additions and 245 deletions

View File

@ -6,6 +6,11 @@
"longPressToRecordVoiceMessage": "Long press to record voice message.", "longPressToRecordVoiceMessage": "Long press to record voice message.",
"pause": "Pause", "pause": "Pause",
"resume": "Resume", "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", "endPoll": "End poll",
"anonymousPoll": "Anonymous", "anonymousPoll": "Anonymous",
"publicPoll": "Public", "publicPoll": "Public",

View File

@ -6,6 +6,11 @@
"noMessagesYet": "Нет сообщений", "noMessagesYet": "Нет сообщений",
"longPressToRecordVoiceMessage": "Зажмите, чтобы записать голосовое сообщение.", "longPressToRecordVoiceMessage": "Зажмите, чтобы записать голосовое сообщение.",
"pause": "Пауза", "pause": "Пауза",
"newSubSpace": "Новое подпространство",
"moveToDifferentSpace": "Перенести в другое пространство",
"moveUp": "Вверх",
"moveDown": "Вниз",
"removeFromSpaceDescription": "Эта комната будет удалена из пространства, но останется в Вашем списке чатов.",
"resume": "Продолжить", "resume": "Продолжить",
"anonymousPoll": "Анонимный", "anonymousPoll": "Анонимный",
"publicPoll": "Открытый", "publicPoll": "Открытый",

View File

@ -223,6 +223,36 @@ abstract class L10n {
/// **'Resume'** /// **'Resume'**
String get 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. /// No description provided for @endPoll.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@ -24,6 +24,22 @@ class L10nAr extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nBe extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nBn extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nBo extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nCa extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nCs extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nDe extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nEl extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nEn extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nEo extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nEs extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nEt extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nEu extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nFa extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nFi extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nFil extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nFr extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nGa extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nGl extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nHe extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nHi extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nHr extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nHu extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nIa extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nId extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nIe extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nIt extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nJa extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nKa extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nKo extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nLt extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nLv extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nNb extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nNl extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nPl extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nPt extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nRo extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nRu extends L10n {
@override @override
String get resume => 'Продолжить'; String get 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 @override
String get endPoll => 'Завершить опрос'; String get endPoll => 'Завершить опрос';
@ -91,7 +107,7 @@ class L10nRu extends L10n {
@override @override
String get cleanExifDescription => String get cleanExifDescription =>
'Удалять метаданные EXIF (модель камеры, геолокация, время) из изображений еред отправкой.'; 'Удалять метаданные EXIF (модель камеры, геолокация, время) из изображений перед отправкой.';
@override @override
String get repeatPassword => 'Повторите пароль'; String get repeatPassword => 'Повторите пароль';

View File

@ -24,6 +24,22 @@ class L10nSk extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nSl extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nSr extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nSv extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nTa extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nTe extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nTh extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nTr extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nUk extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nVi extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -24,6 +24,22 @@ class L10nZh extends L10n {
@override @override
String get resume => 'Resume'; 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 @override
String get endPoll => 'End poll'; String get endPoll => 'End poll';

View File

@ -1,9 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:extera_next/config/app_config.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/matrix_sdk_extensions/matrix_locals.dart';
import 'package:extera_next/utils/room_status_extension.dart'; import 'package:extera_next/utils/room_status_extension.dart';
import 'package:extera_next/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:extera_next/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
@ -35,32 +36,6 @@ class ChatListItem extends StatelessWidget {
super.key, 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
@ -69,14 +44,9 @@ class ChatListItem extends StatelessWidget {
final typingText = room.getLocalizedTypingText(context); final typingText = room.getLocalizedTypingText(context);
final lastEvent = room.lastEvent; final lastEvent = room.lastEvent;
final ownMessage = lastEvent?.senderId == room.client.userID; final ownMessage = lastEvent?.senderId == room.client.userID;
final unread = room.isUnread || room.membership == Membership.invite; final unread = room.isUnread;
final directChatMatrixId = room.directChatMatrixID; final directChatMatrixId = room.directChatMatrixID;
final isDirectChat = directChatMatrixId != null; final isDirectChat = directChatMatrixId != null;
final unreadBubbleSize = unread || room.hasNewMessages
? room.notificationCount > 0
? 20.0
: 14.0
: 0.0;
final hasNotifications = room.notificationCount > 0; final hasNotifications = room.notificationCount > 0;
final backgroundColor = final backgroundColor =
activeChat ? theme.colorScheme.secondaryContainer : null; activeChat ? theme.colorScheme.secondaryContainer : null;
@ -93,8 +63,6 @@ class ChatListItem extends StatelessWidget {
: room.getState(EventTypes.RoomMember, lastEvent.senderId) == null; : room.getState(EventTypes.RoomMember, lastEvent.senderId) == null;
final space = this.space; final space = this.space;
final inviterMxId = room.getState(EventTypes.RoomMember, room.client.userID!)?.senderId;
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 8, horizontal: 8,
@ -116,9 +84,7 @@ class ChatListItem extends StatelessWidget {
duration: FluffyThemes.animationDuration, duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve, curve: FluffyThemes.animationCurve,
scale: hovered ? 1.1 : 1.0, scale: hovered ? 1.1 : 1.0,
child: child: SizedBox(
(!AppConfig.hideAvatarsInInvites || room.membership != Membership.invite)
? SizedBox(
width: Avatar.defaultSize, width: Avatar.defaultSize,
height: Avatar.defaultSize, height: Avatar.defaultSize,
child: Stack( child: Stack(
@ -195,7 +161,7 @@ class ChatListItem extends StatelessWidget {
), ),
], ],
), ),
) : null, ),
), ),
), ),
title: Row( title: Row(
@ -232,13 +198,12 @@ class ChatListItem extends StatelessWidget {
color: theme.colorScheme.primary, color: theme.colorScheme.primary,
), ),
), ),
if (!room.isSpace && if (!room.isSpace && room.membership != Membership.invite)
lastEvent != null &&
room.membership != Membership.invite)
Padding( Padding(
padding: const EdgeInsets.only(left: 4.0), padding: const EdgeInsets.only(left: 4.0),
child: Text( child: Text(
lastEvent.originServerTs.localizedTimeShort(context), room.latestEventReceivedTime
.localizedTimeShort(context),
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
color: theme.colorScheme.outline, color: theme.colorScheme.outline,
@ -253,7 +218,7 @@ class ChatListItem extends StatelessWidget {
children: <Widget>[ children: <Widget>[
if (typingText.isEmpty && if (typingText.isEmpty &&
ownMessage && ownMessage &&
(room.lastEvent?.status.isSending ?? false)) ...[ room.lastEvent?.status.isSending == true) ...[
const SizedBox( const SizedBox(
width: 16, width: 16,
height: 16, height: 16,
@ -321,7 +286,7 @@ class ChatListItem extends StatelessWidget {
), ),
builder: (context, snapshot) => Text( builder: (context, snapshot) => Text(
room.membership == Membership.invite room.membership == Membership.invite
? "${room ? room
.getState( .getState(
EventTypes.RoomMember, EventTypes.RoomMember,
room.client.userID!, room.client.userID!,
@ -331,9 +296,9 @@ class ChatListItem extends StatelessWidget {
(isDirectChat (isDirectChat
? L10n.of(context).newChatRequest ? L10n.of(context).newChatRequest
: L10n.of(context) : L10n.of(context)
.inviteGroupChat)}${inviterMxId != null ? " ($inviterMxId)" : ""}" .inviteGroupChat)
: snapshot.data ?? : snapshot.data ??
L10n.of(context).emptyChat, L10n.of(context).noMessagesYet,
softWrap: false, softWrap: false,
maxLines: room.notificationCount >= 1 ? 2 : 1, maxLines: room.notificationCount >= 1 ? 2 : 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
@ -349,48 +314,33 @@ class ChatListItem extends StatelessWidget {
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
AnimatedContainer( UnreadBubble(room: room),
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(),
),
], ],
), ),
onTap: onTap, onTap: onTap,
trailing: onForget == null 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( : IconButton(
icon: const Icon(Icons.delete_outlined), icon: const Icon(Icons.delete_outlined),
onPressed: onForget, onPressed: onForget,

View File

@ -1,27 +1,40 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart' as sdk; import 'package:matrix/matrix.dart' as sdk;
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:extera_next/config/app_config.dart'; import 'package:extera_next/config/app_config.dart';
import 'package:extera_next/config/themes.dart'; import 'package:extera_next/config/themes.dart';
import 'package:extera_next/pages/chat_list/chat_list_item.dart'; import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:extera_next/pages/chat_list/search_title.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/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/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/public_room_dialog.dart';
import 'package:extera_next/widgets/adaptive_dialogs/show_modal_action_popup.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_ok_cancel_alert_dialog.dart';
import 'package:extera_next/widgets/adaptive_dialogs/show_text_input_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/avatar.dart';
import 'package:extera_next/widgets/future_loading_dialog.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'; import 'package:extera_next/widgets/matrix.dart';
enum AddRoomType { chat, subspace } enum AddRoomType { chat, subspace }
enum SpaceChildAction { edit, moveToSpace, removeFromSpace }
enum SpaceActions {
settings,
invite,
members,
leave,
}
class SpaceView extends StatefulWidget { class SpaceView extends StatefulWidget {
final String spaceId; final String spaceId;
final void Function() onBack; final void Function() onBack;
@ -45,7 +58,7 @@ class SpaceView extends StatefulWidget {
} }
class _SpaceViewState extends State<SpaceView> { class _SpaceViewState extends State<SpaceView> {
final List<SpaceRoomsChunk> _discoveredChildren = []; final List<SpaceRoomsChunk$2> _discoveredChildren = [];
final TextEditingController _filterController = TextEditingController(); final TextEditingController _filterController = TextEditingController();
String? _nextBatch; String? _nextBatch;
bool _noMoreRooms = false; bool _noMoreRooms = false;
@ -58,9 +71,28 @@ class _SpaceViewState extends State<SpaceView> {
} }
void _loadHierarchy() async { 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; 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(() { setState(() {
_isLoading = true; _isLoading = true;
}); });
@ -74,16 +106,25 @@ class _SpaceViewState extends State<SpaceView> {
); );
if (!mounted) return; if (!mounted) return;
setState(() { setState(() {
if (_nextBatch == null) _discoveredChildren.clear();
_nextBatch = hierarchy.nextBatch; _nextBatch = hierarchy.nextBatch;
if (hierarchy.nextBatch == null) { if (hierarchy.nextBatch == null) {
_noMoreRooms = true; _noMoreRooms = true;
} }
_discoveredChildren.addAll( _discoveredChildren.addAll(
hierarchy.rooms hierarchy.rooms.where((room) => room.roomId != widget.spaceId),
.where((c) => room.client.getRoomById(c.roomId) == null),
); );
_isLoading = false; _isLoading = false;
}); });
if (_nextBatch == null) {
matrix.store.setStringList(
cacheKey,
_discoveredChildren
.map((child) => jsonEncode(child.toJson()))
.toList(),
);
}
} catch (e, s) { } catch (e, s) {
Logs().w('Unable to load hierarchy', e, s); Logs().w('Unable to load hierarchy', e, s);
if (!mounted) return; 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 client = Matrix.of(context).client;
final space = client.getRoomById(widget.spaceId); final space = client.getRoomById(widget.spaceId);
@ -111,9 +152,7 @@ class _SpaceViewState extends State<SpaceView> {
), ),
); );
if (mounted && joined == true) { if (mounted && joined == true) {
setState(() { setState(() {});
_discoveredChildren.remove(item);
});
} }
} }
@ -129,6 +168,10 @@ class _SpaceViewState extends State<SpaceView> {
await space?.postLoad(); await space?.postLoad();
context.push('/rooms/${widget.spaceId}/invite'); context.push('/rooms/${widget.spaceId}/invite');
break; break;
case SpaceActions.members:
await space?.postLoad();
context.push('/rooms/${widget.spaceId}/details/members');
break;
case SpaceActions.leave: case SpaceActions.leave:
final confirmed = await showOkCancelAlertDialog( final confirmed = await showOkCancelAlertDialog(
context: context, context: context,
@ -151,27 +194,11 @@ class _SpaceViewState extends State<SpaceView> {
} }
} }
void _addChatOrSubspace() async { void _addChatOrSubspace(AddRoomType roomType) 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;
final names = await showTextInputDialog( final names = await showTextInputDialog(
context: context, context: context,
title: roomType == AddRoomType.subspace title: roomType == AddRoomType.subspace
? L10n.of(context).createNewSpace ? L10n.of(context).newSubSpace
: L10n.of(context).createGroup, : L10n.of(context).createGroup,
hintText: roomType == AddRoomType.subspace hintText: roomType == AddRoomType.subspace
? L10n.of(context).spaceName ? L10n.of(context).spaceName
@ -196,29 +223,169 @@ class _SpaceViewState extends State<SpaceView> {
late final String roomId; late final String roomId;
final activeSpace = client.getRoomById(widget.spaceId)!; final activeSpace = client.getRoomById(widget.spaceId)!;
await activeSpace.postLoad(); await activeSpace.postLoad();
final isPublicSpace = activeSpace.joinRules == JoinRules.public;
if (roomType == AddRoomType.subspace) { if (roomType == AddRoomType.subspace) {
roomId = await client.createSpace( roomId = await client.createSpace(
name: names, name: names,
visibility: activeSpace.joinRules == JoinRules.public visibility:
? sdk.Visibility.public isPublicSpace ? sdk.Visibility.public : sdk.Visibility.private,
: sdk.Visibility.private,
); );
} else { } else {
roomId = await client.createGroupChat( roomId = await client.createGroupChat(
enableEncryption: !isPublicSpace,
groupName: names, groupName: names,
preset: activeSpace.joinRules == JoinRules.public preset: isPublicSpace
? CreateRoomPreset.publicChat ? CreateRoomPreset.publicChat
: CreateRoomPreset.privateChat, : CreateRoomPreset.privateChat,
visibility: activeSpace.joinRules == JoinRules.public visibility:
? sdk.Visibility.public isPublicSpace ? sdk.Visibility.public : sdk.Visibility.private,
: 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); await activeSpace.setSpaceChild(roomId);
}, },
); );
if (result.error != null) return; 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 @override
@ -228,6 +395,11 @@ class _SpaceViewState extends State<SpaceView> {
final room = Matrix.of(context).client.getRoomById(widget.spaceId); final room = Matrix.of(context).client.getRoomById(widget.spaceId);
final displayname = final displayname =
room?.getLocalizedDisplayname() ?? L10n.of(context).nothingFound; room?.getLocalizedDisplayname() ?? L10n.of(context).nothingFound;
const avatarSize = Avatar.defaultSize / 1.5;
final isAdmin = room?.canChangeStateEvent(
EventTypes.SpaceChild,
) ==
true;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
leading: FluffyThemes.isColumnMode(context) leading: FluffyThemes.isColumnMode(context)
@ -242,6 +414,7 @@ class _SpaceViewState extends State<SpaceView> {
title: ListTile( title: ListTile(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
leading: Avatar( leading: Avatar(
size: avatarSize,
mxContent: room?.avatar, mxContent: room?.avatar,
name: displayname, name: displayname,
border: BorderSide(width: 1, color: theme.dividerColor), border: BorderSide(width: 1, color: theme.dividerColor),
@ -252,19 +425,40 @@ class _SpaceViewState extends State<SpaceView> {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, 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: [ 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>( PopupMenuButton<SpaceActions>(
useRootNavigator: true,
onSelected: _onSpaceAction, onSelected: _onSpaceAction,
itemBuilder: (context) => [ itemBuilder: (context) => [
PopupMenuItem( 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( PopupMenuItem(
value: SpaceActions.leave, value: SpaceActions.leave,
child: Row( 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 body: room == null
? const Center( ? const Center(
child: Icon( child: Icon(
@ -331,9 +530,11 @@ class _SpaceViewState extends State<SpaceView> {
.whereType<String>() .whereType<String>()
.toSet(); .toSet();
final joinedRooms = room.client.rooms final joinedRooms = Map.fromEntries(
room.client.rooms
.where((room) => childrenIds.remove(room.id)) .where((room) => childrenIds.remove(room.id))
.toList(); .map((room) => MapEntry(room.id, room)),
);
final joinedParents = room.spaceParents final joinedParents = room.spaceParents
.map((parent) { .map((parent) {
@ -348,7 +549,6 @@ class _SpaceViewState extends State<SpaceView> {
slivers: [ slivers: [
SliverAppBar( SliverAppBar(
floating: true, floating: true,
toolbarHeight: 72,
scrolledUnderElevation: 0, scrolledUnderElevation: 0,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
@ -358,11 +558,6 @@ class _SpaceViewState extends State<SpaceView> {
textInputAction: TextInputAction.search, textInputAction: TextInputAction.search,
decoration: InputDecoration( decoration: InputDecoration(
filled: true, filled: true,
fillColor: theme.colorScheme.secondaryContainer,
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(99),
),
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
hintText: L10n.of(context).search, hintText: L10n.of(context).search,
hintStyle: TextStyle( hintStyle: TextStyle(
@ -422,42 +617,11 @@ class _SpaceViewState extends State<SpaceView> {
}, },
), ),
SliverList.builder( SliverList.builder(
itemCount: joinedRooms.length, itemCount: _discoveredChildren.length + 1,
itemBuilder: (context, i) { 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 (i == _discoveredChildren.length) {
if (_noMoreRooms) { if (_noMoreRooms) {
return Padding( return const SizedBox.shrink();
padding: const EdgeInsets.all(12.0),
child: Center(
child: Text(
L10n.of(context).noMoreChatsFound,
style: const TextStyle(fontSize: 13),
),
),
);
} }
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -467,11 +631,7 @@ class _SpaceViewState extends State<SpaceView> {
child: TextButton( child: TextButton(
onPressed: _isLoading ? null : _loadHierarchy, onPressed: _isLoading ? null : _loadHierarchy,
child: _isLoading child: _isLoading
? LinearProgressIndicator( ? const CircularProgressIndicator.adaptive()
borderRadius: BorderRadius.circular(
AppConfig.borderRadius,
),
)
: Text(L10n.of(context).loadMore), : Text(L10n.of(context).loadMore),
), ),
); );
@ -483,6 +643,7 @@ class _SpaceViewState extends State<SpaceView> {
if (!displayname.toLowerCase().contains(filter)) { if (!displayname.toLowerCase().contains(filter)) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
final joinedRoom = joinedRooms[item.roomId];
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 8, horizontal: 8,
@ -492,51 +653,83 @@ class _SpaceViewState extends State<SpaceView> {
borderRadius: borderRadius:
BorderRadius.circular(AppConfig.borderRadius), BorderRadius.circular(AppConfig.borderRadius),
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
child: ListTile( color: joinedRoom != null &&
widget.activeChat == joinedRoom.id
? theme.colorScheme.secondaryContainer
: Colors.transparent,
child: HoverBuilder(
builder: (context, hovered) => ListTile(
visualDensity: visualDensity:
const VisualDensity(vertical: -0.5), const VisualDensity(vertical: -0.5),
contentPadding: contentPadding:
const EdgeInsets.symmetric(horizontal: 8), const EdgeInsets.symmetric(horizontal: 8),
onTap: () => _joinChildRoom(item), onTap: joinedRoom != null
leading: Avatar( ? () => 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, 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: item.roomType == 'm.space'
? BorderRadius.circular( ? BorderRadius.circular(
AppConfig.borderRadius / 2, AppConfig.borderRadius / 4,
) )
: null, : null,
), ),
title: Row( title: Row(
children: [ children: [
Expanded( Expanded(
child: Opacity(
opacity: joinedRoom == null ? 0.5 : 1,
child: Text( child: Text(
displayname, displayname,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, 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

@ -30,7 +30,9 @@ class PublicRoomDialog extends StatelessWidget {
final result = await showFutureLoadingDialog<String>( final result = await showFutureLoadingDialog<String>(
context: context, context: context,
future: () async { 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; return chunk.roomId;
} }
final roomId = chunk != null && knock final roomId = chunk != null && knock

View File

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

View File

@ -1201,9 +1201,11 @@ packages:
matrix: matrix:
dependency: "direct main" dependency: "direct main"
description: description:
path: "/home/officialdakari/repos/matrix-dart-sdk" path: "."
relative: false ref: main
source: path resolved-ref: f678376e3a4825a6e1a00d89585d6bae0843950c
url: "https://git.extera.xyz/OfficialDakari/matrix-dart-sdk.git"
source: git
version: "3.0.1" version: "3.0.1"
meta: meta:
dependency: transitive dependency: transitive