✨ Chat enter to send
This commit is contained in:
		@@ -271,5 +271,12 @@
 | 
				
			|||||||
  "unreadMessages": {
 | 
					  "unreadMessages": {
 | 
				
			||||||
    "one": "{} unread message",
 | 
					    "one": "{} unread message",
 | 
				
			||||||
    "other": "{} unread messages"
 | 
					    "other": "{} unread messages"
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
 | 
					  "settingsRealmCompactView": "Compact Realm View",
 | 
				
			||||||
 | 
					  "settingsMixedFeed": "Mixed Feed",
 | 
				
			||||||
 | 
					  "settingsAutoTranslate": "Auto Translate",
 | 
				
			||||||
 | 
					  "settingsHideBottomNav": "Hide Bottom Navigation",
 | 
				
			||||||
 | 
					  "settingsSoundEffects": "Sound Effects",
 | 
				
			||||||
 | 
					  "settingsAprilFoolFeatures": "April Fool Features",
 | 
				
			||||||
 | 
					  "settingsEnterToSend": "Enter to Send"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ part of 'chat_summary.dart';
 | 
				
			|||||||
// RiverpodGenerator
 | 
					// RiverpodGenerator
 | 
				
			||||||
// **************************************************************************
 | 
					// **************************************************************************
 | 
				
			||||||
 | 
					
 | 
				
			||||||
String _$chatSummaryHash() => r'fa48d381f489f90055fb728f7e0fda6f8ef49d15';
 | 
					String _$chatSummaryHash() => r'19aad48b5fabb33a76b742400d3b738ceb81c40c';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// See also [ChatSummary].
 | 
					/// See also [ChatSummary].
 | 
				
			||||||
@ProviderFor(ChatSummary)
 | 
					@ProviderFor(ChatSummary)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,10 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
 | 
					import 'package:flutter_riverpod/flutter_riverpod.dart';
 | 
				
			||||||
 | 
					import 'package:freezed_annotation/freezed_annotation.dart';
 | 
				
			||||||
import 'package:shared_preferences/shared_preferences.dart';
 | 
					import 'package:shared_preferences/shared_preferences.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					part 'config.freezed.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const kTokenPairStoreKey = 'dyn_user_tk';
 | 
					const kTokenPairStoreKey = 'dyn_user_tk';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const kNetworkServerDefault = 'https://nt.solian.app';
 | 
					const kNetworkServerDefault = 'https://nt.solian.app';
 | 
				
			||||||
@@ -21,6 +24,7 @@ const kAppHideBottomNav = 'app_hide_bottom_nav';
 | 
				
			|||||||
const kAppSoundEffects = 'app_sound_effects';
 | 
					const kAppSoundEffects = 'app_sound_effects';
 | 
				
			||||||
const kAppAprilFoolFeatures = 'app_april_fool_features';
 | 
					const kAppAprilFoolFeatures = 'app_april_fool_features';
 | 
				
			||||||
const kAppWindowSize = 'app_window_size';
 | 
					const kAppWindowSize = 'app_window_size';
 | 
				
			||||||
 | 
					const kAppEnterToSend = 'app_enter_to_send';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Map<String, FilterQuality> kImageQualityLevel = {
 | 
					const Map<String, FilterQuality> kImageQualityLevel = {
 | 
				
			||||||
  'settingsImageQualityLowest': FilterQuality.none,
 | 
					  'settingsImageQualityLowest': FilterQuality.none,
 | 
				
			||||||
@@ -46,40 +50,17 @@ final serverUrlProvider = Provider<String>((ref) {
 | 
				
			|||||||
  return prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
 | 
					  return prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AppSettings {
 | 
					@freezed
 | 
				
			||||||
  final bool realmCompactView;
 | 
					abstract class AppSettings with _$AppSettings {
 | 
				
			||||||
  final bool mixedFeed;
 | 
					  const factory AppSettings({
 | 
				
			||||||
  final bool autoTranslate;
 | 
					    required bool realmCompactView,
 | 
				
			||||||
  final bool hideBottomNav;
 | 
					    required bool mixedFeed,
 | 
				
			||||||
  final bool soundEffects;
 | 
					    required bool autoTranslate,
 | 
				
			||||||
  final bool aprilFoolFeatures;
 | 
					    required bool hideBottomNav,
 | 
				
			||||||
 | 
					    required bool soundEffects,
 | 
				
			||||||
  AppSettings({
 | 
					    required bool aprilFoolFeatures,
 | 
				
			||||||
    required this.realmCompactView,
 | 
					    required bool enterToSend,
 | 
				
			||||||
    required this.mixedFeed,
 | 
					  }) = _AppSettings;
 | 
				
			||||||
    required this.autoTranslate,
 | 
					 | 
				
			||||||
    required this.hideBottomNav,
 | 
					 | 
				
			||||||
    required this.soundEffects,
 | 
					 | 
				
			||||||
    required this.aprilFoolFeatures,
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  AppSettings copyWith({
 | 
					 | 
				
			||||||
    bool? realmCompactView,
 | 
					 | 
				
			||||||
    bool? mixedFeed,
 | 
					 | 
				
			||||||
    bool? autoTranslate,
 | 
					 | 
				
			||||||
    bool? hideBottomNav,
 | 
					 | 
				
			||||||
    bool? soundEffects,
 | 
					 | 
				
			||||||
    bool? aprilFoolFeatures,
 | 
					 | 
				
			||||||
  }) {
 | 
					 | 
				
			||||||
    return AppSettings(
 | 
					 | 
				
			||||||
      realmCompactView: realmCompactView ?? this.realmCompactView,
 | 
					 | 
				
			||||||
      mixedFeed: mixedFeed ?? this.mixedFeed,
 | 
					 | 
				
			||||||
      autoTranslate: autoTranslate ?? this.autoTranslate,
 | 
					 | 
				
			||||||
      hideBottomNav: hideBottomNav ?? this.hideBottomNav,
 | 
					 | 
				
			||||||
      soundEffects: soundEffects ?? this.soundEffects,
 | 
					 | 
				
			||||||
      aprilFoolFeatures: aprilFoolFeatures ?? this.aprilFoolFeatures,
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AppSettingsNotifier extends StateNotifier<AppSettings> {
 | 
					class AppSettingsNotifier extends StateNotifier<AppSettings> {
 | 
				
			||||||
@@ -94,6 +75,7 @@ class AppSettingsNotifier extends StateNotifier<AppSettings> {
 | 
				
			|||||||
          hideBottomNav: prefs.getBool(kAppHideBottomNav) ?? false,
 | 
					          hideBottomNav: prefs.getBool(kAppHideBottomNav) ?? false,
 | 
				
			||||||
          soundEffects: prefs.getBool(kAppSoundEffects) ?? true,
 | 
					          soundEffects: prefs.getBool(kAppSoundEffects) ?? true,
 | 
				
			||||||
          aprilFoolFeatures: prefs.getBool(kAppAprilFoolFeatures) ?? true,
 | 
					          aprilFoolFeatures: prefs.getBool(kAppAprilFoolFeatures) ?? true,
 | 
				
			||||||
 | 
					          enterToSend: prefs.getBool(kAppEnterToSend) ?? true,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -126,6 +108,11 @@ class AppSettingsNotifier extends StateNotifier<AppSettings> {
 | 
				
			|||||||
    prefs.setBool(kAppAprilFoolFeatures, value);
 | 
					    prefs.setBool(kAppAprilFoolFeatures, value);
 | 
				
			||||||
    state = state.copyWith(aprilFoolFeatures: value);
 | 
					    state = state.copyWith(aprilFoolFeatures: value);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void setEnterToSend(bool value) {
 | 
				
			||||||
 | 
					    prefs.setBool(kAppEnterToSend, value);
 | 
				
			||||||
 | 
					    state = state.copyWith(enterToSend: value);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
final appSettingsProvider =
 | 
					final appSettingsProvider =
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										160
									
								
								lib/pods/config.freezed.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								lib/pods/config.freezed.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,160 @@
 | 
				
			|||||||
 | 
					// dart format width=80
 | 
				
			||||||
 | 
					// coverage:ignore-file
 | 
				
			||||||
 | 
					// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
				
			||||||
 | 
					// ignore_for_file: type=lint
 | 
				
			||||||
 | 
					// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					part of 'config.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// **************************************************************************
 | 
				
			||||||
 | 
					// FreezedGenerator
 | 
				
			||||||
 | 
					// **************************************************************************
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// dart format off
 | 
				
			||||||
 | 
					T _$identity<T>(T value) => value;
 | 
				
			||||||
 | 
					/// @nodoc
 | 
				
			||||||
 | 
					mixin _$AppSettings {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 bool get realmCompactView; bool get mixedFeed; bool get autoTranslate; bool get hideBottomNav; bool get soundEffects; bool get aprilFoolFeatures; bool get enterToSend;
 | 
				
			||||||
 | 
					/// Create a copy of AppSettings
 | 
				
			||||||
 | 
					/// with the given fields replaced by the non-null parameter values.
 | 
				
			||||||
 | 
					@JsonKey(includeFromJson: false, includeToJson: false)
 | 
				
			||||||
 | 
					@pragma('vm:prefer-inline')
 | 
				
			||||||
 | 
					$AppSettingsCopyWith<AppSettings> get copyWith => _$AppSettingsCopyWithImpl<AppSettings>(this as AppSettings, _$identity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@override
 | 
				
			||||||
 | 
					bool operator ==(Object other) {
 | 
				
			||||||
 | 
					  return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettings&&(identical(other.realmCompactView, realmCompactView) || other.realmCompactView == realmCompactView)&&(identical(other.mixedFeed, mixedFeed) || other.mixedFeed == mixedFeed)&&(identical(other.autoTranslate, autoTranslate) || other.autoTranslate == autoTranslate)&&(identical(other.hideBottomNav, hideBottomNav) || other.hideBottomNav == hideBottomNav)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.aprilFoolFeatures, aprilFoolFeatures) || other.aprilFoolFeatures == aprilFoolFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@override
 | 
				
			||||||
 | 
					int get hashCode => Object.hash(runtimeType,realmCompactView,mixedFeed,autoTranslate,hideBottomNav,soundEffects,aprilFoolFeatures,enterToSend);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@override
 | 
				
			||||||
 | 
					String toString() {
 | 
				
			||||||
 | 
					  return 'AppSettings(realmCompactView: $realmCompactView, mixedFeed: $mixedFeed, autoTranslate: $autoTranslate, hideBottomNav: $hideBottomNav, soundEffects: $soundEffects, aprilFoolFeatures: $aprilFoolFeatures, enterToSend: $enterToSend)';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// @nodoc
 | 
				
			||||||
 | 
					abstract mixin class $AppSettingsCopyWith<$Res>  {
 | 
				
			||||||
 | 
					  factory $AppSettingsCopyWith(AppSettings value, $Res Function(AppSettings) _then) = _$AppSettingsCopyWithImpl;
 | 
				
			||||||
 | 
					@useResult
 | 
				
			||||||
 | 
					$Res call({
 | 
				
			||||||
 | 
					 bool realmCompactView, bool mixedFeed, bool autoTranslate, bool hideBottomNav, bool soundEffects, bool aprilFoolFeatures, bool enterToSend
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/// @nodoc
 | 
				
			||||||
 | 
					class _$AppSettingsCopyWithImpl<$Res>
 | 
				
			||||||
 | 
					    implements $AppSettingsCopyWith<$Res> {
 | 
				
			||||||
 | 
					  _$AppSettingsCopyWithImpl(this._self, this._then);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final AppSettings _self;
 | 
				
			||||||
 | 
					  final $Res Function(AppSettings) _then;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Create a copy of AppSettings
 | 
				
			||||||
 | 
					/// with the given fields replaced by the non-null parameter values.
 | 
				
			||||||
 | 
					@pragma('vm:prefer-inline') @override $Res call({Object? realmCompactView = null,Object? mixedFeed = null,Object? autoTranslate = null,Object? hideBottomNav = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,}) {
 | 
				
			||||||
 | 
					  return _then(_self.copyWith(
 | 
				
			||||||
 | 
					realmCompactView: null == realmCompactView ? _self.realmCompactView : realmCompactView // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,mixedFeed: null == mixedFeed ? _self.mixedFeed : mixedFeed // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,autoTranslate: null == autoTranslate ? _self.autoTranslate : autoTranslate // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,hideBottomNav: null == hideBottomNav ? _self.hideBottomNav : hideBottomNav // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,soundEffects: null == soundEffects ? _self.soundEffects : soundEffects // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,aprilFoolFeatures: null == aprilFoolFeatures ? _self.aprilFoolFeatures : aprilFoolFeatures // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,enterToSend: null == enterToSend ? _self.enterToSend : enterToSend // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,
 | 
				
			||||||
 | 
					  ));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// @nodoc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _AppSettings implements AppSettings {
 | 
				
			||||||
 | 
					  const _AppSettings({required this.realmCompactView, required this.mixedFeed, required this.autoTranslate, required this.hideBottomNav, required this.soundEffects, required this.aprilFoolFeatures, required this.enterToSend});
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@override final  bool realmCompactView;
 | 
				
			||||||
 | 
					@override final  bool mixedFeed;
 | 
				
			||||||
 | 
					@override final  bool autoTranslate;
 | 
				
			||||||
 | 
					@override final  bool hideBottomNav;
 | 
				
			||||||
 | 
					@override final  bool soundEffects;
 | 
				
			||||||
 | 
					@override final  bool aprilFoolFeatures;
 | 
				
			||||||
 | 
					@override final  bool enterToSend;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Create a copy of AppSettings
 | 
				
			||||||
 | 
					/// with the given fields replaced by the non-null parameter values.
 | 
				
			||||||
 | 
					@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
				
			||||||
 | 
					@pragma('vm:prefer-inline')
 | 
				
			||||||
 | 
					_$AppSettingsCopyWith<_AppSettings> get copyWith => __$AppSettingsCopyWithImpl<_AppSettings>(this, _$identity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@override
 | 
				
			||||||
 | 
					bool operator ==(Object other) {
 | 
				
			||||||
 | 
					  return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.realmCompactView, realmCompactView) || other.realmCompactView == realmCompactView)&&(identical(other.mixedFeed, mixedFeed) || other.mixedFeed == mixedFeed)&&(identical(other.autoTranslate, autoTranslate) || other.autoTranslate == autoTranslate)&&(identical(other.hideBottomNav, hideBottomNav) || other.hideBottomNav == hideBottomNav)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.aprilFoolFeatures, aprilFoolFeatures) || other.aprilFoolFeatures == aprilFoolFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@override
 | 
				
			||||||
 | 
					int get hashCode => Object.hash(runtimeType,realmCompactView,mixedFeed,autoTranslate,hideBottomNav,soundEffects,aprilFoolFeatures,enterToSend);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@override
 | 
				
			||||||
 | 
					String toString() {
 | 
				
			||||||
 | 
					  return 'AppSettings(realmCompactView: $realmCompactView, mixedFeed: $mixedFeed, autoTranslate: $autoTranslate, hideBottomNav: $hideBottomNav, soundEffects: $soundEffects, aprilFoolFeatures: $aprilFoolFeatures, enterToSend: $enterToSend)';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// @nodoc
 | 
				
			||||||
 | 
					abstract mixin class _$AppSettingsCopyWith<$Res> implements $AppSettingsCopyWith<$Res> {
 | 
				
			||||||
 | 
					  factory _$AppSettingsCopyWith(_AppSettings value, $Res Function(_AppSettings) _then) = __$AppSettingsCopyWithImpl;
 | 
				
			||||||
 | 
					@override @useResult
 | 
				
			||||||
 | 
					$Res call({
 | 
				
			||||||
 | 
					 bool realmCompactView, bool mixedFeed, bool autoTranslate, bool hideBottomNav, bool soundEffects, bool aprilFoolFeatures, bool enterToSend
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/// @nodoc
 | 
				
			||||||
 | 
					class __$AppSettingsCopyWithImpl<$Res>
 | 
				
			||||||
 | 
					    implements _$AppSettingsCopyWith<$Res> {
 | 
				
			||||||
 | 
					  __$AppSettingsCopyWithImpl(this._self, this._then);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final _AppSettings _self;
 | 
				
			||||||
 | 
					  final $Res Function(_AppSettings) _then;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Create a copy of AppSettings
 | 
				
			||||||
 | 
					/// with the given fields replaced by the non-null parameter values.
 | 
				
			||||||
 | 
					@override @pragma('vm:prefer-inline') $Res call({Object? realmCompactView = null,Object? mixedFeed = null,Object? autoTranslate = null,Object? hideBottomNav = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,}) {
 | 
				
			||||||
 | 
					  return _then(_AppSettings(
 | 
				
			||||||
 | 
					realmCompactView: null == realmCompactView ? _self.realmCompactView : realmCompactView // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,mixedFeed: null == mixedFeed ? _self.mixedFeed : mixedFeed // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,autoTranslate: null == autoTranslate ? _self.autoTranslate : autoTranslate // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,hideBottomNav: null == hideBottomNav ? _self.hideBottomNav : hideBottomNav // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,soundEffects: null == soundEffects ? _self.soundEffects : soundEffects // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,aprilFoolFeatures: null == aprilFoolFeatures ? _self.aprilFoolFeatures : aprilFoolFeatures // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,enterToSend: null == enterToSend ? _self.enterToSend : enterToSend // ignore: cast_nullable_to_non_nullable
 | 
				
			||||||
 | 
					as bool,
 | 
				
			||||||
 | 
					  ));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// dart format on
 | 
				
			||||||
@@ -93,10 +93,13 @@ class ChatRoomListTile extends HookConsumerWidget {
 | 
				
			|||||||
                      style: Theme.of(context).textTheme.bodySmall,
 | 
					                      style: Theme.of(context).textTheme.bodySmall,
 | 
				
			||||||
                    ),
 | 
					                    ),
 | 
				
			||||||
                  ),
 | 
					                  ),
 | 
				
			||||||
                  Text(
 | 
					                  Align(
 | 
				
			||||||
 | 
					                    alignment: Alignment.centerRight,
 | 
				
			||||||
 | 
					                    child: Text(
 | 
				
			||||||
                      RelativeTime(context).format(data.lastMessage.createdAt),
 | 
					                      RelativeTime(context).format(data.lastMessage.createdAt),
 | 
				
			||||||
                      style: Theme.of(context).textTheme.bodySmall,
 | 
					                      style: Theme.of(context).textTheme.bodySmall,
 | 
				
			||||||
                    ),
 | 
					                    ),
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
@@ -117,24 +120,14 @@ class ChatRoomListTile extends HookConsumerWidget {
 | 
				
			|||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Widget buildTrailing() {
 | 
					 | 
				
			||||||
      if (trailing != null) return trailing!;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return summary.when(
 | 
					 | 
				
			||||||
        data: (data) {
 | 
					 | 
				
			||||||
          if (data == null || data.unreadCount == 0) {
 | 
					 | 
				
			||||||
            return const SizedBox.shrink();
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          return Badge(label: Text(data.unreadCount.toString()));
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        loading: () => const SizedBox.shrink(),
 | 
					 | 
				
			||||||
        error: (_, __) => const SizedBox.shrink(),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return ListTile(
 | 
					    return ListTile(
 | 
				
			||||||
      leading:
 | 
					      leading: Badge(
 | 
				
			||||||
 | 
					        isLabelVisible: summary.when(
 | 
				
			||||||
 | 
					          data: (data) => (data?.unreadCount ?? 0) > 0,
 | 
				
			||||||
 | 
					          loading: () => false,
 | 
				
			||||||
 | 
					          error: (_, __) => false,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        child:
 | 
				
			||||||
            (isDirect && room.pictureId == null)
 | 
					            (isDirect && room.pictureId == null)
 | 
				
			||||||
                ? SplitAvatarWidget(
 | 
					                ? SplitAvatarWidget(
 | 
				
			||||||
                  filesId:
 | 
					                  filesId:
 | 
				
			||||||
@@ -145,13 +138,13 @@ class ChatRoomListTile extends HookConsumerWidget {
 | 
				
			|||||||
                : room.pictureId == null
 | 
					                : room.pictureId == null
 | 
				
			||||||
                ? CircleAvatar(child: Text(room.name![0].toUpperCase()))
 | 
					                ? CircleAvatar(child: Text(room.name![0].toUpperCase()))
 | 
				
			||||||
                : ProfilePictureWidget(fileId: room.pictureId),
 | 
					                : ProfilePictureWidget(fileId: room.pictureId),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
      title: Text(
 | 
					      title: Text(
 | 
				
			||||||
        (isDirect && room.name == null)
 | 
					        (isDirect && room.name == null)
 | 
				
			||||||
            ? room.members!.map((e) => e.account.nick).join(', ')
 | 
					            ? room.members!.map((e) => e.account.nick).join(', ')
 | 
				
			||||||
            : room.name ?? '',
 | 
					            : room.name ?? '',
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
      subtitle: buildSubtitle(),
 | 
					      subtitle: buildSubtitle(),
 | 
				
			||||||
      trailing: buildTrailing(),
 | 
					 | 
				
			||||||
      onTap: () async {
 | 
					      onTap: () async {
 | 
				
			||||||
        // Clear unread count if there are unread messages
 | 
					        // Clear unread count if there are unread messages
 | 
				
			||||||
        final summary = await ref.read(chatSummaryProvider.future);
 | 
					        final summary = await ref.read(chatSummaryProvider.future);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ import 'dart:convert';
 | 
				
			|||||||
import 'package:auto_route/auto_route.dart';
 | 
					import 'package:auto_route/auto_route.dart';
 | 
				
			||||||
import 'package:easy_localization/easy_localization.dart';
 | 
					import 'package:easy_localization/easy_localization.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:flutter/services.dart';
 | 
				
			||||||
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
					import 'package:flutter_hooks/flutter_hooks.dart';
 | 
				
			||||||
import 'package:gap/gap.dart';
 | 
					import 'package:gap/gap.dart';
 | 
				
			||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
					import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
				
			||||||
@@ -676,7 +677,7 @@ class ChatRoomScreen extends HookConsumerWidget {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _ChatInput extends StatelessWidget {
 | 
					class _ChatInput extends ConsumerWidget {
 | 
				
			||||||
  final TextEditingController messageController;
 | 
					  final TextEditingController messageController;
 | 
				
			||||||
  final SnChatRoom chatRoom;
 | 
					  final SnChatRoom chatRoom;
 | 
				
			||||||
  final VoidCallback onSend;
 | 
					  final VoidCallback onSend;
 | 
				
			||||||
@@ -705,8 +706,26 @@ class _ChatInput extends StatelessWidget {
 | 
				
			|||||||
    required this.onMoveAttachment,
 | 
					    required this.onMoveAttachment,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _handleKeyPress(BuildContext context, WidgetRef ref, RawKeyEvent event) {
 | 
				
			||||||
 | 
					    if (event is! RawKeyDownEvent) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final enterToSend = ref.read(appSettingsProvider).enterToSend;
 | 
				
			||||||
 | 
					    final isEnter = event.logicalKey == LogicalKeyboardKey.enter;
 | 
				
			||||||
 | 
					    final isModifierPressed = event.isMetaPressed || event.isControlPressed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isEnter) {
 | 
				
			||||||
 | 
					      if (enterToSend && !isModifierPressed) {
 | 
				
			||||||
 | 
					        onSend();
 | 
				
			||||||
 | 
					      } else if (!enterToSend && isModifierPressed) {
 | 
				
			||||||
 | 
					        onSend();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context, WidgetRef ref) {
 | 
				
			||||||
 | 
					    final enterToSend = ref.watch(appSettingsProvider).enterToSend;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Material(
 | 
					    return Material(
 | 
				
			||||||
      elevation: 8,
 | 
					      elevation: 8,
 | 
				
			||||||
      color: Theme.of(context).colorScheme.surface,
 | 
					      color: Theme.of(context).colorScheme.surface,
 | 
				
			||||||
@@ -806,8 +825,20 @@ class _ChatInput extends StatelessWidget {
 | 
				
			|||||||
                      ],
 | 
					                      ],
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
                Expanded(
 | 
					                Expanded(
 | 
				
			||||||
 | 
					                  child: RawKeyboardListener(
 | 
				
			||||||
 | 
					                    focusNode: FocusNode(),
 | 
				
			||||||
 | 
					                    onKey: (event) => _handleKeyPress(context, ref, event),
 | 
				
			||||||
                    child: TextField(
 | 
					                    child: TextField(
 | 
				
			||||||
                      controller: messageController,
 | 
					                      controller: messageController,
 | 
				
			||||||
 | 
					                      inputFormatters: [
 | 
				
			||||||
 | 
					                        if (enterToSend)
 | 
				
			||||||
 | 
					                          TextInputFormatter.withFunction((oldValue, newValue) {
 | 
				
			||||||
 | 
					                            if (newValue.text.endsWith('\n')) {
 | 
				
			||||||
 | 
					                              return oldValue;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            return newValue;
 | 
				
			||||||
 | 
					                          }),
 | 
				
			||||||
 | 
					                      ],
 | 
				
			||||||
                      decoration: InputDecoration(
 | 
					                      decoration: InputDecoration(
 | 
				
			||||||
                        hintText:
 | 
					                        hintText:
 | 
				
			||||||
                            (chatRoom.type == 1 && chatRoom.name == null)
 | 
					                            (chatRoom.type == 1 && chatRoom.name == null)
 | 
				
			||||||
@@ -829,7 +860,7 @@ class _ChatInput extends StatelessWidget {
 | 
				
			|||||||
                      maxLines: null,
 | 
					                      maxLines: null,
 | 
				
			||||||
                      onTapOutside:
 | 
					                      onTapOutside:
 | 
				
			||||||
                          (_) => FocusManager.instance.primaryFocus?.unfocus(),
 | 
					                          (_) => FocusManager.instance.primaryFocus?.unfocus(),
 | 
				
			||||||
                    onSubmitted: (_) => onSend(),
 | 
					                    ),
 | 
				
			||||||
                  ),
 | 
					                  ),
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
                IconButton(
 | 
					                IconButton(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,7 @@ class SettingsScreen extends HookConsumerWidget {
 | 
				
			|||||||
    final serverUrl = ref.watch(serverUrlProvider);
 | 
					    final serverUrl = ref.watch(serverUrlProvider);
 | 
				
			||||||
    final prefs = ref.watch(sharedPreferencesProvider);
 | 
					    final prefs = ref.watch(sharedPreferencesProvider);
 | 
				
			||||||
    final controller = TextEditingController(text: serverUrl);
 | 
					    final controller = TextEditingController(text: serverUrl);
 | 
				
			||||||
 | 
					    final settings = ref.watch(appSettingsProvider);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final docBasepath = useState<String?>(null);
 | 
					    final docBasepath = useState<String?>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -174,6 +175,99 @@ class SettingsScreen extends HookConsumerWidget {
 | 
				
			|||||||
                  );
 | 
					                  );
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
 | 
					            const Divider(),
 | 
				
			||||||
 | 
					            ListTile(
 | 
				
			||||||
 | 
					              minLeadingWidth: 48,
 | 
				
			||||||
 | 
					              title: Text('settingsRealmCompactView').tr(),
 | 
				
			||||||
 | 
					              contentPadding: const EdgeInsets.only(left: 24, right: 17),
 | 
				
			||||||
 | 
					              leading: const Icon(Symbols.view_compact),
 | 
				
			||||||
 | 
					              trailing: Switch(
 | 
				
			||||||
 | 
					                value: settings.realmCompactView,
 | 
				
			||||||
 | 
					                onChanged: (value) {
 | 
				
			||||||
 | 
					                  ref
 | 
				
			||||||
 | 
					                      .read(appSettingsProvider.notifier)
 | 
				
			||||||
 | 
					                      .setRealmCompactView(value);
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            ListTile(
 | 
				
			||||||
 | 
					              minLeadingWidth: 48,
 | 
				
			||||||
 | 
					              title: Text('settingsMixedFeed').tr(),
 | 
				
			||||||
 | 
					              contentPadding: const EdgeInsets.only(left: 24, right: 17),
 | 
				
			||||||
 | 
					              leading: const Icon(Symbols.merge),
 | 
				
			||||||
 | 
					              trailing: Switch(
 | 
				
			||||||
 | 
					                value: settings.mixedFeed,
 | 
				
			||||||
 | 
					                onChanged: (value) {
 | 
				
			||||||
 | 
					                  ref.read(appSettingsProvider.notifier).setMixedFeed(value);
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            ListTile(
 | 
				
			||||||
 | 
					              minLeadingWidth: 48,
 | 
				
			||||||
 | 
					              title: Text('settingsAutoTranslate').tr(),
 | 
				
			||||||
 | 
					              contentPadding: const EdgeInsets.only(left: 24, right: 17),
 | 
				
			||||||
 | 
					              leading: const Icon(Symbols.translate),
 | 
				
			||||||
 | 
					              trailing: Switch(
 | 
				
			||||||
 | 
					                value: settings.autoTranslate,
 | 
				
			||||||
 | 
					                onChanged: (value) {
 | 
				
			||||||
 | 
					                  ref
 | 
				
			||||||
 | 
					                      .read(appSettingsProvider.notifier)
 | 
				
			||||||
 | 
					                      .setAutoTranslate(value);
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            ListTile(
 | 
				
			||||||
 | 
					              minLeadingWidth: 48,
 | 
				
			||||||
 | 
					              title: Text('settingsHideBottomNav').tr(),
 | 
				
			||||||
 | 
					              contentPadding: const EdgeInsets.only(left: 24, right: 17),
 | 
				
			||||||
 | 
					              leading: const Icon(Symbols.navigation),
 | 
				
			||||||
 | 
					              trailing: Switch(
 | 
				
			||||||
 | 
					                value: settings.hideBottomNav,
 | 
				
			||||||
 | 
					                onChanged: (value) {
 | 
				
			||||||
 | 
					                  ref
 | 
				
			||||||
 | 
					                      .read(appSettingsProvider.notifier)
 | 
				
			||||||
 | 
					                      .setHideBottomNav(value);
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            ListTile(
 | 
				
			||||||
 | 
					              minLeadingWidth: 48,
 | 
				
			||||||
 | 
					              title: Text('settingsSoundEffects').tr(),
 | 
				
			||||||
 | 
					              contentPadding: const EdgeInsets.only(left: 24, right: 17),
 | 
				
			||||||
 | 
					              leading: const Icon(Symbols.volume_up),
 | 
				
			||||||
 | 
					              trailing: Switch(
 | 
				
			||||||
 | 
					                value: settings.soundEffects,
 | 
				
			||||||
 | 
					                onChanged: (value) {
 | 
				
			||||||
 | 
					                  ref.read(appSettingsProvider.notifier).setSoundEffects(value);
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            ListTile(
 | 
				
			||||||
 | 
					              minLeadingWidth: 48,
 | 
				
			||||||
 | 
					              title: Text('settingsAprilFoolFeatures').tr(),
 | 
				
			||||||
 | 
					              contentPadding: const EdgeInsets.only(left: 24, right: 17),
 | 
				
			||||||
 | 
					              leading: const Icon(Symbols.celebration),
 | 
				
			||||||
 | 
					              trailing: Switch(
 | 
				
			||||||
 | 
					                value: settings.aprilFoolFeatures,
 | 
				
			||||||
 | 
					                onChanged: (value) {
 | 
				
			||||||
 | 
					                  ref
 | 
				
			||||||
 | 
					                      .read(appSettingsProvider.notifier)
 | 
				
			||||||
 | 
					                      .setAprilFoolFeatures(value);
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            ListTile(
 | 
				
			||||||
 | 
					              minLeadingWidth: 48,
 | 
				
			||||||
 | 
					              title: Text('settingsEnterToSend').tr(),
 | 
				
			||||||
 | 
					              contentPadding: const EdgeInsets.only(left: 24, right: 17),
 | 
				
			||||||
 | 
					              leading: const Icon(Symbols.send),
 | 
				
			||||||
 | 
					              trailing: Switch(
 | 
				
			||||||
 | 
					                value: settings.enterToSend,
 | 
				
			||||||
 | 
					                onChanged: (value) {
 | 
				
			||||||
 | 
					                  ref.read(appSettingsProvider.notifier).setEnterToSend(value);
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user