diff --git a/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png index 3a514ed..6050c62 100644 Binary files a/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png and b/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png b/android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png index 87fd263..6050c62 100644 Binary files a/android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png differ diff --git a/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png index 1116df6..917ba2e 100644 Binary files a/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png and b/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png b/android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png index 4fcb9e7..917ba2e 100644 Binary files a/android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png index 7093798..e5412f6 100644 Binary files a/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png index 0dd0c27..e5412f6 100644 Binary files a/android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png index 69072f1..3302707 100644 Binary files a/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png index ad85e0b..3302707 100644 Binary files a/android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png index f03ed0d..363a4e8 100644 Binary files a/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png index 8e0ffc8..363a4e8 100644 Binary files a/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index d17b8e5..7492885 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index efeaadb..6d2afdc 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 7849fbe..1d0276e 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 547f4bb..91250aa 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 6c34cd9..024ea15 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 5941666..89f33b4 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index 497580f..d7096a2 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index db6d7d3..5a490c5 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index 1db7f9c..8c77502 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index a20d290..6329715 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png index d6090ea..1208ff6 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index 74dc965..a7a270c 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index db6d7d3..5a490c5 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index fcf1601..0eb2fa0 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index 4632a7f..e2df646 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png index 2f1fad6..75fe362 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png index 8675f18..20bcec6 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png index 7e42c84..ebb2188 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png index f57322e..3bd433d 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index 4632a7f..e2df646 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index 1ab5734..289c2dc 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png index d17b8e5..7492885 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png index 547f4bb..91250aa 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index 0f776d9..b115759 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index cff034b..8f18716 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index 10bdca0..576d931 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index fd28ce3..3f77921 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -2868,5 +2868,50 @@ "@selectASpace": { "type": "String", "placeholders": {} + }, + "newPost": "New post", + "@newPost": { + "type": "String", + "placeholders": {} + }, + "postPhoto": "Post photo", + "@postPhoto": { + "type": "String", + "placeholders": {} + }, + "postVideo": "Post video", + "@postVideo": { + "type": "String", + "placeholders": {} + }, + "noPostsYet": "No posts yet. Share the first photo or video!", + "@noPostsYet": { + "type": "String", + "placeholders": {} + }, + "noCommentsYet": "No comments yet. Be the first to comment!", + "@noCommentsYet": { + "type": "String", + "placeholders": {} + }, + "like": "Like", + "@like": { + "type": "String", + "placeholders": {} + }, + "unlike": "Unlike", + "@unlike": { + "type": "String", + "placeholders": {} + }, + "editCaption": "Edit caption", + "@editCaption": { + "type": "String", + "placeholders": {} + }, + "captionHint": "Caption", + "@captionHint": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 524a04f..466cc05 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1997,5 +1997,50 @@ "@unbanFromChat": { "type": "String", "placeholders": {} + }, + "newPost": "Nova publicação", + "@newPost": { + "type": "String", + "placeholders": {} + }, + "postPhoto": "Postar foto", + "@postPhoto": { + "type": "String", + "placeholders": {} + }, + "postVideo": "Postar vídeo", + "@postVideo": { + "type": "String", + "placeholders": {} + }, + "noPostsYet": "Nenhuma publicação ainda. Compartilhe a primeira foto ou vídeo!", + "@noPostsYet": { + "type": "String", + "placeholders": {} + }, + "noCommentsYet": "Nenhum comentário ainda. Seja o primeiro a comentar!", + "@noCommentsYet": { + "type": "String", + "placeholders": {} + }, + "like": "Curtir", + "@like": { + "type": "String", + "placeholders": {} + }, + "unlike": "Descurtir", + "@unlike": { + "type": "String", + "placeholders": {} + }, + "editCaption": "Editar legenda", + "@editCaption": { + "type": "String", + "placeholders": {} + }, + "captionHint": "Legenda", + "@captionHint": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_BR.arb b/lib/l10n/intl_pt_BR.arb index 15b860b..618726b 100644 --- a/lib/l10n/intl_pt_BR.arb +++ b/lib/l10n/intl_pt_BR.arb @@ -2520,5 +2520,50 @@ "@selectASpace": { "type": "String", "placeholders": {} + }, + "newPost": "Nova publicação", + "@newPost": { + "type": "String", + "placeholders": {} + }, + "postPhoto": "Postar foto", + "@postPhoto": { + "type": "String", + "placeholders": {} + }, + "postVideo": "Postar vídeo", + "@postVideo": { + "type": "String", + "placeholders": {} + }, + "noPostsYet": "Nenhuma publicação ainda. Compartilhe a primeira foto ou vídeo!", + "@noPostsYet": { + "type": "String", + "placeholders": {} + }, + "noCommentsYet": "Nenhum comentário ainda. Seja o primeiro a comentar!", + "@noCommentsYet": { + "type": "String", + "placeholders": {} + }, + "like": "Curtir", + "@like": { + "type": "String", + "placeholders": {} + }, + "unlike": "Descurtir", + "@unlike": { + "type": "String", + "placeholders": {} + }, + "editCaption": "Editar legenda", + "@editCaption": { + "type": "String", + "placeholders": {} + }, + "captionHint": "Legenda", + "@captionHint": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_PT.arb b/lib/l10n/intl_pt_PT.arb index 8139eb0..92add3c 100644 --- a/lib/l10n/intl_pt_PT.arb +++ b/lib/l10n/intl_pt_PT.arb @@ -2200,5 +2200,50 @@ "@discardEdits": { "type": "String", "placeholders": {} + }, + "newPost": "Nova publicação", + "@newPost": { + "type": "String", + "placeholders": {} + }, + "postPhoto": "Publicar foto", + "@postPhoto": { + "type": "String", + "placeholders": {} + }, + "postVideo": "Publicar vídeo", + "@postVideo": { + "type": "String", + "placeholders": {} + }, + "noPostsYet": "Ainda não há publicações. Partilhe a primeira foto ou vídeo!", + "@noPostsYet": { + "type": "String", + "placeholders": {} + }, + "noCommentsYet": "Ainda não há comentários. Seja o primeiro a comentar!", + "@noCommentsYet": { + "type": "String", + "placeholders": {} + }, + "like": "Gostar", + "@like": { + "type": "String", + "placeholders": {} + }, + "unlike": "Não gostar", + "@unlike": { + "type": "String", + "placeholders": {} + }, + "editCaption": "Editar legenda", + "@editCaption": { + "type": "String", + "placeholders": {} + }, + "captionHint": "Legenda", + "@captionHint": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 39d5f03..739b886 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -10,6 +10,7 @@ import 'package:collection/collection.dart'; import 'package:desktop_drop/desktop_drop.dart'; import 'package:emoji_picker_flutter/emoji_picker_flutter.dart'; import 'package:file_picker/file_picker.dart'; +import 'package:tatchat/config/app_config.dart'; import 'package:tatchat/config/setting_keys.dart'; import 'package:tatchat/config/themes.dart'; import 'package:tatchat/l10n/l10n.dart'; @@ -19,6 +20,7 @@ import 'package:tatchat/pages/chat/start_poll_bottom_sheet.dart'; import 'package:tatchat/pages/chat/trust_user_key_dialog.dart'; import 'package:tatchat/pages/chat/utils/web_file_to_x_file.dart'; import 'package:tatchat/pages/chat_details/chat_details.dart'; +import 'package:tatchat/pages/mural/mural_chat_page.dart'; import 'package:tatchat/utils/adaptive_bottom_sheet.dart'; import 'package:tatchat/utils/error_reporter.dart'; import 'package:tatchat/utils/file_selector.dart'; @@ -83,6 +85,10 @@ class ChatPage extends StatelessWidget { ); } + if (room.tags.keys.contains(AppConfig.muralTag)) { + return MuralChatPage(key: Key('mural_page_$roomId'), roomId: roomId); + } + return ChatPageWithRoom( key: Key('chat_page_${roomId}_${eventId}_$action'), room: room, diff --git a/lib/pages/chat_list/chat_list_view.dart b/lib/pages/chat_list/chat_list_view.dart index a4fde5a..bf64866 100644 --- a/lib/pages/chat_list/chat_list_view.dart +++ b/lib/pages/chat_list/chat_list_view.dart @@ -34,54 +34,59 @@ class ChatListView extends StatelessWidget { return; } }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (isColumnMode) - ChatTopTabBar( - currentTab: homeTab, - onTabSelected: controller.setHomeTab, - ), - Expanded( - child: Scaffold( - body: SafeArea( - top: false, - bottom: false, - left: false, - right: false, - child: IndexedStack( - index: homeTab.index, - children: [ - ChatTabScaffold( - tab: HomeTab.chats, - isSearchMode: controller.isSearchMode, - onSearchPressed: controller.startSearch, - onSearchCancelled: () => controller.cancelSearch(), - searchController: controller.searchController, - searchFocusNode: controller.searchFocusNode, - onSearchChanged: (text) => - controller.onSearchEnter(text, globalSearch: true), - controller: controller, - child: ChatListViewBody(controller), - ), - ChannelsTab(controller), - SpacesTab(controller), - MuralsTab(controller), - ], - ), + child: SafeArea( + bottom: false, + left: false, + right: false, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (isColumnMode) + ChatTopTabBar( + currentTab: homeTab, + onTabSelected: controller.setHomeTab, + ), + Expanded( + child: Scaffold( + body: SafeArea( + top: false, + bottom: false, + left: false, + right: false, + child: IndexedStack( + index: homeTab.index, + children: [ + ChatTabScaffold( + tab: HomeTab.chats, + isSearchMode: controller.isSearchMode, + onSearchPressed: controller.startSearch, + onSearchCancelled: () => controller.cancelSearch(), + searchController: controller.searchController, + searchFocusNode: controller.searchFocusNode, + onSearchChanged: (text) => + controller.onSearchEnter(text, globalSearch: true), + controller: controller, + child: ChatListViewBody(controller), + ), + ChannelsTab(controller), + SpacesTab(controller), + MuralsTab(controller), + ], + ), + ), + floatingActionButton: !controller.isSearchMode && !isColumnMode + ? const StartChatFab() + : const SizedBox.shrink(), + bottomNavigationBar: isColumnMode + ? null + : ChatBottomTabBar( + currentTab: homeTab, + onTabSelected: controller.setHomeTab, + ), ), - floatingActionButton: !controller.isSearchMode && !isColumnMode - ? const StartChatFab() - : const SizedBox.shrink(), - bottomNavigationBar: isColumnMode - ? null - : ChatBottomTabBar( - currentTab: homeTab, - onTabSelected: controller.setHomeTab, - ), ), - ), - ], + ], + ), ), ); } diff --git a/lib/pages/mural/mural_chat_page.dart b/lib/pages/mural/mural_chat_page.dart new file mode 100644 index 0000000..5ee880f --- /dev/null +++ b/lib/pages/mural/mural_chat_page.dart @@ -0,0 +1,911 @@ +// SPDX-FileCopyrightText: 2019-Present Christian Kußowski +// SPDX-FileCopyrightText: 2019-Present Contributors to FluffyChat +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +import 'package:go_router/go_router.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:tatchat/l10n/l10n.dart'; +import 'package:tatchat/pages/chat/send_file_dialog.dart'; +import 'package:tatchat/pages/image_viewer/video_player.dart'; +import 'package:tatchat/utils/adaptive_bottom_sheet.dart'; +import 'package:tatchat/utils/matrix_sdk_extensions/matrix_locals.dart'; +import 'package:tatchat/utils/stream_extension.dart'; +import 'package:tatchat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; +import 'package:tatchat/widgets/avatar.dart'; +import 'package:tatchat/widgets/matrix.dart'; +import 'package:tatchat/widgets/mxc_image.dart'; +import 'package:material_ui/material_ui.dart'; +import 'package:matrix/matrix.dart'; + +const _muralMediaTypes = { + MessageTypes.Image, + MessageTypes.Video, + MessageTypes.Sticker, +}; + +const _likeKey = '👍'; + +class MuralChatPage extends StatelessWidget { + final String roomId; + + const MuralChatPage({super.key, required this.roomId}); + + @override + Widget build(BuildContext context) { + final room = Matrix.of(context).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), + ), + ), + ); + } + return MuralChatView(key: Key('mural_chat_page_$roomId'), room: room); + } +} + +class MuralChatView extends StatefulWidget { + final Room room; + + const MuralChatView({super.key, required this.room}); + + @override + State createState() => _MuralChatViewState(); +} + +class _MuralChatViewState extends State { + Room get room => + Matrix.of(context).client.getRoomById(widget.room.id) ?? widget.room; + + Timeline? timeline; + + List get _mediaEvents => timeline == null + ? const [] + : timeline!.events + .where( + (e) => !e.redacted && _muralMediaTypes.contains(e.messageType), + ) + .toList(); + + bool _descriptionExpanded = false; + + @override + void initState() { + super.initState(); + _getTimeline(); + } + + @override + void dispose() { + timeline?.cancelSubscriptions(); + super.dispose(); + } + + void updateView() { + if (!mounted) return; + setState(() {}); + } + + Future _getTimeline() async { + timeline?.cancelSubscriptions(); + timeline = await room.getTimeline(onUpdate: updateView); + if (!mounted) return; + setState(() {}); + } + + Future _sendFiles(List? files) async { + if (files == null || files.isEmpty || !mounted) return; + await showAdaptiveDialog( + context: context, + builder: (c) => SendFileDialog( + files: files, + room: room, + outerContext: context, + threadRootEventId: null, + threadLastEventId: null, + ), + ); + } + + Future _postPhoto() async { + final file = await ImagePicker().pickImage(source: ImageSource.gallery); + if (!mounted) return; + await _sendFiles(file == null ? null : [file]); + } + + Future _takePhoto() async { + final file = await ImagePicker().pickImage(source: ImageSource.camera); + if (!mounted) return; + await _sendFiles(file == null ? null : [file]); + } + + Future _postVideo() async { + final file = await ImagePicker().pickVideo(source: ImageSource.gallery); + if (!mounted) return; + await _sendFiles(file == null ? null : [file]); + } + + Future _recordVideo() async { + final file = await ImagePicker().pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1), + ); + if (!mounted) return; + await _sendFiles(file == null ? null : [file]); + } + + Future _showPostSheet() async { + final l10n = L10n.of(context); + await showAdaptiveBottomSheet( + context: context, + builder: (context) => SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 8, + ), + child: Text( + l10n.newPost, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + ListTile( + leading: const Icon(Icons.photo_library_outlined), + title: Text(l10n.postPhoto), + onTap: () { + Navigator.of(context, rootNavigator: true).pop(); + _postPhoto(); + }, + ), + ListTile( + leading: const Icon(Icons.photo_camera_outlined), + title: Text(l10n.takeAPhoto), + onTap: () { + Navigator.of(context, rootNavigator: true).pop(); + _takePhoto(); + }, + ), + ListTile( + leading: const Icon(Icons.video_library_outlined), + title: Text(l10n.postVideo), + onTap: () { + Navigator.of(context, rootNavigator: true).pop(); + _postVideo(); + }, + ), + ListTile( + leading: const Icon(Icons.videocam_outlined), + title: Text(l10n.recordAVideo), + onTap: () { + Navigator.of(context, rootNavigator: true).pop(); + _recordVideo(); + }, + ), + ], + ), + ), + ), + ); + } + + void _openMedia(Event event) { + final timeline = this.timeline; + if (timeline == null) return; + final theme = Theme.of(context); + showModalBottomSheet( + context: context, + useSafeArea: true, + isScrollControlled: true, + backgroundColor: theme.colorScheme.surface, + builder: (context) => FractionallySizedBox( + heightFactor: 0.92, + child: _PostViewer( + key: Key('post_viewer_${event.eventId}'), + event: event, + room: room, + timeline: timeline, + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final l10n = L10n.of(context); + final coverColor = theme.colorScheme.secondaryContainer; + final gridCellSize = (MediaQuery.sizeOf(context).width - 4) / 3; + final events = _mediaEvents; + final isAdmin = room.ownPowerLevel >= PowerLevel.admin; + + return Scaffold( + extendBodyBehindAppBar: true, + floatingActionButton: isAdmin + ? FloatingActionButton( + onPressed: _showPostSheet, + tooltip: l10n.newPost, + child: const Icon(Icons.add_photo_alternate_outlined), + ) + : null, + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + foregroundColor: Colors.white, + iconTheme: const IconThemeData(color: Colors.white), + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _Header( + room: room, + coverColor: coverColor, + descriptionExpanded: _descriptionExpanded, + onToggleDescription: () => + setState(() => _descriptionExpanded = !_descriptionExpanded), + onEditInfo: () => context.go('/rooms/${room.id}/details'), + ), + const Divider(height: 1), + Expanded( + child: events.isEmpty + ? Center( + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.photo_library_outlined, + size: 72, + color: theme.colorScheme.secondary, + ), + const SizedBox(height: 12), + Text( + l10n.noPostsYet, + textAlign: TextAlign.center, + style: TextStyle( + color: theme.colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ), + ) + : GridView.builder( + padding: const EdgeInsets.all(2), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + mainAxisSpacing: 2, + crossAxisSpacing: 2, + ), + itemCount: events.length, + itemBuilder: (context, i) { + final event = events[i]; + return _MediaCell( + event: event, + client: room.client, + cellSize: gridCellSize, + onTap: () => _openMedia(event), + ); + }, + ), + ), + ], + ), + ); + } +} + +class _Header extends StatelessWidget { + final Room room; + final Color coverColor; + final bool descriptionExpanded; + final VoidCallback onToggleDescription; + final VoidCallback onEditInfo; + + const _Header({ + required this.room, + required this.coverColor, + required this.descriptionExpanded, + required this.onToggleDescription, + required this.onEditInfo, + }); + + @override + Widget build(BuildContext context) { + const coverHeight = 200.0; + const avatarSize = 72.0; + final theme = Theme.of(context); + final l10n = L10n.of(context); + final displayName = room.getLocalizedDisplayname(MatrixLocals(l10n)); + final creatorId = room.creatorUserIds.isEmpty + ? null + : room.creatorUserIds.first; + final creatorState = creatorId == null + ? null + : room.getState(EventTypes.RoomMember, creatorId); + final creatorName = + creatorState?.content.tryGet('displayname') ?? creatorId; + final creatorAvatar = Uri.tryParse( + creatorState?.content.tryGet('avatar_url') ?? '', + ); + final topic = room.topic.trim(); + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Stack( + clipBehavior: Clip.none, + children: [ + SizedBox( + height: coverHeight, + child: Stack( + fit: StackFit.expand, + children: [ + DecoratedBox( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + coverColor, + theme.colorScheme.primaryContainer, + ], + ), + ), + ), + if (room.avatar != null) + MxcImage( + uri: room.avatar, + client: room.client, + width: MediaQuery.sizeOf(context).width, + height: coverHeight, + fit: BoxFit.cover, + ), + DecoratedBox( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black.withValues(alpha: 0.45), + ], + stops: const [0.55, 1.0], + ), + ), + ), + ], + ), + ), + Positioned( + left: 16, + bottom: -avatarSize / 2, + child: Avatar( + mxContent: creatorAvatar, + name: creatorName, + size: avatarSize, + client: room.client, + shapeBorder: CircleBorder( + side: BorderSide(width: 3, color: theme.colorScheme.surface), + ), + ), + ), + Positioned( + left: 16 + avatarSize + 12, + right: 8, + bottom: -avatarSize / 2, + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text( + displayName, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: theme.textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + color: Colors.white, + shadows: const [ + Shadow( + color: Colors.black54, + blurRadius: 6, + offset: Offset(0, 1), + ), + ], + ), + ), + ), + ), + DecoratedBox( + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.35), + shape: BoxShape.circle, + ), + child: IconButton( + tooltip: l10n.settings, + icon: const Icon(Icons.more_vert), + color: Colors.white, + onPressed: onEditInfo, + ), + ), + ], + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.fromLTRB(16, 48, 16, 4), + child: _ExpandableDescription( + text: topic, + expanded: descriptionExpanded, + onToggle: onToggleDescription, + ), + ), + ], + ); + } +} + +class _ExpandableDescription extends StatelessWidget { + final String? text; + final bool expanded; + final VoidCallback onToggle; + + const _ExpandableDescription({ + required this.text, + required this.expanded, + required this.onToggle, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final l10n = L10n.of(context); + final text = this.text?.trim(); + if (text == null || text.isEmpty) return const SizedBox.shrink(); + final style = theme.textTheme.bodyMedium?.copyWith(height: 1.4); + return LayoutBuilder( + builder: (context, constraints) { + final painter = TextPainter( + text: TextSpan(text: text, style: style), + maxLines: 4, + textDirection: TextDirection.ltr, + )..layout(maxWidth: constraints.maxWidth); + final exceeds = painter.didExceedMaxLines; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + text, + style: style, + maxLines: expanded ? null : 4, + overflow: expanded ? null : TextOverflow.ellipsis, + ), + if (exceeds) + TextButton( + onPressed: onToggle, + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + minimumSize: const Size(0, 32), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + child: Text(expanded ? l10n.showLess : l10n.showMore), + ), + ], + ); + }, + ); + } +} + +class _MediaCell extends StatelessWidget { + final Event event; + final Client client; + final double cellSize; + final VoidCallback onTap; + + const _MediaCell({ + required this.event, + required this.client, + required this.cellSize, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final isVideo = event.messageType == MessageTypes.Video; + final thumbnailUri = isVideo + ? Uri.tryParse( + event.content + .tryGetMap('info') + ?.tryGet('thumbnail_url') ?? + '', + ) + : null; + return GestureDetector( + onTap: onTap, + child: ColoredBox( + color: theme.colorScheme.surfaceContainerHighest, + child: Stack( + fit: StackFit.expand, + children: [ + if (isVideo) + thumbnailUri == null + ? ColoredBox( + color: theme.colorScheme.surfaceContainerHighest, + child: const Center( + child: Icon( + Icons.video_library_outlined, + size: 36, + color: Colors.white70, + ), + ), + ) + : MxcImage( + uri: thumbnailUri, + client: client, + width: cellSize, + height: cellSize, + fit: BoxFit.cover, + ) + else + MxcImage( + key: ValueKey(event.eventId), + event: event, + width: cellSize, + height: cellSize, + fit: BoxFit.cover, + isThumbnail: true, + ), + if (isVideo) + const Center( + child: Icon( + Icons.play_circle_fill, + size: 40, + color: Colors.white, + ), + ), + ], + ), + ), + ); + } +} + +class _PostViewer extends StatefulWidget { + final Event event; + final Room room; + final Timeline timeline; + + const _PostViewer({ + super.key, + required this.event, + required this.room, + required this.timeline, + }); + + @override + State<_PostViewer> createState() => _PostViewerState(); +} + +class _PostViewerState extends State<_PostViewer> { + final TextEditingController _commentController = TextEditingController(); + bool _sending = false; + + Event get event => widget.event; + + @override + void dispose() { + _commentController.dispose(); + super.dispose(); + } + + Event? _myReaction() { + final reactions = event.aggregatedEvents( + widget.timeline, + RelationshipTypes.reaction, + ); + for (final reaction in reactions) { + final senderId = reaction.senderId; + if (senderId != widget.room.client.userID) continue; + final key = reaction.content + .tryGetMap('m.relates_to') + ?.tryGet('key'); + if (key == _likeKey) return reaction; + } + return null; + } + + int get _likeCount => event + .aggregatedEvents(widget.timeline, RelationshipTypes.reaction) + .where( + (e) => + e.content + .tryGetMap('m.relates_to') + ?.tryGet('key') == + _likeKey, + ) + .length; + + Future _toggleLike() async { + final reaction = _myReaction(); + if (reaction != null) { + await reaction.redactEvent(); + } else { + await widget.room.sendReaction(event.eventId, _likeKey); + } + } + + Future _editCaption() async { + final l10n = L10n.of(context); + final currentBody = event.getDisplayEvent(widget.timeline).body.trim(); + final result = await showTextInputDialog( + context: context, + title: l10n.editCaption, + okLabel: l10n.ok, + cancelLabel: l10n.cancel, + hintText: l10n.captionHint, + initialText: currentBody, + maxLines: 3, + ); + if (result == null || + result.trim().isEmpty || + result.trim() == currentBody) { + return; + } + await widget.room.sendTextEvent(result.trim(), editEventId: event.eventId); + } + + Future _sendComment() async { + final text = _commentController.text.trim(); + if (text.isEmpty || _sending) return; + _commentController.clear(); + setState(() => _sending = true); + try { + await widget.room.sendTextEvent(text, threadRootEventId: event.eventId); + } finally { + if (mounted) setState(() => _sending = false); + } + } + + List _comments() { + final comments = widget.timeline.events + .where( + (e) => + !e.redacted && + e.relationshipType == RelationshipTypes.thread && + e.relationshipEventId == event.eventId, + ) + .toList(); + comments.sort((a, b) => a.originServerTs.compareTo(b.originServerTs)); + return comments; + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final l10n = L10n.of(context); + final isVideo = event.messageType == MessageTypes.Video; + + return StreamBuilder( + stream: widget.room.client.onSync.stream + .where((syncUpdate) => syncUpdate.hasRoomUpdate) + .rateLimit(const Duration(seconds: 1)), + builder: (context, snapshots) { + final comments = _comments(); + final caption = event.getDisplayEvent(widget.timeline).body.trim(); + final liked = _myReaction() != null; + + return SafeArea( + top: false, + left: false, + right: false, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Container( + width: 36, + height: 5, + margin: const EdgeInsets.symmetric(vertical: 8), + decoration: BoxDecoration( + color: theme.colorScheme.outlineVariant, + borderRadius: BorderRadius.circular(3), + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + children: [ + Expanded( + child: Text( + widget.room.getLocalizedDisplayname(MatrixLocals(l10n)), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ), + IconButton( + icon: const Icon(Icons.close), + tooltip: l10n.close, + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), + ), + Expanded( + flex: 5, + child: Container( + color: theme.colorScheme.surfaceContainerHighest, + width: double.infinity, + child: isVideo + ? EventVideoPlayer(event) + : MxcImage( + key: ValueKey(event.eventId), + event: event, + fit: BoxFit.contain, + isThumbnail: false, + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + IconButton( + tooltip: l10n.editCaption, + icon: const Icon(Icons.edit_outlined), + onPressed: _editCaption, + ), + const Spacer(), + if (_likeCount > 0) + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Text( + '$_likeCount', + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + ), + ), + IconButton( + tooltip: liked ? l10n.unlike : l10n.like, + icon: Icon( + liked ? Icons.favorite : Icons.favorite_border, + color: liked ? Colors.redAccent : null, + ), + onPressed: _toggleLike, + ), + ], + ), + ), + if (caption.trim().isNotEmpty) + Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 8), + child: Text( + caption.trim(), + style: theme.textTheme.bodyMedium, + ), + ), + const Divider(height: 1), + Expanded( + flex: 3, + child: comments.isEmpty + ? Center( + child: Padding( + padding: const EdgeInsets.all(16), + child: Text( + l10n.noCommentsYet, + textAlign: TextAlign.center, + style: TextStyle( + color: theme.colorScheme.onSurfaceVariant, + ), + ), + ), + ) + : ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 4), + itemCount: comments.length, + itemBuilder: (context, i) { + final comment = comments[i]; + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 6, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Avatar( + mxContent: comment + .senderFromMemoryOrFallback + .avatarUrl, + name: comment.senderFromMemoryOrFallback + .calcDisplayname(), + size: 32, + client: widget.room.client, + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + comment.senderFromMemoryOrFallback + .calcDisplayname(), + style: theme.textTheme.labelMedium + ?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 2), + Text( + comment + .getDisplayEvent(widget.timeline) + .calcLocalizedBodyFallback( + MatrixLocals(l10n), + withSenderNamePrefix: false, + hideReply: true, + ), + style: theme.textTheme.bodyMedium, + ), + ], + ), + ), + ], + ), + ); + }, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(16, 4, 8, 8), + child: Row( + children: [ + Expanded( + child: TextField( + controller: _commentController, + textInputAction: TextInputAction.send, + onSubmitted: (_) => _sendComment(), + decoration: InputDecoration( + hintText: l10n.optionalMessage, + isDense: true, + border: InputBorder.none, + ), + ), + ), + IconButton( + tooltip: l10n.send, + icon: const Icon(Icons.send_outlined), + onPressed: _sendComment, + ), + ], + ), + ), + ], + ), + ); + }, + ); + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png index 5941666..89f33b4 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png index b7e0fe1..80e7fa2 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png index db791dc..e470dec 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png index f96daef..cdbf2b9 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png index 6809be0..362a05c 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png index d21f27a..1f2fed2 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png index e5b187b..1dac06f 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/web/favicon.png b/web/favicon.png index 0aab876..85b0aa4 100644 Binary files a/web/favicon.png and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png index 86aeff2..4eef982 100644 Binary files a/web/icons/Icon-192.png and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png index a4418f3..43c6f57 100644 Binary files a/web/icons/Icon-512.png and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png index 86aeff2..4eef982 100644 Binary files a/web/icons/Icon-maskable-192.png and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png index a4418f3..43c6f57 100644 Binary files a/web/icons/Icon-maskable-512.png and b/web/icons/Icon-maskable-512.png differ diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index 6995a11..fe5b322 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ