Compare commits
2 Commits
38f8103265
...
b0f3b6b5c3
Author | SHA1 | Date | |
---|---|---|---|
b0f3b6b5c3
|
|||
cb2af379fa
|
@@ -473,6 +473,7 @@
|
|||||||
"settingsKeyboardShortcutSettings": "Settings",
|
"settingsKeyboardShortcutSettings": "Settings",
|
||||||
"settingsKeyboardShortcutNewMessage": "New Message",
|
"settingsKeyboardShortcutNewMessage": "New Message",
|
||||||
"settingsKeyboardShortcutCloseDialog": "Close Dialog",
|
"settingsKeyboardShortcutCloseDialog": "Close Dialog",
|
||||||
|
"settingsMessageDisplayStyle": "Message Display Style",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"drafts": "Drafts",
|
"drafts": "Drafts",
|
||||||
"noDrafts": "No drafts yet",
|
"noDrafts": "No drafts yet",
|
||||||
@@ -1021,5 +1022,6 @@
|
|||||||
"currentEmbed": "Current Embed",
|
"currentEmbed": "Current Embed",
|
||||||
"noEmbed": "No embed yet",
|
"noEmbed": "No embed yet",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"webView": "Web View"
|
"webView": "Web View",
|
||||||
|
"messageActions": "Message Actions"
|
||||||
}
|
}
|
||||||
|
@@ -26,6 +26,7 @@ const kAppAprilFoolFeatures = 'app_april_fool_features';
|
|||||||
const kAppWindowSize = 'app_window_size';
|
const kAppWindowSize = 'app_window_size';
|
||||||
const kAppEnterToSend = 'app_enter_to_send';
|
const kAppEnterToSend = 'app_enter_to_send';
|
||||||
const kAppDefaultPoolId = 'app_default_pool_id';
|
const kAppDefaultPoolId = 'app_default_pool_id';
|
||||||
|
const kAppMessageDisplayStyle = 'app_message_display_style';
|
||||||
const kFeaturedPostsCollapsedId =
|
const kFeaturedPostsCollapsedId =
|
||||||
'featured_posts_collapsed_id'; // Key for storing the ID of the collapsed featured post
|
'featured_posts_collapsed_id'; // Key for storing the ID of the collapsed featured post
|
||||||
|
|
||||||
@@ -67,6 +68,7 @@ sealed class AppSettings with _$AppSettings {
|
|||||||
required int? appColorScheme, // The color stored via the int type
|
required int? appColorScheme, // The color stored via the int type
|
||||||
required Size? windowSize, // The window size for desktop platforms
|
required Size? windowSize, // The window size for desktop platforms
|
||||||
required String? defaultPoolId,
|
required String? defaultPoolId,
|
||||||
|
required String messageDisplayStyle,
|
||||||
}) = _AppSettings;
|
}) = _AppSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +89,7 @@ class AppSettingsNotifier extends _$AppSettingsNotifier {
|
|||||||
appColorScheme: prefs.getInt(kAppColorSchemeStoreKey),
|
appColorScheme: prefs.getInt(kAppColorSchemeStoreKey),
|
||||||
windowSize: _getWindowSizeFromPrefs(prefs),
|
windowSize: _getWindowSizeFromPrefs(prefs),
|
||||||
defaultPoolId: prefs.getString(kAppDefaultPoolId),
|
defaultPoolId: prefs.getString(kAppDefaultPoolId),
|
||||||
|
messageDisplayStyle: prefs.getString(kAppMessageDisplayStyle) ?? 'bubble',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +109,7 @@ class AppSettingsNotifier extends _$AppSettingsNotifier {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDefaultPoolId(String? value) {
|
void setDefaultPoolId(String? value) {
|
||||||
final prefs = ref.read(sharedPreferencesProvider);
|
final prefs = ref.read(sharedPreferencesProvider);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
@@ -122,7 +126,7 @@ class AppSettingsNotifier extends _$AppSettingsNotifier {
|
|||||||
state = state.copyWith(autoTranslate: value);
|
state = state.copyWith(autoTranslate: value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDataSavingMode(bool value){
|
void setDataSavingMode(bool value) {
|
||||||
final prefs = ref.read(sharedPreferencesProvider);
|
final prefs = ref.read(sharedPreferencesProvider);
|
||||||
prefs.setBool(kAppDataSavingMode, value);
|
prefs.setBool(kAppDataSavingMode, value);
|
||||||
state = state.copyWith(dataSavingMode: value);
|
state = state.copyWith(dataSavingMode: value);
|
||||||
@@ -186,6 +190,12 @@ class AppSettingsNotifier extends _$AppSettingsNotifier {
|
|||||||
Size? getWindowSize() {
|
Size? getWindowSize() {
|
||||||
return state.windowSize;
|
return state.windowSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setMessageDisplayStyle(String value) {
|
||||||
|
final prefs = ref.read(sharedPreferencesProvider);
|
||||||
|
prefs.setString(kAppMessageDisplayStyle, value);
|
||||||
|
state = state.copyWith(messageDisplayStyle: value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final updateInfoProvider =
|
final updateInfoProvider =
|
||||||
|
@@ -16,7 +16,7 @@ mixin _$AppSettings {
|
|||||||
|
|
||||||
bool get autoTranslate; bool get dataSavingMode; bool get soundEffects; bool get aprilFoolFeatures; bool get enterToSend; bool get appBarTransparent; bool get showBackgroundImage; String? get customFonts; int? get appColorScheme;// The color stored via the int type
|
bool get autoTranslate; bool get dataSavingMode; bool get soundEffects; bool get aprilFoolFeatures; bool get enterToSend; bool get appBarTransparent; bool get showBackgroundImage; String? get customFonts; int? get appColorScheme;// The color stored via the int type
|
||||||
Size? get windowSize;// The window size for desktop platforms
|
Size? get windowSize;// The window size for desktop platforms
|
||||||
String? get defaultPoolId;
|
String? get defaultPoolId; String get messageDisplayStyle;
|
||||||
/// Create a copy of AppSettings
|
/// Create a copy of AppSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -27,16 +27,16 @@ $AppSettingsCopyWith<AppSettings> get copyWith => _$AppSettingsCopyWithImpl<AppS
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettings&&(identical(other.autoTranslate, autoTranslate) || other.autoTranslate == autoTranslate)&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.aprilFoolFeatures, aprilFoolFeatures) || other.aprilFoolFeatures == aprilFoolFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettings&&(identical(other.autoTranslate, autoTranslate) || other.autoTranslate == autoTranslate)&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.aprilFoolFeatures, aprilFoolFeatures) || other.aprilFoolFeatures == aprilFoolFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,autoTranslate,dataSavingMode,soundEffects,aprilFoolFeatures,enterToSend,appBarTransparent,showBackgroundImage,customFonts,appColorScheme,windowSize,defaultPoolId);
|
int get hashCode => Object.hash(runtimeType,autoTranslate,dataSavingMode,soundEffects,aprilFoolFeatures,enterToSend,appBarTransparent,showBackgroundImage,customFonts,appColorScheme,windowSize,defaultPoolId,messageDisplayStyle);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AppSettings(autoTranslate: $autoTranslate, dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, aprilFoolFeatures: $aprilFoolFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, customFonts: $customFonts, appColorScheme: $appColorScheme, windowSize: $windowSize, defaultPoolId: $defaultPoolId)';
|
return 'AppSettings(autoTranslate: $autoTranslate, dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, aprilFoolFeatures: $aprilFoolFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, customFonts: $customFonts, appColorScheme: $appColorScheme, windowSize: $windowSize, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ abstract mixin class $AppSettingsCopyWith<$Res> {
|
|||||||
factory $AppSettingsCopyWith(AppSettings value, $Res Function(AppSettings) _then) = _$AppSettingsCopyWithImpl;
|
factory $AppSettingsCopyWith(AppSettings value, $Res Function(AppSettings) _then) = _$AppSettingsCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, String? defaultPoolId
|
bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, String? defaultPoolId, String messageDisplayStyle
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ class _$AppSettingsCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of AppSettings
|
/// Create a copy of AppSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? autoTranslate = null,Object? dataSavingMode = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? windowSize = freezed,Object? defaultPoolId = freezed,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? autoTranslate = null,Object? dataSavingMode = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? windowSize = freezed,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
autoTranslate: null == autoTranslate ? _self.autoTranslate : autoTranslate // ignore: cast_nullable_to_non_nullable
|
autoTranslate: null == autoTranslate ? _self.autoTranslate : autoTranslate // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
|
as bool,dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -77,7 +77,8 @@ as bool,customFonts: freezed == customFonts ? _self.customFonts : customFonts //
|
|||||||
as String?,appColorScheme: freezed == appColorScheme ? _self.appColorScheme : appColorScheme // ignore: cast_nullable_to_non_nullable
|
as String?,appColorScheme: freezed == appColorScheme ? _self.appColorScheme : appColorScheme // ignore: cast_nullable_to_non_nullable
|
||||||
as int?,windowSize: freezed == windowSize ? _self.windowSize : windowSize // ignore: cast_nullable_to_non_nullable
|
as int?,windowSize: freezed == windowSize ? _self.windowSize : windowSize // ignore: cast_nullable_to_non_nullable
|
||||||
as Size?,defaultPoolId: freezed == defaultPoolId ? _self.defaultPoolId : defaultPoolId // ignore: cast_nullable_to_non_nullable
|
as Size?,defaultPoolId: freezed == defaultPoolId ? _self.defaultPoolId : defaultPoolId // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,
|
as String?,messageDisplayStyle: null == messageDisplayStyle ? _self.messageDisplayStyle : messageDisplayStyle // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,10 +160,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, String? defaultPoolId)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, String? defaultPoolId, String messageDisplayStyle)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _AppSettings() when $default != null:
|
case _AppSettings() when $default != null:
|
||||||
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.windowSize,_that.defaultPoolId);case _:
|
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.windowSize,_that.defaultPoolId,_that.messageDisplayStyle);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -180,10 +181,10 @@ return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_tha
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, String? defaultPoolId) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, String? defaultPoolId, String messageDisplayStyle) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _AppSettings():
|
case _AppSettings():
|
||||||
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.windowSize,_that.defaultPoolId);}
|
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.windowSize,_that.defaultPoolId,_that.messageDisplayStyle);}
|
||||||
}
|
}
|
||||||
/// A variant of `when` that fallback to returning `null`
|
/// A variant of `when` that fallback to returning `null`
|
||||||
///
|
///
|
||||||
@@ -197,10 +198,10 @@ return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_tha
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, String? defaultPoolId)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, String? defaultPoolId, String messageDisplayStyle)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _AppSettings() when $default != null:
|
case _AppSettings() when $default != null:
|
||||||
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.windowSize,_that.defaultPoolId);case _:
|
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.windowSize,_that.defaultPoolId,_that.messageDisplayStyle);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -212,7 +213,7 @@ return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_tha
|
|||||||
|
|
||||||
|
|
||||||
class _AppSettings implements AppSettings {
|
class _AppSettings implements AppSettings {
|
||||||
const _AppSettings({required this.autoTranslate, required this.dataSavingMode, required this.soundEffects, required this.aprilFoolFeatures, required this.enterToSend, required this.appBarTransparent, required this.showBackgroundImage, required this.customFonts, required this.appColorScheme, required this.windowSize, required this.defaultPoolId});
|
const _AppSettings({required this.autoTranslate, required this.dataSavingMode, required this.soundEffects, required this.aprilFoolFeatures, required this.enterToSend, required this.appBarTransparent, required this.showBackgroundImage, required this.customFonts, required this.appColorScheme, required this.windowSize, required this.defaultPoolId, required this.messageDisplayStyle});
|
||||||
|
|
||||||
|
|
||||||
@override final bool autoTranslate;
|
@override final bool autoTranslate;
|
||||||
@@ -228,6 +229,7 @@ class _AppSettings implements AppSettings {
|
|||||||
@override final Size? windowSize;
|
@override final Size? windowSize;
|
||||||
// The window size for desktop platforms
|
// The window size for desktop platforms
|
||||||
@override final String? defaultPoolId;
|
@override final String? defaultPoolId;
|
||||||
|
@override final String messageDisplayStyle;
|
||||||
|
|
||||||
/// Create a copy of AppSettings
|
/// Create a copy of AppSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -239,16 +241,16 @@ _$AppSettingsCopyWith<_AppSettings> get copyWith => __$AppSettingsCopyWithImpl<_
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.autoTranslate, autoTranslate) || other.autoTranslate == autoTranslate)&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.aprilFoolFeatures, aprilFoolFeatures) || other.aprilFoolFeatures == aprilFoolFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.autoTranslate, autoTranslate) || other.autoTranslate == autoTranslate)&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.aprilFoolFeatures, aprilFoolFeatures) || other.aprilFoolFeatures == aprilFoolFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,autoTranslate,dataSavingMode,soundEffects,aprilFoolFeatures,enterToSend,appBarTransparent,showBackgroundImage,customFonts,appColorScheme,windowSize,defaultPoolId);
|
int get hashCode => Object.hash(runtimeType,autoTranslate,dataSavingMode,soundEffects,aprilFoolFeatures,enterToSend,appBarTransparent,showBackgroundImage,customFonts,appColorScheme,windowSize,defaultPoolId,messageDisplayStyle);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AppSettings(autoTranslate: $autoTranslate, dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, aprilFoolFeatures: $aprilFoolFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, customFonts: $customFonts, appColorScheme: $appColorScheme, windowSize: $windowSize, defaultPoolId: $defaultPoolId)';
|
return 'AppSettings(autoTranslate: $autoTranslate, dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, aprilFoolFeatures: $aprilFoolFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, customFonts: $customFonts, appColorScheme: $appColorScheme, windowSize: $windowSize, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -259,7 +261,7 @@ abstract mixin class _$AppSettingsCopyWith<$Res> implements $AppSettingsCopyWith
|
|||||||
factory _$AppSettingsCopyWith(_AppSettings value, $Res Function(_AppSettings) _then) = __$AppSettingsCopyWithImpl;
|
factory _$AppSettingsCopyWith(_AppSettings value, $Res Function(_AppSettings) _then) = __$AppSettingsCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, String? defaultPoolId
|
bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, String? defaultPoolId, String messageDisplayStyle
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -276,7 +278,7 @@ class __$AppSettingsCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of AppSettings
|
/// Create a copy of AppSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? autoTranslate = null,Object? dataSavingMode = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? windowSize = freezed,Object? defaultPoolId = freezed,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? autoTranslate = null,Object? dataSavingMode = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? windowSize = freezed,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,}) {
|
||||||
return _then(_AppSettings(
|
return _then(_AppSettings(
|
||||||
autoTranslate: null == autoTranslate ? _self.autoTranslate : autoTranslate // ignore: cast_nullable_to_non_nullable
|
autoTranslate: null == autoTranslate ? _self.autoTranslate : autoTranslate // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
|
as bool,dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -289,7 +291,8 @@ as bool,customFonts: freezed == customFonts ? _self.customFonts : customFonts //
|
|||||||
as String?,appColorScheme: freezed == appColorScheme ? _self.appColorScheme : appColorScheme // ignore: cast_nullable_to_non_nullable
|
as String?,appColorScheme: freezed == appColorScheme ? _self.appColorScheme : appColorScheme // ignore: cast_nullable_to_non_nullable
|
||||||
as int?,windowSize: freezed == windowSize ? _self.windowSize : windowSize // ignore: cast_nullable_to_non_nullable
|
as int?,windowSize: freezed == windowSize ? _self.windowSize : windowSize // ignore: cast_nullable_to_non_nullable
|
||||||
as Size?,defaultPoolId: freezed == defaultPoolId ? _self.defaultPoolId : defaultPoolId // ignore: cast_nullable_to_non_nullable
|
as Size?,defaultPoolId: freezed == defaultPoolId ? _self.defaultPoolId : defaultPoolId // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,
|
as String?,messageDisplayStyle: null == messageDisplayStyle ? _self.messageDisplayStyle : messageDisplayStyle // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,7 +7,7 @@ part of 'config.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$appSettingsNotifierHash() =>
|
String _$appSettingsNotifierHash() =>
|
||||||
r'a623ad859b71f42d0527b7f8b75bd37a6fd5d5c7';
|
r'9f0979f18b107e61185391e7c39bd81ac4b8ca50';
|
||||||
|
|
||||||
/// See also [AppSettingsNotifier].
|
/// See also [AppSettingsNotifier].
|
||||||
@ProviderFor(AppSettingsNotifier)
|
@ProviderFor(AppSettingsNotifier)
|
||||||
|
@@ -12,6 +12,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/pods/userinfo.dart';
|
||||||
import 'package:island/services/color_extraction.dart';
|
import 'package:island/services/color_extraction.dart';
|
||||||
import 'package:island/services/responsive.dart';
|
import 'package:island/services/responsive.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
@@ -35,7 +36,8 @@ class SettingsScreen extends HookConsumerWidget {
|
|||||||
final isDesktop =
|
final isDesktop =
|
||||||
!kIsWeb && (Platform.isWindows || Platform.isMacOS || Platform.isLinux);
|
!kIsWeb && (Platform.isWindows || Platform.isMacOS || Platform.isLinux);
|
||||||
final isWide = isWideScreen(context);
|
final isWide = isWideScreen(context);
|
||||||
final poolsAsync = ref.watch(poolsProvider);
|
final pools = ref.watch(poolsProvider);
|
||||||
|
final user = ref.watch(userInfoProvider);
|
||||||
final docBasepath = useState<String?>(null);
|
final docBasepath = useState<String?>(null);
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
@@ -129,6 +131,48 @@ class SettingsScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Message display style settings
|
||||||
|
ListTile(
|
||||||
|
minLeadingWidth: 48,
|
||||||
|
title: Text('settingsMessageDisplayStyle').tr(),
|
||||||
|
contentPadding: const EdgeInsets.only(left: 24, right: 17),
|
||||||
|
leading: const Icon(Symbols.chat),
|
||||||
|
trailing: DropdownButtonHideUnderline(
|
||||||
|
child: DropdownButton2<String>(
|
||||||
|
isExpanded: true,
|
||||||
|
items: [
|
||||||
|
DropdownMenuItem<String>(
|
||||||
|
value: 'bubble',
|
||||||
|
child: Text('Bubble').fontSize(14),
|
||||||
|
),
|
||||||
|
DropdownMenuItem<String>(
|
||||||
|
value: 'discord',
|
||||||
|
child: Text('Discord').fontSize(14),
|
||||||
|
),
|
||||||
|
DropdownMenuItem<String>(
|
||||||
|
value: 'irc',
|
||||||
|
child: Text('IRC').fontSize(14),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
value: settings.messageDisplayStyle,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value != null) {
|
||||||
|
ref
|
||||||
|
.read(appSettingsNotifierProvider.notifier)
|
||||||
|
.setMessageDisplayStyle(value);
|
||||||
|
showSnackBar('settingsApplied'.tr());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buttonStyleData: const ButtonStyleData(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 5),
|
||||||
|
height: 40,
|
||||||
|
width: 140,
|
||||||
|
),
|
||||||
|
menuItemStyleData: const MenuItemStyleData(height: 40),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
// Color scheme settings
|
// Color scheme settings
|
||||||
ListTile(
|
ListTile(
|
||||||
minLeadingWidth: 48,
|
minLeadingWidth: 48,
|
||||||
@@ -370,65 +414,67 @@ class SettingsScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
poolsAsync.when(
|
if (user.value != null)
|
||||||
data: (pools) {
|
pools.when(
|
||||||
final validPools = pools.filterValid();
|
data: (data) {
|
||||||
final currentPoolId = resolveDefaultPoolId(ref, pools);
|
final validPools = data.filterValid();
|
||||||
|
final currentPoolId = resolveDefaultPoolId(ref, data);
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
isThreeLine: true,
|
isThreeLine: true,
|
||||||
minLeadingWidth: 48,
|
|
||||||
title: Text('settingsDefaultPool').tr(),
|
|
||||||
contentPadding: const EdgeInsets.only(left: 24, right: 17),
|
|
||||||
leading: const Icon(Symbols.cloud),
|
|
||||||
subtitle: Text(
|
|
||||||
validPools
|
|
||||||
.firstWhereOrNull((p) => p.id == currentPoolId)
|
|
||||||
?.description ??
|
|
||||||
'settingsDefaultPoolHelper'.tr(),
|
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
|
||||||
),
|
|
||||||
trailing: DropdownButtonHideUnderline(
|
|
||||||
child: DropdownButton2<String>(
|
|
||||||
isExpanded: true,
|
|
||||||
items:
|
|
||||||
validPools.map((p) {
|
|
||||||
return DropdownMenuItem<String>(
|
|
||||||
value: p.id,
|
|
||||||
child: Text(p.name).fontSize(14),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
value: currentPoolId,
|
|
||||||
onChanged: (value) {
|
|
||||||
ref
|
|
||||||
.read(appSettingsNotifierProvider.notifier)
|
|
||||||
.setDefaultPoolId(value);
|
|
||||||
showSnackBar('settingsApplied'.tr());
|
|
||||||
},
|
|
||||||
buttonStyleData: const ButtonStyleData(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 5),
|
|
||||||
height: 40,
|
|
||||||
width: 220,
|
|
||||||
),
|
|
||||||
menuItemStyleData: const MenuItemStyleData(height: 40),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
loading:
|
|
||||||
() => const ListTile(
|
|
||||||
minLeadingWidth: 48,
|
|
||||||
title: Text('Loading pools...'),
|
|
||||||
leading: CircularProgressIndicator(),
|
|
||||||
),
|
|
||||||
error:
|
|
||||||
(err, st) => ListTile(
|
|
||||||
minLeadingWidth: 48,
|
minLeadingWidth: 48,
|
||||||
title: Text('settingsDefaultPool').tr(),
|
title: Text('settingsDefaultPool').tr(),
|
||||||
subtitle: Text('Error: $err'),
|
contentPadding: const EdgeInsets.only(left: 24, right: 17),
|
||||||
leading: const Icon(Icons.error, color: Colors.red),
|
leading: const Icon(Symbols.cloud),
|
||||||
),
|
subtitle: Text(
|
||||||
),
|
'settingsDefaultPoolHelper'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
trailing: DropdownButtonHideUnderline(
|
||||||
|
child: DropdownButton2<String>(
|
||||||
|
isExpanded: true,
|
||||||
|
items:
|
||||||
|
validPools.map((p) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: p.id,
|
||||||
|
child: Text(
|
||||||
|
p.name,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
).fontSize(14),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
value: currentPoolId,
|
||||||
|
onChanged: (value) {
|
||||||
|
ref
|
||||||
|
.read(appSettingsNotifierProvider.notifier)
|
||||||
|
.setDefaultPoolId(value);
|
||||||
|
showSnackBar('settingsApplied'.tr());
|
||||||
|
},
|
||||||
|
buttonStyleData: const ButtonStyleData(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 5),
|
||||||
|
height: 40,
|
||||||
|
width: 120,
|
||||||
|
),
|
||||||
|
menuItemStyleData: const MenuItemStyleData(height: 40),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading:
|
||||||
|
() => const ListTile(
|
||||||
|
minLeadingWidth: 48,
|
||||||
|
title: Text('Loading pools...'),
|
||||||
|
leading: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
error:
|
||||||
|
(err, st) => ListTile(
|
||||||
|
minLeadingWidth: 48,
|
||||||
|
title: Text('settingsDefaultPool').tr(),
|
||||||
|
subtitle: Text('Error: $err'),
|
||||||
|
leading: const Icon(Icons.error, color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
final behaviorSettings = [
|
final behaviorSettings = [
|
||||||
|
@@ -18,6 +18,32 @@ class MessageContent extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (item.type == 'messages.delete' || item.deletedAt != null) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.delete,
|
||||||
|
size: 14,
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Text(
|
||||||
|
item.content ?? 'Deleted a message',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
||||||
|
fontStyle: FontStyle.italic,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case 'call.start':
|
case 'call.start':
|
||||||
case 'call.ended':
|
case 'call.ended':
|
||||||
@@ -71,30 +97,6 @@ class MessageContent extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
case 'messages.delete':
|
|
||||||
return Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Symbols.delete,
|
|
||||||
size: 14,
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
|
||||||
),
|
|
||||||
const Gap(4),
|
|
||||||
Text(
|
|
||||||
item.content ?? 'Deleted a message',
|
|
||||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.onSurfaceVariant.withOpacity(0.6),
|
|
||||||
fontStyle: FontStyle.italic,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
case 'text':
|
case 'text':
|
||||||
default:
|
default:
|
||||||
return Column(
|
return Column(
|
||||||
|
@@ -13,18 +13,21 @@ import 'package:island/database/message.dart';
|
|||||||
import 'package:island/models/embed.dart';
|
import 'package:island/models/embed.dart';
|
||||||
import 'package:island/pods/messages_notifier.dart';
|
import 'package:island/pods/messages_notifier.dart';
|
||||||
import 'package:island/pods/translate.dart';
|
import 'package:island/pods/translate.dart';
|
||||||
|
import 'package:island/pods/config.dart';
|
||||||
import 'package:island/screens/chat/room.dart';
|
import 'package:island/screens/chat/room.dart';
|
||||||
import 'package:island/utils/mapping.dart';
|
import 'package:island/utils/mapping.dart';
|
||||||
|
import 'package:island/widgets/account/account_pfc.dart';
|
||||||
import 'package:island/widgets/app_scaffold.dart';
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
import 'package:island/widgets/chat/message_content.dart';
|
import 'package:island/widgets/chat/message_content.dart';
|
||||||
import 'package:island/widgets/chat/message_indicators.dart';
|
import 'package:island/widgets/chat/message_indicators.dart';
|
||||||
import 'package:island/widgets/chat/message_sender_info.dart';
|
import 'package:island/widgets/chat/message_sender_info.dart';
|
||||||
import 'package:island/widgets/content/alert.native.dart';
|
import 'package:island/widgets/content/alert.native.dart';
|
||||||
import 'package:island/widgets/content/cloud_file_collection.dart';
|
import 'package:island/widgets/content/cloud_file_collection.dart';
|
||||||
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
import 'package:island/widgets/content/embed/link.dart';
|
import 'package:island/widgets/content/embed/link.dart';
|
||||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:super_context_menu/super_context_menu.dart';
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
|
|
||||||
class MessageItemAction {
|
class MessageItemAction {
|
||||||
static const String edit = "edit";
|
static const String edit = "edit";
|
||||||
@@ -51,6 +54,189 @@ class MessageItem extends HookConsumerWidget {
|
|||||||
required this.onJump,
|
required this.onJump,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final remoteMessage = message.toRemoteMessage();
|
||||||
|
final settings = ref.watch(appSettingsNotifierProvider);
|
||||||
|
|
||||||
|
final isMobile = !kIsWeb && (Platform.isAndroid || Platform.isIOS);
|
||||||
|
|
||||||
|
final messageLanguage =
|
||||||
|
remoteMessage.content != null
|
||||||
|
? ref.watch(detectStringLanguageProvider(remoteMessage.content!))
|
||||||
|
: null;
|
||||||
|
|
||||||
|
final currentLanguage = context.locale.toString();
|
||||||
|
final translatableLanguage =
|
||||||
|
messageLanguage != null
|
||||||
|
? messageLanguage.substring(0, 2) != currentLanguage.substring(0, 2)
|
||||||
|
: false;
|
||||||
|
|
||||||
|
final translating = useState(false);
|
||||||
|
final translatedText = useState<String?>(null);
|
||||||
|
|
||||||
|
Future<void> translate() async {
|
||||||
|
if (translatedText.value != null) {
|
||||||
|
translatedText.value = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (translating.value) return;
|
||||||
|
if (remoteMessage.content == null) return;
|
||||||
|
translating.value = true;
|
||||||
|
try {
|
||||||
|
final text = await ref.watch(
|
||||||
|
translateStringProvider(
|
||||||
|
TranslateQuery(
|
||||||
|
text: remoteMessage.content!,
|
||||||
|
lang: currentLanguage.substring(0, 2),
|
||||||
|
),
|
||||||
|
).future,
|
||||||
|
);
|
||||||
|
translatedText.value = text;
|
||||||
|
} catch (err) {
|
||||||
|
showErrorAlert(err);
|
||||||
|
} finally {
|
||||||
|
translating.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showActionMenu() {
|
||||||
|
if (onAction == null) return;
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder:
|
||||||
|
(context) => SheetScaffold(
|
||||||
|
titleText: 'messageActions'.tr(),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
if (isCurrentUser)
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Symbols.edit),
|
||||||
|
title: Text('edit'.tr()),
|
||||||
|
onTap: () {
|
||||||
|
onAction!.call(MessageItemAction.edit);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (isCurrentUser)
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Symbols.delete),
|
||||||
|
title: Text('delete'.tr()),
|
||||||
|
onTap: () {
|
||||||
|
onAction!.call(MessageItemAction.delete);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (isCurrentUser) Divider(),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Symbols.reply),
|
||||||
|
title: Text('reply'.tr()),
|
||||||
|
onTap: () {
|
||||||
|
onAction!.call(MessageItemAction.reply);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Symbols.forward),
|
||||||
|
title: Text('forward'.tr()),
|
||||||
|
onTap: () {
|
||||||
|
onAction!.call(MessageItemAction.forward);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (translatableLanguage) Divider(),
|
||||||
|
if (translatableLanguage)
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Symbols.translate),
|
||||||
|
title: Text(
|
||||||
|
translatedText.value == null
|
||||||
|
? 'translate'.tr()
|
||||||
|
: translating.value
|
||||||
|
? 'translating'.tr()
|
||||||
|
: 'translated'.tr(),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
translate();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (isMobile) Divider(),
|
||||||
|
if (isMobile)
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Symbols.copy_all),
|
||||||
|
title: Text('copyMessage'.tr()),
|
||||||
|
onTap: () {
|
||||||
|
Clipboard.setData(
|
||||||
|
ClipboardData(text: remoteMessage.content ?? ''),
|
||||||
|
);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onLongPress: showActionMenu,
|
||||||
|
onSecondaryTap: showActionMenu,
|
||||||
|
child: switch (settings.messageDisplayStyle) {
|
||||||
|
'irc' => MessageItemDisplayIRC(
|
||||||
|
message: message,
|
||||||
|
isCurrentUser: isCurrentUser,
|
||||||
|
progress: progress,
|
||||||
|
showAvatar: showAvatar,
|
||||||
|
onJump: onJump,
|
||||||
|
translatedText: translatedText.value,
|
||||||
|
translating: translating.value,
|
||||||
|
),
|
||||||
|
'discord' => MessageItemDisplayDiscord(
|
||||||
|
message: message,
|
||||||
|
isCurrentUser: isCurrentUser,
|
||||||
|
progress: progress,
|
||||||
|
showAvatar: showAvatar,
|
||||||
|
onJump: onJump,
|
||||||
|
translatedText: translatedText.value,
|
||||||
|
translating: translating.value,
|
||||||
|
),
|
||||||
|
_ => MessageItemDisplayBubble(
|
||||||
|
message: message,
|
||||||
|
isCurrentUser: isCurrentUser,
|
||||||
|
progress: progress,
|
||||||
|
showAvatar: showAvatar,
|
||||||
|
onJump: onJump,
|
||||||
|
translatedText: translatedText.value,
|
||||||
|
translating: translating.value,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageItemDisplayBubble extends HookConsumerWidget {
|
||||||
|
final LocalChatMessage message;
|
||||||
|
final bool isCurrentUser;
|
||||||
|
final Map<int, double>? progress;
|
||||||
|
final bool showAvatar;
|
||||||
|
final Function(String messageId) onJump;
|
||||||
|
final String? translatedText;
|
||||||
|
final bool translating;
|
||||||
|
|
||||||
|
const MessageItemDisplayBubble({
|
||||||
|
super.key,
|
||||||
|
required this.message,
|
||||||
|
required this.isCurrentUser,
|
||||||
|
required this.progress,
|
||||||
|
required this.showAvatar,
|
||||||
|
required this.onJump,
|
||||||
|
required this.translatedText,
|
||||||
|
required this.translating,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final textColor =
|
final textColor =
|
||||||
@@ -108,261 +294,455 @@ class MessageItem extends HookConsumerWidget {
|
|||||||
final remoteMessage = message.toRemoteMessage();
|
final remoteMessage = message.toRemoteMessage();
|
||||||
final sender = remoteMessage.sender;
|
final sender = remoteMessage.sender;
|
||||||
|
|
||||||
final isMobile = !kIsWeb && (Platform.isAndroid || Platform.isIOS);
|
return Material(
|
||||||
|
color:
|
||||||
final messageLanguage =
|
hasBackground
|
||||||
remoteMessage.content != null
|
? Colors.transparent
|
||||||
? ref.watch(detectStringLanguageProvider(remoteMessage.content!))
|
: Theme.of(context).colorScheme.surface,
|
||||||
: null;
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||||
final currentLanguage = context.locale.toString();
|
child: Column(
|
||||||
final translatableLanguage =
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
messageLanguage != null
|
mainAxisSize: MainAxisSize.min,
|
||||||
? messageLanguage.substring(0, 2) != currentLanguage.substring(0, 2)
|
|
||||||
: false;
|
|
||||||
|
|
||||||
final translating = useState(false);
|
|
||||||
final translatedText = useState<String?>(null);
|
|
||||||
|
|
||||||
Future<void> translate() async {
|
|
||||||
if (translatedText.value != null) {
|
|
||||||
translatedText.value = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (translating.value) return;
|
|
||||||
if (remoteMessage.content == null) return;
|
|
||||||
translating.value = true;
|
|
||||||
try {
|
|
||||||
final text = await ref.watch(
|
|
||||||
translateStringProvider(
|
|
||||||
TranslateQuery(
|
|
||||||
text: remoteMessage.content!,
|
|
||||||
lang: currentLanguage.substring(0, 2),
|
|
||||||
),
|
|
||||||
).future,
|
|
||||||
);
|
|
||||||
translatedText.value = text;
|
|
||||||
} catch (err) {
|
|
||||||
showErrorAlert(err);
|
|
||||||
} finally {
|
|
||||||
translating.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ContextMenuWidget(
|
|
||||||
menuProvider: (_) {
|
|
||||||
if (onAction == null) return Menu(children: []);
|
|
||||||
return Menu(
|
|
||||||
children: [
|
children: [
|
||||||
if (isCurrentUser)
|
if (showAvatar) ...[
|
||||||
MenuAction(
|
const Gap(8),
|
||||||
title: 'edit'.tr(),
|
MessageSenderInfo(
|
||||||
image: MenuImage.icon(Symbols.edit),
|
sender: sender,
|
||||||
callback: () {
|
createdAt: message.createdAt,
|
||||||
onAction!.call(MessageItemAction.edit);
|
textColor: textColor,
|
||||||
},
|
|
||||||
),
|
),
|
||||||
if (isCurrentUser)
|
const Gap(4),
|
||||||
MenuAction(
|
],
|
||||||
title: 'delete'.tr(),
|
const Gap(2),
|
||||||
image: MenuImage.icon(Symbols.delete),
|
Row(
|
||||||
callback: () {
|
spacing: 4,
|
||||||
onAction!.call(MessageItemAction.delete);
|
mainAxisSize: MainAxisSize.min,
|
||||||
},
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
),
|
children: [
|
||||||
if (isCurrentUser) MenuSeparator(),
|
Flexible(
|
||||||
MenuAction(
|
child: AnimatedContainer(
|
||||||
title: 'reply'.tr(),
|
duration: const Duration(milliseconds: 200),
|
||||||
image: MenuImage.icon(Symbols.reply),
|
decoration: BoxDecoration(
|
||||||
callback: () {
|
color: flashColor,
|
||||||
onAction!.call(MessageItemAction.reply);
|
borderRadius: BorderRadius.circular(16),
|
||||||
},
|
),
|
||||||
),
|
padding: const EdgeInsets.symmetric(
|
||||||
MenuAction(
|
horizontal: 12,
|
||||||
title: 'forward'.tr(),
|
vertical: 6,
|
||||||
image: MenuImage.icon(Symbols.forward),
|
),
|
||||||
callback: () {
|
child: Column(
|
||||||
onAction!.call(MessageItemAction.forward);
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
},
|
children: [
|
||||||
),
|
if (remoteMessage.repliedMessageId != null)
|
||||||
if (translatableLanguage) MenuSeparator(),
|
MessageQuoteWidget(
|
||||||
if (translatableLanguage)
|
message: message,
|
||||||
MenuAction(
|
textColor: textColor,
|
||||||
title:
|
isReply: true,
|
||||||
translatedText.value == null
|
).padding(vertical: 4),
|
||||||
? 'translate'.tr()
|
if (remoteMessage.forwardedMessageId != null)
|
||||||
: translating.value
|
MessageQuoteWidget(
|
||||||
? 'translating'.tr()
|
message: message,
|
||||||
: 'translated'.tr(),
|
textColor: textColor,
|
||||||
image: MenuImage.icon(Symbols.translate),
|
isReply: false,
|
||||||
callback: translate,
|
).padding(vertical: 4),
|
||||||
),
|
if (MessageContent.hasContent(remoteMessage))
|
||||||
if (isMobile) MenuSeparator(),
|
MessageContent(
|
||||||
if (isMobile)
|
item: remoteMessage,
|
||||||
MenuAction(
|
translatedText: translatedText,
|
||||||
title: 'copyMessage'.tr(),
|
),
|
||||||
image: MenuImage.icon(Symbols.copy_all),
|
if (remoteMessage.attachments.isNotEmpty)
|
||||||
callback: () {
|
LayoutBuilder(
|
||||||
Clipboard.setData(
|
builder: (context, constraints) {
|
||||||
ClipboardData(text: remoteMessage.content ?? ''),
|
return CloudFileList(
|
||||||
);
|
files: remoteMessage.attachments,
|
||||||
},
|
maxWidth: constraints.maxWidth,
|
||||||
),
|
padding: EdgeInsets.symmetric(vertical: 4),
|
||||||
],
|
);
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
child: Material(
|
if (remoteMessage.meta['embeds'] != null)
|
||||||
color:
|
...((remoteMessage.meta['embeds'] as List<dynamic>)
|
||||||
hasBackground
|
.map((embed) => convertMapKeysToSnakeCase(embed))
|
||||||
? Colors.transparent
|
.where((embed) => embed['type'] == 'link')
|
||||||
: Theme.of(context).colorScheme.surface,
|
.map((embed) => SnScrappedLink.fromJson(embed))
|
||||||
child: Padding(
|
.map(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
(link) => LayoutBuilder(
|
||||||
child: Column(
|
builder: (context, constraints) {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
return EmbedLinkWidget(
|
||||||
mainAxisSize: MainAxisSize.min,
|
link: link,
|
||||||
children: [
|
maxWidth: math.min(
|
||||||
if (showAvatar) ...[
|
constraints.maxWidth,
|
||||||
const Gap(8),
|
480,
|
||||||
MessageSenderInfo(
|
),
|
||||||
sender: sender,
|
margin: const EdgeInsets.symmetric(
|
||||||
createdAt: message.createdAt,
|
vertical: 4,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList()),
|
||||||
|
if (progress != null && progress!.isNotEmpty)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
if ((remoteMessage.content?.isNotEmpty ?? false))
|
||||||
|
const Gap(0),
|
||||||
|
for (var entry in progress!.entries)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'fileUploadingProgress'.tr(
|
||||||
|
args: [
|
||||||
|
(entry.key + 1).toString(),
|
||||||
|
entry.value.toStringAsFixed(1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: textColor.withOpacity(0.8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
LinearProgressIndicator(
|
||||||
|
value: entry.value / 100,
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.surfaceVariant,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(0),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MessageIndicators(
|
||||||
|
editedAt: remoteMessage.editedAt,
|
||||||
|
status: message.status,
|
||||||
|
isCurrentUser: isCurrentUser,
|
||||||
textColor: textColor,
|
textColor: textColor,
|
||||||
),
|
),
|
||||||
const Gap(4),
|
|
||||||
],
|
],
|
||||||
const Gap(2),
|
),
|
||||||
Row(
|
],
|
||||||
spacing: 4,
|
),
|
||||||
mainAxisSize: MainAxisSize.min,
|
),
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageItemDisplayIRC extends HookConsumerWidget {
|
||||||
|
final LocalChatMessage message;
|
||||||
|
final bool isCurrentUser;
|
||||||
|
final Map<int, double>? progress;
|
||||||
|
final bool showAvatar;
|
||||||
|
final Function(String messageId) onJump;
|
||||||
|
final String? translatedText;
|
||||||
|
final bool translating;
|
||||||
|
|
||||||
|
const MessageItemDisplayIRC({
|
||||||
|
super.key,
|
||||||
|
required this.message,
|
||||||
|
required this.isCurrentUser,
|
||||||
|
required this.progress,
|
||||||
|
required this.showAvatar,
|
||||||
|
required this.onJump,
|
||||||
|
required this.translatedText,
|
||||||
|
required this.translating,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final remoteMessage = message.toRemoteMessage();
|
||||||
|
final sender = remoteMessage.sender;
|
||||||
|
final textColor = Theme.of(context).colorScheme.onSurfaceVariant;
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 2),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
DateFormat('HH:mm').format(message.createdAt),
|
||||||
|
style: TextStyle(color: textColor.withOpacity(0.7), fontSize: 12),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'<${sender.account.nick}>',
|
||||||
|
style: TextStyle(color: Colors.blue),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
translatedText ?? remoteMessage.content ?? '',
|
||||||
|
style: TextStyle(color: textColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageItemDisplayDiscord extends HookConsumerWidget {
|
||||||
|
final LocalChatMessage message;
|
||||||
|
final bool isCurrentUser;
|
||||||
|
final Map<int, double>? progress;
|
||||||
|
final bool showAvatar;
|
||||||
|
final Function(String messageId) onJump;
|
||||||
|
final String? translatedText;
|
||||||
|
final bool translating;
|
||||||
|
|
||||||
|
const MessageItemDisplayDiscord({
|
||||||
|
super.key,
|
||||||
|
required this.message,
|
||||||
|
required this.isCurrentUser,
|
||||||
|
required this.progress,
|
||||||
|
required this.showAvatar,
|
||||||
|
required this.onJump,
|
||||||
|
required this.translatedText,
|
||||||
|
required this.translating,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final textColor = Theme.of(context).colorScheme.onSurfaceVariant;
|
||||||
|
final remoteMessage = message.toRemoteMessage();
|
||||||
|
final sender = remoteMessage.sender;
|
||||||
|
|
||||||
|
const kAvatarRadius = 12.0;
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||||
|
child:
|
||||||
|
showAvatar
|
||||||
|
? Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
Row(
|
||||||
child: AnimatedContainer(
|
spacing: 8,
|
||||||
duration: const Duration(milliseconds: 200),
|
children: [
|
||||||
decoration: BoxDecoration(
|
AccountPfcGestureDetector(
|
||||||
color: flashColor,
|
uname: sender.account.name,
|
||||||
borderRadius: BorderRadius.circular(16),
|
child: ProfilePictureWidget(
|
||||||
|
file: sender.account.profile.picture,
|
||||||
|
radius: kAvatarRadius,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(
|
MessageSenderInfo(
|
||||||
horizontal: 12,
|
sender: sender,
|
||||||
vertical: 6,
|
createdAt: message.createdAt,
|
||||||
|
textColor: textColor,
|
||||||
|
showAvatar: false,
|
||||||
|
isCompact: true,
|
||||||
),
|
),
|
||||||
child: Column(
|
],
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
),
|
||||||
children: [
|
Column(
|
||||||
if (remoteMessage.repliedMessageId != null)
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
MessageQuoteWidget(
|
children: [
|
||||||
message: message,
|
if (remoteMessage.repliedMessageId != null)
|
||||||
textColor: textColor,
|
MessageQuoteWidget(
|
||||||
isReply: true,
|
message: message,
|
||||||
).padding(vertical: 4),
|
textColor: textColor,
|
||||||
if (remoteMessage.forwardedMessageId != null)
|
isReply: true,
|
||||||
MessageQuoteWidget(
|
).padding(vertical: 4),
|
||||||
message: message,
|
if (remoteMessage.forwardedMessageId != null)
|
||||||
textColor: textColor,
|
MessageQuoteWidget(
|
||||||
isReply: false,
|
message: message,
|
||||||
).padding(vertical: 4),
|
textColor: textColor,
|
||||||
if (MessageContent.hasContent(remoteMessage))
|
isReply: false,
|
||||||
MessageContent(
|
).padding(vertical: 4),
|
||||||
item: remoteMessage,
|
if (MessageContent.hasContent(remoteMessage))
|
||||||
translatedText: translatedText.value,
|
MessageContent(
|
||||||
),
|
item: remoteMessage,
|
||||||
if (remoteMessage.attachments.isNotEmpty)
|
translatedText: translatedText,
|
||||||
LayoutBuilder(
|
),
|
||||||
|
if (remoteMessage.attachments.isNotEmpty)
|
||||||
|
LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
return CloudFileList(
|
||||||
|
files: remoteMessage.attachments,
|
||||||
|
maxWidth: constraints.maxWidth,
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 4),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (remoteMessage.meta['embeds'] != null)
|
||||||
|
...((remoteMessage.meta['embeds'] as List<dynamic>)
|
||||||
|
.map((embed) => convertMapKeysToSnakeCase(embed))
|
||||||
|
.where((embed) => embed['type'] == 'link')
|
||||||
|
.map((embed) => SnScrappedLink.fromJson(embed))
|
||||||
|
.map(
|
||||||
|
(link) => LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
return EmbedLinkWidget(
|
||||||
|
link: link,
|
||||||
|
maxWidth: math.min(
|
||||||
|
constraints.maxWidth,
|
||||||
|
480,
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.symmetric(
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList()),
|
||||||
|
if (progress != null && progress!.isNotEmpty)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
if ((remoteMessage.content?.isNotEmpty ?? false))
|
||||||
|
const SizedBox.shrink(),
|
||||||
|
for (var entry in progress!.entries)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'fileUploadingProgress'.tr(
|
||||||
|
args: [
|
||||||
|
(entry.key + 1).toString(),
|
||||||
|
entry.value.toStringAsFixed(1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: textColor.withOpacity(0.8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
LinearProgressIndicator(
|
||||||
|
value: entry.value / 100,
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.surfaceVariant,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(0),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(left: kAvatarRadius * 2 + 8),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Padding(
|
||||||
|
padding: EdgeInsets.only(left: kAvatarRadius * 2 + 8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (showAvatar)
|
||||||
|
MessageSenderInfo(
|
||||||
|
sender: sender,
|
||||||
|
createdAt: message.createdAt,
|
||||||
|
textColor: textColor,
|
||||||
|
showAvatar: false,
|
||||||
|
isCompact: true,
|
||||||
|
),
|
||||||
|
if (remoteMessage.repliedMessageId != null)
|
||||||
|
MessageQuoteWidget(
|
||||||
|
message: message,
|
||||||
|
textColor: textColor,
|
||||||
|
isReply: true,
|
||||||
|
).padding(vertical: 4),
|
||||||
|
if (remoteMessage.forwardedMessageId != null)
|
||||||
|
MessageQuoteWidget(
|
||||||
|
message: message,
|
||||||
|
textColor: textColor,
|
||||||
|
isReply: false,
|
||||||
|
).padding(vertical: 4),
|
||||||
|
if (MessageContent.hasContent(remoteMessage))
|
||||||
|
MessageContent(
|
||||||
|
item: remoteMessage,
|
||||||
|
translatedText: translatedText,
|
||||||
|
),
|
||||||
|
if (remoteMessage.attachments.isNotEmpty)
|
||||||
|
LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
return CloudFileList(
|
||||||
|
files: remoteMessage.attachments,
|
||||||
|
maxWidth: constraints.maxWidth,
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 4),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (remoteMessage.meta['embeds'] != null)
|
||||||
|
...((remoteMessage.meta['embeds'] as List<dynamic>)
|
||||||
|
.map((embed) => convertMapKeysToSnakeCase(embed))
|
||||||
|
.where((embed) => embed['type'] == 'link')
|
||||||
|
.map((embed) => SnScrappedLink.fromJson(embed))
|
||||||
|
.map(
|
||||||
|
(link) => LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
return CloudFileList(
|
return EmbedLinkWidget(
|
||||||
files: remoteMessage.attachments,
|
link: link,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: math.min(constraints.maxWidth, 480),
|
||||||
padding: EdgeInsets.symmetric(vertical: 4),
|
margin: const EdgeInsets.symmetric(
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (remoteMessage.meta['embeds'] != null)
|
)
|
||||||
...((remoteMessage.meta['embeds'] as List<dynamic>)
|
.toList()),
|
||||||
.map(
|
if (progress != null && progress!.isNotEmpty)
|
||||||
(embed) => convertMapKeysToSnakeCase(embed),
|
Column(
|
||||||
)
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
.where((embed) => embed['type'] == 'link')
|
spacing: 8,
|
||||||
.map((embed) => SnScrappedLink.fromJson(embed))
|
children: [
|
||||||
.map(
|
if ((remoteMessage.content?.isNotEmpty ?? false))
|
||||||
(link) => LayoutBuilder(
|
const Gap(0),
|
||||||
builder: (context, constraints) {
|
for (var entry in progress!.entries)
|
||||||
return EmbedLinkWidget(
|
|
||||||
link: link,
|
|
||||||
maxWidth: math.min(
|
|
||||||
constraints.maxWidth,
|
|
||||||
480,
|
|
||||||
),
|
|
||||||
margin: const EdgeInsets.symmetric(
|
|
||||||
vertical: 4,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList()),
|
|
||||||
if (progress != null && progress!.isNotEmpty)
|
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: 8,
|
|
||||||
children: [
|
children: [
|
||||||
if ((remoteMessage.content?.isNotEmpty ??
|
Text(
|
||||||
false))
|
'fileUploadingProgress'.tr(
|
||||||
const Gap(0),
|
args: [
|
||||||
for (var entry in progress!.entries)
|
(entry.key + 1).toString(),
|
||||||
Column(
|
entry.value.toStringAsFixed(1),
|
||||||
crossAxisAlignment:
|
|
||||||
CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'fileUploadingProgress'.tr(
|
|
||||||
args: [
|
|
||||||
(entry.key + 1).toString(),
|
|
||||||
entry.value.toStringAsFixed(1),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: textColor.withOpacity(0.8),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Gap(4),
|
|
||||||
LinearProgressIndicator(
|
|
||||||
value: entry.value / 100,
|
|
||||||
backgroundColor:
|
|
||||||
Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.surfaceVariant,
|
|
||||||
valueColor:
|
|
||||||
AlwaysStoppedAnimation<Color>(
|
|
||||||
Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Gap(0),
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: textColor.withOpacity(0.8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
LinearProgressIndicator(
|
||||||
|
value: entry.value / 100,
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.surfaceVariant,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const Gap(0),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
MessageIndicators(
|
|
||||||
editedAt: remoteMessage.editedAt,
|
|
||||||
status: message.status,
|
|
||||||
isCurrentUser: isCurrentUser,
|
|
||||||
textColor: textColor,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -41,7 +41,7 @@ class MessageListTile extends StatelessWidget {
|
|||||||
sender: sender,
|
sender: sender,
|
||||||
createdAt: message.createdAt,
|
createdAt: message.createdAt,
|
||||||
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
compact: true,
|
showAvatar: false,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
MessageContent(item: remoteMessage),
|
MessageContent(item: remoteMessage),
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:island/models/chat.dart';
|
import 'package:island/models/chat.dart';
|
||||||
import 'package:island/widgets/account/account_name.dart';
|
import 'package:island/widgets/account/account_name.dart';
|
||||||
import 'package:island/widgets/account/account_pfc.dart';
|
import 'package:island/widgets/account/account_pfc.dart';
|
||||||
@@ -9,14 +10,16 @@ class MessageSenderInfo extends StatelessWidget {
|
|||||||
final SnChatMember sender;
|
final SnChatMember sender;
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
final Color textColor;
|
final Color textColor;
|
||||||
final bool compact;
|
final bool showAvatar;
|
||||||
|
final bool isCompact;
|
||||||
|
|
||||||
const MessageSenderInfo({
|
const MessageSenderInfo({
|
||||||
super.key,
|
super.key,
|
||||||
required this.sender,
|
required this.sender,
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
required this.textColor,
|
required this.textColor,
|
||||||
this.compact = false,
|
this.showAvatar = true,
|
||||||
|
this.isCompact = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -28,11 +31,13 @@ class MessageSenderInfo extends StatelessWidget {
|
|||||||
? DateFormat('MM/dd HH:mm').format(createdAt.toLocal())
|
? DateFormat('MM/dd HH:mm').format(createdAt.toLocal())
|
||||||
: DateFormat('HH:mm').format(createdAt.toLocal());
|
: DateFormat('HH:mm').format(createdAt.toLocal());
|
||||||
|
|
||||||
if (compact) {
|
if (isCompact) {
|
||||||
return Row(
|
return Row(
|
||||||
spacing: 8,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||||
|
textBaseline: TextBaseline.alphabetic,
|
||||||
children: [
|
children: [
|
||||||
if (!compact)
|
if (showAvatar)
|
||||||
AccountPfcGestureDetector(
|
AccountPfcGestureDetector(
|
||||||
uname: sender.account.name,
|
uname: sender.account.name,
|
||||||
child: ProfilePictureWidget(
|
child: ProfilePictureWidget(
|
||||||
@@ -40,6 +45,34 @@ class MessageSenderInfo extends StatelessWidget {
|
|||||||
radius: 14,
|
radius: 14,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (showAvatar) const Gap(4),
|
||||||
|
AccountName(
|
||||||
|
account: sender.account,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: textColor,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(6),
|
||||||
|
Text(
|
||||||
|
timestamp,
|
||||||
|
style: TextStyle(fontSize: 10, color: textColor.withOpacity(0.7)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showAvatar) {
|
||||||
|
return Row(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
AccountPfcGestureDetector(
|
||||||
|
uname: sender.account.name,
|
||||||
|
child: ProfilePictureWidget(
|
||||||
|
fileId: sender.account.profile.picture?.id,
|
||||||
|
radius: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -84,13 +117,14 @@ class MessageSenderInfo extends StatelessWidget {
|
|||||||
spacing: 8,
|
spacing: 8,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
AccountPfcGestureDetector(
|
if (showAvatar)
|
||||||
uname: sender.account.name,
|
AccountPfcGestureDetector(
|
||||||
child: ProfilePictureWidget(
|
uname: sender.account.name,
|
||||||
fileId: sender.account.profile.picture?.id,
|
child: ProfilePictureWidget(
|
||||||
radius: 16,
|
fileId: sender.account.profile.picture?.id,
|
||||||
|
radius: 16,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: 2,
|
spacing: 2,
|
||||||
|
Reference in New Issue
Block a user