feat: disable cloud translations in Encrypted Rooms

feat: follow with fluffychat's image viewer
feat: navigation rail on mobile
feat: bring back bubble gradient as an option
feat: darker amoled colors
This commit is contained in:
OfficialDakari 2025-06-20 19:06:03 +05:00
parent 6316dda939
commit 01f13723f5
17 changed files with 93 additions and 39 deletions

View File

@ -16,6 +16,9 @@
"ignoreUser": "Ignore user",
"normalUser": "Normal user",
"pinCode": "PIN code",
"displayNavigationRail": "Display navigation rail on mobile",
"enableGradient": "Enable bubble background gradient",
"translationDisabledInE2e": "Cloud translation is disabled in encrypted rooms to preserve privacy. Select specific words and use system context menu to translate with apps that support it.",
"remove": "Remove",
"@remove": {
"type": "String",

View File

@ -23,6 +23,9 @@
"importNow": "Импортировать сейчас",
"@importNow": {},
"importEmojis": "Импортировать эмодзи",
"displayNavigationRail": "Всегда показывать боковую панель",
"enableGradient": "Фоновый градиент для сообщений",
"translationDisabledInE2e": "Облачные переводы недоступны в зашифрованных комнатах для защиты конфиденциальности. Выбирайте отдельные слова и переводите их через другие приложения.",
"@importEmojis": {},
"importFromZipFile": "Импортировать из ZIP-файла",
"@importFromZipFile": {},

View File

@ -12,6 +12,7 @@ abstract class AppConfig {
static String _defaultHomeserver = 'extera.xyz';
static bool displayNavigationRail = true;
static bool enableGradient = true;
static String get defaultHomeserver => _defaultHomeserver;
static double fontSizeFactor = 1;
@ -113,5 +114,8 @@ abstract class AppConfig {
if (json['hide_unknown_events'] is bool) {
hideUnknownEvents = json['hide_unknown_events'];
}
if (json['enable_gradient'] is bool) {
enableGradient = json['enable_gradient'];
}
}
}

View File

@ -35,6 +35,7 @@ abstract class SettingKeys {
'chat.fluffy.swipeRightToLeftToReply';
static const String experimentalVoip = 'chat.fluffy.experimental_voip';
static const String showPresences = 'chat.fluffy.show_presences';
static const String enableGradient = 'xyz.extera.next.enableGradient';
}
enum AppSettings<T> {
@ -45,6 +46,7 @@ enum AppSettings<T> {
audioRecordingBitRate<int>('audioRecordingBitRate', 64000),
audioRecordingSamplingRate<int>('audioRecordingSamplingRate', 44100),
enableSoftLogout<bool>('enableSoftLogout', false),
enableGradient<bool>('enableGradient', true),
pushNotificationsGatewayUrl<String>(
'pushNotificationsGatewayUrl',
'https://push.fluffychat.im/_matrix/push/v1/notify',

View File

@ -46,11 +46,11 @@ abstract class FluffyThemes {
? {
'surface': const Color.fromARGB(255, 0, 0, 0),
'surfaceBright': const Color.fromARGB(255, 0, 0, 0),
'surfaceContainer': const Color.fromARGB(255, 22, 22, 22),
'surfaceContainerHigh': const Color.fromARGB(255, 33, 33, 33),
'surfaceContainerHighest': const Color.fromARGB(255, 33, 33, 33),
'surfaceContainerLow': const Color.fromARGB(255, 22, 22, 22),
'surfaceContainerLowest': const Color.fromARGB(255, 22, 22, 22),
'surfaceContainer': const Color.fromARGB(255, 11, 11, 11),
'surfaceContainerHigh': const Color.fromARGB(255, 22, 22, 22),
'surfaceContainerHighest': const Color.fromARGB(255, 22, 22, 22),
'surfaceContainerLow': const Color.fromARGB(255, 11, 11, 11),
'surfaceContainerLowest': const Color.fromARGB(255, 8, 8, 8),
'surfaceDim': const Color.fromARGB(255, 0, 0, 0),
'surfaceTint': const Color.fromARGB(255, 11, 11, 11),
'surfaceVariant': const Color.fromARGB(255, 0, 0, 0),

View File

@ -761,6 +761,12 @@ class ChatController extends State<ChatPageWithRoom>
}
void translateEventAction() async {
if (room.encrypted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).translationDisabledInE2e)),
);
return;
}
final event = selectedEvents.single;
var text = event.isRichMessage ? event.formattedText : event.text;
if (text == null) {

View File

@ -1,3 +1,4 @@
import 'package:fluffychat/config/app_config.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@ -150,6 +151,7 @@ class ChatEventList extends StatelessWidget {
wallpaperMode: hasWallpaper,
scrollController: controller.scrollController,
colors: colors,
gradient: AppConfig.enableGradient,
),
);
},

View File

@ -41,6 +41,7 @@ class Message extends StatelessWidget {
final bool wallpaperMode;
final ScrollController? scrollController;
final List<Color> colors;
final bool gradient;
const Message(
this.event, {
@ -48,6 +49,7 @@ class Message extends StatelessWidget {
this.previousEvent,
this.displayReadMarker = false,
this.longPressSelect = false,
this.gradient = false,
required this.onSelect,
required this.onInfoTab,
required this.scrollToEventId,
@ -328,7 +330,7 @@ class Message extends StatelessWidget {
clipBehavior: Clip.antiAlias,
child: BubbleBackground(
colors: colors,
ignore: noBubble || !ownMessage,
ignore: noBubble || !ownMessage || !gradient,
scrollController: scrollController,
child: Container(
decoration: BoxDecoration(
@ -638,7 +640,7 @@ class BubblePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
if (!(context.widget is ScrollView || context.widget is SingleChildScrollView)) return;
// if (!(context.widget is ScrollView || context.widget is SingleChildScrollView)) return;
final scrollable = _scrollable ??= Scrollable.of(context);
final scrollableBox = scrollable.context.findRenderObject() as RenderBox;
final scrollableRect = Offset.zero & scrollableBox.size;

View File

@ -30,7 +30,15 @@ class PollWidgetState extends State<PollWidget> {
padding: EdgeInsetsGeometry.all(16),
child: Column(
children: [
Text(content?['question']['m.text'] as String, style: TextStyle(fontWeight: FontWeight.bold))
Text(content?['question']['m.text'] as String, style: TextStyle(fontWeight: FontWeight.bold)),
Padding(
padding: EdgeInsets.fromLTRB(4, 4, 4, 2),
child: Column(
children: [
],
),
)
],
),
);

View File

@ -49,6 +49,7 @@ class TranslatedEventDialogState extends State<TranslatedEventDialog> {
longPressSelect: false,
selected: false,
wallpaperMode: false,
gradient: false
);
return Scaffold(

View File

@ -1,3 +1,4 @@
import 'package:fluffychat/config/app_config.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/generated/l10n/l10n.dart';
@ -30,8 +31,7 @@ class ChatListView extends StatelessWidget {
},
child: Row(
children: [
if (FluffyThemes.isColumnMode(context) &&
controller.widget.displayNavigationRail) ...[
if (FluffyThemes.isColumnMode(context) || AppConfig.displayNavigationRail) ...[
SpacesNavigationRail(
activeSpaceId: controller.activeSpaceId,
onGoToChats: controller.clearActiveSpace,

View File

@ -56,10 +56,10 @@ class ImageViewerController extends State<ImageViewer> {
void onKeyEvent(KeyEvent event) {
switch (event.logicalKey) {
case LogicalKeyboardKey.arrowLeft:
case LogicalKeyboardKey.arrowUp:
if (canGoBack) prevImage();
break;
case LogicalKeyboardKey.arrowRight:
case LogicalKeyboardKey.arrowDown:
if (canGoNext) nextImage();
break;
}
@ -119,4 +119,4 @@ class ImageViewerController extends State<ImageViewer> {
@override
Widget build(BuildContext context) => ImageViewerView(this);
}
}

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/generated/l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/generated/l10n/l10n.dart';
import 'package:fluffychat/pages/image_viewer/video_player.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/hover_builder.dart';
@ -75,6 +75,7 @@ class ImageViewerView extends StatelessWidget {
focusNode: controller.focusNode,
onKeyEvent: controller.onKeyEvent,
child: PageView.builder(
scrollDirection: Axis.vertical,
controller: controller.pageController,
itemCount: controller.allEvents.length,
itemBuilder: (context, i) {
@ -119,30 +120,33 @@ class ImageViewerView extends StatelessWidget {
},
),
),
if (hovered && controller.canGoBack)
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: IconButton(
style: iconButtonStyle,
tooltip: L10n.of(context).previous,
icon: const Icon(Icons.chevron_left_outlined),
onPressed: controller.prevImage,
),
),
),
if (hovered && controller.canGoNext)
if (hovered)
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: IconButton(
style: iconButtonStyle,
tooltip: L10n.of(context).next,
icon: const Icon(Icons.chevron_right_outlined),
onPressed: controller.nextImage,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (controller.canGoBack)
Padding(
padding: const EdgeInsets.all(12.0),
child: IconButton(
style: iconButtonStyle,
tooltip: L10n.of(context).previous,
icon: const Icon(Icons.arrow_upward_outlined),
onPressed: controller.prevImage,
),
),
if (controller.canGoNext)
Padding(
padding: const EdgeInsets.all(12.0),
child: IconButton(
style: iconButtonStyle,
tooltip: L10n.of(context).next,
icon: const Icon(Icons.arrow_downward_outlined),
onPressed: controller.nextImage,
),
),
],
),
),
],

View File

@ -233,7 +233,7 @@ class SettingsStyleView extends StatelessWidget {
vertical: 8,
),
child: Text(
'Рассказать шутку?',
'Я такой угарный анекдот про зайца вспомнил, го расскажу прямо на странице настроек?',
style: TextStyle(
color: theme.onBubbleColor,
fontSize: AppConfig.messageFontSize *
@ -314,6 +314,12 @@ class SettingsStyleView extends StatelessWidget {
],
),
),
SettingsSwitchListTile.adaptive(
title: L10n.of(context).enableGradient,
onChanged: (b) => AppConfig.enableGradient = b,
storeKey: SettingKeys.enableGradient,
defaultValue: AppConfig.enableGradient,
),
Divider(
color: theme.dividerColor,
),
@ -398,6 +404,12 @@ class SettingsStyleView extends StatelessWidget {
storeKey: SettingKeys.separateChatTypes,
defaultValue: AppConfig.separateChatTypes,
),
SettingsSwitchListTile.adaptive(
title: L10n.of(context).displayNavigationRail,
onChanged: (b) => AppConfig.displayNavigationRail = b,
storeKey: SettingKeys.displayNavigationRail,
defaultValue: AppConfig.displayNavigationRail,
),
],
),
),

View File

@ -442,6 +442,10 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
AppConfig.displayNavigationRail =
store.getBool(SettingKeys.displayNavigationRail) ??
AppConfig.displayNavigationRail;
AppConfig.enableGradient =
store.getBool(SettingKeys.enableGradient) ??
AppConfig.enableGradient;
}
@override

View File

@ -137,7 +137,7 @@ void showMemberActionsPopupMenu({
],
),
),
if (user.canBan)
if (user.canBan && user.membership != Membership.ban)
PopupMenuItem(
value: _MemberActions.ban,
child: Row(

View File

@ -32,7 +32,10 @@ class SpacesNavigationRail extends StatelessWidget {
.value
.uri
.path
.startsWith('/rooms/settings');
.startsWith('/rooms/settings')
&& FluffyThemes.isColumnMode(context);
// workaround on settings button remaining selected.
// who will even see it selected on mobile?
return StreamBuilder(
key: ValueKey(
client.userID.toString(),