mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-10-09 02:54:14 +00:00
use single file to manage all colors
This commit is contained in:
parent
75df8840ae
commit
8580908536
138 changed files with 1183 additions and 873 deletions
|
|
@ -14,11 +14,17 @@ import 'package:twonly/src/model/error_code.dart';
|
||||||
import 'package:twonly/src/providers/settings.provider.dart';
|
import 'package:twonly/src/providers/settings.provider.dart';
|
||||||
import 'package:twonly/src/services/backup.service.dart';
|
import 'package:twonly/src/services/backup.service.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/visual/themes/colors.dart';
|
||||||
|
|
||||||
|
export 'package:twonly/src/visual/themes/colors.dart' show AppColor;
|
||||||
|
|
||||||
extension ShortCutsExtension on BuildContext {
|
extension ShortCutsExtension on BuildContext {
|
||||||
AppLocalizations get lang => AppLocalizations.of(this)!;
|
AppLocalizations get lang => AppLocalizations.of(this)!;
|
||||||
ColorScheme get color => Theme.of(this).colorScheme;
|
ColorScheme get color => Theme.of(this).colorScheme;
|
||||||
|
Color appColor(AppColor color) {
|
||||||
|
return Theme.of(this).extension<AppColors>()![color];
|
||||||
|
}
|
||||||
|
|
||||||
Future<dynamic> navPush(Widget route) async {
|
Future<dynamic> navPush(Widget route) async {
|
||||||
return Navigator.push(
|
return Navigator.push(
|
||||||
this,
|
this,
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class _AppOutdatedCompState extends State<AppOutdatedComp> {
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8),
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.red.withAlpha(100),
|
color: context.color.errorContainer,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
@ -71,7 +71,7 @@ class _AppOutdatedCompState extends State<AppOutdatedComp> {
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
color: Colors.white,
|
color: context.color.onErrorContainer,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -90,7 +90,7 @@ class _AppOutdatedCompState extends State<AppOutdatedComp> {
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8),
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.red,
|
color: context.color.error,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
@ -101,7 +101,7 @@ class _AppOutdatedCompState extends State<AppOutdatedComp> {
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
color: Colors.white,
|
color: context.color.onError,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -123,7 +123,7 @@ class _AppOutdatedCompState extends State<AppOutdatedComp> {
|
||||||
child: Text(
|
child: Text(
|
||||||
context.lang.appOutdatedBtn,
|
context.lang.appOutdatedBtn,
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
color: Colors.white,
|
color: context.color.onSurface,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ class ConnectionStatusComp extends StatelessWidget {
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
if (!isConnected)
|
if (!isConnected)
|
||||||
const Positioned.fill(
|
Positioned.fill(
|
||||||
child: SpinKitRipple(
|
child: SpinKitRipple(
|
||||||
color: Colors.red,
|
color: context.color.error,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
|
|
@ -29,7 +29,7 @@ class ConnectionStatusComp extends StatelessWidget {
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isConnected
|
color: isConnected
|
||||||
? context.color.primary.withAlpha(100)
|
? context.color.primary.withAlpha(100)
|
||||||
: Colors.red,
|
: context.color.error,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(0.5),
|
padding: const EdgeInsets.all(0.5),
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,11 @@ class ContactRequestBadgeComp extends StatelessWidget {
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: NotificationBadgeComp(
|
child: NotificationBadgeComp(
|
||||||
backgroundColor: isDarkMode(context)
|
backgroundColor: context.color.inverseSurface,
|
||||||
? Colors.white
|
textColor: context.color.onInverseSurface,
|
||||||
: Colors.black,
|
|
||||||
textColor: isDarkMode(context) ? Colors.black : Colors.white,
|
|
||||||
count: count.toString(),
|
count: count.toString(),
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
color: Colors.black,
|
color: context.color.onPrimary,
|
||||||
icon: const FaIcon(
|
icon: const FaIcon(
|
||||||
FontAwesomeIcons.userPlus,
|
FontAwesomeIcons.userPlus,
|
||||||
size: 18,
|
size: 18,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
typedef TextLabelBuilder = Widget Function(String label);
|
typedef TextLabelBuilder = Widget Function(String label);
|
||||||
|
|
||||||
|
|
@ -99,14 +100,9 @@ class _DraggableScrollbarState extends State<DraggableScrollbar>
|
||||||
child: ValueListenableBuilder<double>(
|
child: ValueListenableBuilder<double>(
|
||||||
valueListenable: _thumbOffsetNotifier,
|
valueListenable: _thumbOffsetNotifier,
|
||||||
builder: (context, thumbOffset, child) {
|
builder: (context, thumbOffset, child) {
|
||||||
final isDark =
|
final colors = context.color;
|
||||||
Theme.of(context).brightness == Brightness.dark;
|
final handleColor = colors.surfaceContainerLowest;
|
||||||
final handleColor = isDark
|
final iconColor = colors.onSurfaceVariant;
|
||||||
? Colors.grey.shade900
|
|
||||||
: Colors.white;
|
|
||||||
final iconColor = isDark
|
|
||||||
? Colors.white70
|
|
||||||
: Colors.black54;
|
|
||||||
|
|
||||||
final label = widget.labelBuilder?.call(
|
final label = widget.labelBuilder?.call(
|
||||||
_viewOffsetNotifier.value,
|
_viewOffsetNotifier.value,
|
||||||
|
|
@ -145,17 +141,14 @@ class _DraggableScrollbarState extends State<DraggableScrollbar>
|
||||||
horizontal: 12,
|
horizontal: 12,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color:
|
color: colors.surfaceContainerHigh
|
||||||
(isDark
|
|
||||||
? Colors.grey.shade900
|
|
||||||
: Colors.grey.shade200)
|
|
||||||
.withValues(alpha: 0.95),
|
.withValues(alpha: 0.95),
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
8,
|
8,
|
||||||
),
|
),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withValues(
|
color: colors.shadow.withValues(
|
||||||
alpha: 0.2,
|
alpha: 0.2,
|
||||||
),
|
),
|
||||||
blurRadius: 8,
|
blurRadius: 8,
|
||||||
|
|
@ -166,9 +159,7 @@ class _DraggableScrollbarState extends State<DraggableScrollbar>
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: isDark
|
color: colors.onSurface,
|
||||||
? Colors.white
|
|
||||||
: Colors.black,
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
|
|
@ -182,9 +173,7 @@ class _DraggableScrollbarState extends State<DraggableScrollbar>
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: handleColor,
|
color: handleColor,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isDark
|
color: colors.outlineVariant,
|
||||||
? Colors.white10
|
|
||||||
: Colors.black12,
|
|
||||||
width: 0.5,
|
width: 0.5,
|
||||||
),
|
),
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class EmojiPickerBottom extends StatelessWidget {
|
||||||
margin: const EdgeInsets.all(10),
|
margin: const EdgeInsets.all(10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(32),
|
borderRadius: BorderRadius.circular(32),
|
||||||
color: Colors.grey,
|
color: context.color.outline,
|
||||||
),
|
),
|
||||||
height: 3,
|
height: 3,
|
||||||
width: 60,
|
width: 60,
|
||||||
|
|
@ -66,7 +66,7 @@ class EmojiPickerBottom extends StatelessWidget {
|
||||||
),
|
),
|
||||||
searchViewConfig: SearchViewConfig(
|
searchViewConfig: SearchViewConfig(
|
||||||
backgroundColor: context.color.surfaceContainer,
|
backgroundColor: context.color.surfaceContainer,
|
||||||
buttonIconColor: Colors.white,
|
buttonIconColor: context.color.onSurface,
|
||||||
),
|
),
|
||||||
categoryViewConfig: CategoryViewConfig(
|
categoryViewConfig: CategoryViewConfig(
|
||||||
backgroundColor: context.color.surfaceContainer,
|
backgroundColor: context.color.surfaceContainer,
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
class NotificationBadgeComp extends StatelessWidget {
|
class NotificationBadgeComp extends StatelessWidget {
|
||||||
const NotificationBadgeComp({
|
const NotificationBadgeComp({
|
||||||
required this.count,
|
required this.count,
|
||||||
required this.child,
|
required this.child,
|
||||||
this.backgroundColor = Colors.red,
|
this.backgroundColor,
|
||||||
this.textColor = Colors.white,
|
this.textColor,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
final String count;
|
final String count;
|
||||||
final Color backgroundColor;
|
final Color? backgroundColor;
|
||||||
final Color textColor;
|
final Color? textColor;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -27,14 +28,14 @@ class NotificationBadgeComp extends StatelessWidget {
|
||||||
height: 18,
|
height: 18,
|
||||||
width: 18,
|
width: 18,
|
||||||
child: CircleAvatar(
|
child: CircleAvatar(
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor ?? context.color.error,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Transform.rotate(
|
child: Transform.rotate(
|
||||||
angle: infinity ? 90 * (3.141592653589793 / 180) : 0,
|
angle: infinity ? 90 * (3.141592653589793 / 180) : 0,
|
||||||
child: Text(
|
child: Text(
|
||||||
infinity ? '8' : count,
|
infinity ? '8' : count,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: textColor,
|
color: textColor ?? context.color.onError,
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -143,13 +143,13 @@ class _ProfileQrCodeCompState extends State<ProfileQrCodeComp> {
|
||||||
? Container(
|
? Container(
|
||||||
key: const ValueKey('qr_code_container'),
|
key: const ValueKey('qr_code_container'),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: context.color.primary,
|
color: context.appColor(AppColor.qrBackground),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black26,
|
color: context.color.shadow.withValues(alpha: 0.2),
|
||||||
blurRadius: 3,
|
blurRadius: 3,
|
||||||
offset: Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -159,11 +159,11 @@ class _ProfileQrCodeCompState extends State<ProfileQrCodeComp> {
|
||||||
errorCorrectLevel: QrErrorCorrectLevel.M,
|
errorCorrectLevel: QrErrorCorrectLevel.M,
|
||||||
),
|
),
|
||||||
eyeStyle: QrEyeStyle(
|
eyeStyle: QrEyeStyle(
|
||||||
color: isDarkMode(context) ? Colors.black : Colors.white,
|
color: context.appColor(AppColor.qrForeground),
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
),
|
),
|
||||||
dataModuleStyle: QrDataModuleStyle(
|
dataModuleStyle: QrDataModuleStyle(
|
||||||
color: isDarkMode(context) ? Colors.black : Colors.white,
|
color: context.appColor(AppColor.qrForeground),
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
),
|
),
|
||||||
gapless: false,
|
gapless: false,
|
||||||
|
|
@ -173,7 +173,7 @@ class _ProfileQrCodeCompState extends State<ProfileQrCodeComp> {
|
||||||
embeddedImageStyle: QrEmbeddedImageStyle(
|
embeddedImageStyle: QrEmbeddedImageStyle(
|
||||||
size: const Size(60, 66),
|
size: const Size(60, 66),
|
||||||
embeddedImageShape: EmbeddedImageShape.square,
|
embeddedImageShape: EmbeddedImageShape.square,
|
||||||
shapeColor: context.color.primary,
|
shapeColor: context.appColor(AppColor.qrBackground),
|
||||||
safeArea: true,
|
safeArea: true,
|
||||||
),
|
),
|
||||||
size: widget.size,
|
size: widget.size,
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ class _SelectChatDeletionTimeListTitleState
|
||||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||||
),
|
),
|
||||||
// Provide a background color for the popup.
|
// Provide a background color for the popup.
|
||||||
color: CupertinoColors.systemBackground.resolveFrom(context),
|
color: context.color.surface,
|
||||||
// Use a SafeArea widget to avoid system overlaps.
|
// Use a SafeArea widget to avoid system overlaps.
|
||||||
child: SafeArea(top: false, child: child),
|
child: SafeArea(top: false, child: child),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,11 @@ class SelectableThumbnailComp extends StatelessWidget {
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? context.color.primary : Colors.transparent,
|
color: isSelected ? context.color.primary : Colors.transparent,
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black12,
|
color: context.color.shadow.withValues(alpha: 0.12),
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
offset: Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -49,20 +49,20 @@ class SelectableThumbnailComp extends StatelessWidget {
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(2),
|
padding: const EdgeInsets.all(2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? context.color.primary : Colors.black38,
|
color: isSelected
|
||||||
|
? context.color.primary
|
||||||
|
: context.color.scrim.withValues(alpha: 0.38),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
color: context.color.onSurface,
|
||||||
? Colors.white
|
|
||||||
: Colors.black,
|
|
||||||
width: 1.5,
|
width: 1.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: isSelected
|
child: isSelected
|
||||||
? const Icon(
|
? Icon(
|
||||||
Icons.check,
|
Icons.check,
|
||||||
size: 14,
|
size: 14,
|
||||||
color: Colors.white,
|
color: context.color.onPrimary,
|
||||||
)
|
)
|
||||||
: const SizedBox(width: 14, height: 14),
|
: const SizedBox(width: 14, height: 14),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
enum SnackbarLevel {
|
enum SnackbarLevel {
|
||||||
info,
|
info,
|
||||||
|
|
@ -14,23 +15,28 @@ void showSnackbar(
|
||||||
String message, {
|
String message, {
|
||||||
SnackbarLevel level = SnackbarLevel.error,
|
SnackbarLevel level = SnackbarLevel.error,
|
||||||
}) {
|
}) {
|
||||||
Color backgroundColor;
|
final (backgroundColor, foregroundColor, iconData) = switch (level) {
|
||||||
IconData iconData;
|
SnackbarLevel.info => (
|
||||||
|
context.appColor(AppColor.info),
|
||||||
switch (level) {
|
context.appColor(AppColor.onInfo),
|
||||||
case SnackbarLevel.info:
|
Icons.info_outline,
|
||||||
backgroundColor = Colors.blue.shade700;
|
),
|
||||||
iconData = Icons.info_outline;
|
SnackbarLevel.success => (
|
||||||
case SnackbarLevel.success:
|
context.appColor(AppColor.success),
|
||||||
backgroundColor = Colors.green.shade700;
|
context.appColor(AppColor.onSuccess),
|
||||||
iconData = Icons.check_circle_outline;
|
Icons.check_circle_outline,
|
||||||
case SnackbarLevel.warning:
|
),
|
||||||
backgroundColor = Colors.orange.shade800;
|
SnackbarLevel.warning => (
|
||||||
iconData = Icons.warning_amber_rounded;
|
context.appColor(AppColor.warning),
|
||||||
case SnackbarLevel.error:
|
context.appColor(AppColor.onWarning),
|
||||||
backgroundColor = Colors.red.shade700;
|
Icons.warning_amber_rounded,
|
||||||
iconData = Icons.error_outline;
|
),
|
||||||
}
|
SnackbarLevel.error => (
|
||||||
|
context.color.error,
|
||||||
|
context.color.onError,
|
||||||
|
Icons.error_outline,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
AnimationController? localAnimationController;
|
AnimationController? localAnimationController;
|
||||||
|
|
||||||
|
|
@ -44,7 +50,8 @@ void showSnackbar(
|
||||||
child: _SnackbarWidget(
|
child: _SnackbarWidget(
|
||||||
message: message,
|
message: message,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
icon: Icon(iconData, color: Colors.white, size: 28),
|
foregroundColor: foregroundColor,
|
||||||
|
icon: Icon(iconData, color: foregroundColor, size: 28),
|
||||||
onCloseClick: () {
|
onCloseClick: () {
|
||||||
localAnimationController?.reverse();
|
localAnimationController?.reverse();
|
||||||
},
|
},
|
||||||
|
|
@ -101,11 +108,13 @@ class _SnackbarWidget extends StatelessWidget {
|
||||||
const _SnackbarWidget({
|
const _SnackbarWidget({
|
||||||
required this.message,
|
required this.message,
|
||||||
required this.backgroundColor,
|
required this.backgroundColor,
|
||||||
|
required this.foregroundColor,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.onCloseClick,
|
required this.onCloseClick,
|
||||||
});
|
});
|
||||||
final String message;
|
final String message;
|
||||||
final Color backgroundColor;
|
final Color backgroundColor;
|
||||||
|
final Color foregroundColor;
|
||||||
final Icon icon;
|
final Icon icon;
|
||||||
final VoidCallback onCloseClick;
|
final VoidCallback onCloseClick;
|
||||||
|
|
||||||
|
|
@ -118,9 +127,9 @@ class _SnackbarWidget extends StatelessWidget {
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black26,
|
color: Theme.of(context).colorScheme.shadow.withValues(alpha: 0.2),
|
||||||
spreadRadius: 1,
|
spreadRadius: 1,
|
||||||
blurRadius: 30,
|
blurRadius: 30,
|
||||||
),
|
),
|
||||||
|
|
@ -138,10 +147,10 @@ class _SnackbarWidget extends StatelessWidget {
|
||||||
child: Text(
|
child: Text(
|
||||||
message,
|
message,
|
||||||
style: theme.textTheme.bodyMedium?.merge(
|
style: theme.textTheme.bodyMedium?.merge(
|
||||||
const TextStyle(
|
TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: Colors.white,
|
color: foregroundColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
|
|
@ -151,9 +160,13 @@ class _SnackbarWidget extends StatelessWidget {
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: onCloseClick,
|
onTap: onCloseClick,
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
child: const Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Icon(Icons.close, color: Colors.white70, size: 20),
|
child: Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: foregroundColor.withValues(alpha: 0.75),
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/key_verification.dao.dart';
|
import 'package:twonly/src/database/daos/key_verification.dao.dart';
|
||||||
import 'package:twonly/src/database/tables/contacts.table.dart';
|
import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/visual/components/verification_badge_info.comp.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/elements/svg_icon.element.dart';
|
import 'package:twonly/src/visual/elements/svg_icon.element.dart';
|
||||||
|
|
||||||
class VerificationBadgeComp extends StatefulWidget {
|
class VerificationBadgeComp extends StatefulWidget {
|
||||||
|
|
@ -198,7 +198,7 @@ class _VerificationBadgeCompState extends State<VerificationBadgeComp> {
|
||||||
? SvgIcons.verifiedNumeric(_verifiedByTransferredTrustCount)
|
? SvgIcons.verifiedNumeric(_verifiedByTransferredTrustCount)
|
||||||
: SvgIcons.verifiedRed,
|
: SvgIcons.verifiedRed,
|
||||||
color: (_verifiedByTransferredTrustCount > 0 && !_isVerified)
|
color: (_verifiedByTransferredTrustCount > 0 && !_isVerified)
|
||||||
? colorVerificationBadgeYellow
|
? context.appColor(AppColor.verificationPending)
|
||||||
: null,
|
: null,
|
||||||
size: widget.size,
|
size: widget.size,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ import 'package:twonly/src/visual/components/profile_qr_code.comp.dart';
|
||||||
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
||||||
import 'package:twonly/src/visual/elements/svg_icon.element.dart';
|
import 'package:twonly/src/visual/elements/svg_icon.element.dart';
|
||||||
|
|
||||||
const colorVerificationBadgeYellow = Color.fromARGB(255, 0, 182, 238);
|
|
||||||
|
|
||||||
class VerificationBadgeInfo extends StatelessWidget {
|
class VerificationBadgeInfo extends StatelessWidget {
|
||||||
const VerificationBadgeInfo({
|
const VerificationBadgeInfo({
|
||||||
this.displayButtons = false,
|
this.displayButtons = false,
|
||||||
|
|
@ -121,19 +119,19 @@ class VerificationBadgeInfo extends StatelessWidget {
|
||||||
),
|
),
|
||||||
_buildItem(
|
_buildItem(
|
||||||
context,
|
context,
|
||||||
icon: const SvgIcon(
|
icon: SvgIcon(
|
||||||
assetPath: SvgIcons.verifiedGreen,
|
assetPath: SvgIcons.verifiedGreen,
|
||||||
size: 40,
|
size: 40,
|
||||||
color: colorVerificationBadgeYellow,
|
color: context.appColor(AppColor.verificationPending),
|
||||||
),
|
),
|
||||||
description: context.lang.verificationBadgeYellowDesc,
|
description: context.lang.verificationBadgeYellowDesc,
|
||||||
boldTextColor: colorVerificationBadgeYellow,
|
boldTextColor: context.appColor(AppColor.verificationPending),
|
||||||
),
|
),
|
||||||
_buildItem(
|
_buildItem(
|
||||||
context,
|
context,
|
||||||
icon: const SvgIcon(assetPath: SvgIcons.verifiedRed, size: 40),
|
icon: const SvgIcon(assetPath: SvgIcons.verifiedRed, size: 40),
|
||||||
description: context.lang.verificationBadgeRedDesc,
|
description: context.lang.verificationBadgeRedDesc,
|
||||||
boldTextColor: const Color(0xffff0000),
|
boldTextColor: context.color.error,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'dart:math' as math;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:vector_graphics/vector_graphics.dart';
|
import 'package:vector_graphics/vector_graphics.dart';
|
||||||
|
|
||||||
/// Animated chain-link logo for the "verification success" moment.
|
/// Animated chain-link logo for the "verification success" moment.
|
||||||
|
|
@ -85,9 +85,6 @@ class VerificationSuccessAnimationState
|
||||||
static const _svg2 =
|
static const _svg2 =
|
||||||
'<svg viewBox="0 0 640 640"><path d="$_path2" fill="white"/></svg>';
|
'<svg viewBox="0 0 640 640"><path d="$_path2" fill="white"/></svg>';
|
||||||
|
|
||||||
static const _grey = Color(0xFF8E9AAF);
|
|
||||||
static const Color _green = defaultPrimaryColor;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
@ -196,6 +193,7 @@ class VerificationSuccessAnimationState
|
||||||
animation: Listenable.merge([_ctrl, _idleCtrl]),
|
animation: Listenable.merge([_ctrl, _idleCtrl]),
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
final scale = widget.size / 640;
|
final scale = widget.size / 640;
|
||||||
|
final successColor = context.color.primary;
|
||||||
final t = _flyIn.value; // 0 → 1 (fly-in progress)
|
final t = _flyIn.value; // 0 → 1 (fly-in progress)
|
||||||
final sep = 1.0 - t; // 1 → 0 (separation)
|
final sep = 1.0 - t; // 1 → 0 (separation)
|
||||||
|
|
||||||
|
|
@ -209,7 +207,11 @@ class VerificationSuccessAnimationState
|
||||||
final rotation = sep * 0.7;
|
final rotation = sep * 0.7;
|
||||||
|
|
||||||
// Interpolated colour.
|
// Interpolated colour.
|
||||||
final color = Color.lerp(_grey, _green, _colorFade.value)!;
|
final color = Color.lerp(
|
||||||
|
context.appColor(AppColor.verificationIdle),
|
||||||
|
successColor,
|
||||||
|
_colorFade.value,
|
||||||
|
)!;
|
||||||
|
|
||||||
// Pulse: 1 → 1.08 → 1 (smooth sine bump).
|
// Pulse: 1 → 1.08 → 1 (smooth sine bump).
|
||||||
final pulseScale = 1.0 + math.sin(_pulse.value * math.pi) * 0.08;
|
final pulseScale = 1.0 + math.sin(_pulse.value * math.pi) * 0.08;
|
||||||
|
|
@ -232,7 +234,9 @@ class VerificationSuccessAnimationState
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: _green.withValues(alpha: 0.3 * _glow.value),
|
color: successColor.withValues(
|
||||||
|
alpha: 0.3 * _glow.value,
|
||||||
|
),
|
||||||
blurRadius: widget.size * 0.45 * _glow.value,
|
blurRadius: widget.size * 0.45 * _glow.value,
|
||||||
spreadRadius: widget.size * 0.08 * _glow.value,
|
spreadRadius: widget.size * 0.08 * _glow.value,
|
||||||
),
|
),
|
||||||
|
|
@ -247,7 +251,7 @@ class VerificationSuccessAnimationState
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
painter: _RipplePainter(
|
painter: _RipplePainter(
|
||||||
progress: _ripple.value,
|
progress: _ripple.value,
|
||||||
color: _green,
|
color: successColor,
|
||||||
maxRadius: widget.size * 0.45,
|
maxRadius: widget.size * 0.45,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -323,7 +327,7 @@ class VerificationSuccessAnimationState
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
painter: _SparklePainter(
|
painter: _SparklePainter(
|
||||||
progress: _sparkle.value,
|
progress: _sparkle.value,
|
||||||
color: _green,
|
color: successColor,
|
||||||
maxRadius: widget.size * 0.4,
|
maxRadius: widget.size * 0.4,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,10 @@ InputDecoration inputTextMessageDeco(BuildContext context, String hintText) {
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
borderSide: const BorderSide(color: Colors.grey, width: 2),
|
borderSide: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,9 +90,9 @@ class _BetterTextState extends State<BetterText> {
|
||||||
spans.add(
|
spans.add(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: url,
|
text: url,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
decorationColor: Colors.white,
|
decorationColor: widget.textColor,
|
||||||
),
|
),
|
||||||
recognizer: recognizer,
|
recognizer: recognizer,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -68,9 +68,9 @@ class ContactChip extends StatelessWidget {
|
||||||
],
|
],
|
||||||
if (onTap != null && contact != null) ...[
|
if (onTap != null && contact != null) ...[
|
||||||
const SizedBox(width: 15),
|
const SizedBox(width: 15),
|
||||||
const FaIcon(
|
FaIcon(
|
||||||
FontAwesomeIcons.xmark,
|
FontAwesomeIcons.xmark,
|
||||||
color: Colors.grey,
|
color: context.color.onSurfaceVariant,
|
||||||
size: 12,
|
size: 12,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -37,20 +37,16 @@ class _MyButtonState extends State<MyButton> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isEnabled = widget.onPressed != null || widget.onLongPress != null;
|
final isEnabled = widget.onPressed != null || widget.onLongPress != null;
|
||||||
final isDark = isDarkMode(context);
|
final colors = context.color;
|
||||||
final disabledBgColor = isDark
|
final disabledBgColor = colors.surfaceContainerHighest;
|
||||||
? const Color(0xFF353535)
|
final disabledFgColor = colors.onSurface.withValues(alpha: 0.38);
|
||||||
: const Color(0xFFE0E0E0);
|
|
||||||
final disabledFgColor = isDark
|
|
||||||
? const Color(0xFF757575)
|
|
||||||
: const Color(0xFF9E9E9E);
|
|
||||||
|
|
||||||
late final ButtonStyle buttonStyle;
|
late final ButtonStyle buttonStyle;
|
||||||
switch (widget.variant) {
|
switch (widget.variant) {
|
||||||
case MyButtonVariant.primary:
|
case MyButtonVariant.primary:
|
||||||
buttonStyle = FilledButton.styleFrom(
|
buttonStyle = FilledButton.styleFrom(
|
||||||
backgroundColor: context.color.primary,
|
backgroundColor: context.color.primary,
|
||||||
foregroundColor: Colors.black87,
|
foregroundColor: colors.onPrimary,
|
||||||
disabledBackgroundColor: disabledBgColor,
|
disabledBackgroundColor: disabledBgColor,
|
||||||
disabledForegroundColor: disabledFgColor,
|
disabledForegroundColor: disabledFgColor,
|
||||||
minimumSize: const Size.fromHeight(60),
|
minimumSize: const Size.fromHeight(60),
|
||||||
|
|
@ -65,8 +61,8 @@ class _MyButtonState extends State<MyButton> {
|
||||||
);
|
);
|
||||||
case MyButtonVariant.secondary:
|
case MyButtonVariant.secondary:
|
||||||
buttonStyle = FilledButton.styleFrom(
|
buttonStyle = FilledButton.styleFrom(
|
||||||
backgroundColor: isDark ? Colors.grey[800] : Colors.grey[200],
|
backgroundColor: colors.surfaceContainerHigh,
|
||||||
foregroundColor: isDark ? Colors.white : Colors.black87,
|
foregroundColor: colors.onSurface,
|
||||||
disabledBackgroundColor: disabledBgColor,
|
disabledBackgroundColor: disabledBgColor,
|
||||||
disabledForegroundColor: disabledFgColor,
|
disabledForegroundColor: disabledFgColor,
|
||||||
minimumSize: const Size.fromHeight(60),
|
minimumSize: const Size.fromHeight(60),
|
||||||
|
|
@ -82,9 +78,7 @@ class _MyButtonState extends State<MyButton> {
|
||||||
case MyButtonVariant.text:
|
case MyButtonVariant.text:
|
||||||
buttonStyle = TextButton.styleFrom(
|
buttonStyle = TextButton.styleFrom(
|
||||||
minimumSize: const Size(0, 50),
|
minimumSize: const Size(0, 50),
|
||||||
foregroundColor: isDark
|
foregroundColor: colors.onSurfaceVariant,
|
||||||
? Colors.white.withValues(alpha: 0.7)
|
|
||||||
: Colors.black.withValues(alpha: 0.7),
|
|
||||||
disabledForegroundColor: disabledFgColor,
|
disabledForegroundColor: disabledFgColor,
|
||||||
textStyle: const TextStyle(
|
textStyle: const TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
|
|
@ -97,7 +91,7 @@ class _MyButtonState extends State<MyButton> {
|
||||||
case MyButtonVariant.primaryMiddle:
|
case MyButtonVariant.primaryMiddle:
|
||||||
buttonStyle = FilledButton.styleFrom(
|
buttonStyle = FilledButton.styleFrom(
|
||||||
backgroundColor: context.color.primary,
|
backgroundColor: context.color.primary,
|
||||||
foregroundColor: Colors.black87,
|
foregroundColor: colors.onPrimary,
|
||||||
disabledBackgroundColor: disabledBgColor,
|
disabledBackgroundColor: disabledBgColor,
|
||||||
disabledForegroundColor: disabledFgColor,
|
disabledForegroundColor: disabledFgColor,
|
||||||
minimumSize: const Size(0, 48),
|
minimumSize: const Size(0, 48),
|
||||||
|
|
@ -116,7 +110,7 @@ class _MyButtonState extends State<MyButton> {
|
||||||
case MyButtonVariant.primaryDense:
|
case MyButtonVariant.primaryDense:
|
||||||
buttonStyle = FilledButton.styleFrom(
|
buttonStyle = FilledButton.styleFrom(
|
||||||
backgroundColor: context.color.primary,
|
backgroundColor: context.color.primary,
|
||||||
foregroundColor: Colors.black87,
|
foregroundColor: colors.onPrimary,
|
||||||
disabledBackgroundColor: disabledBgColor,
|
disabledBackgroundColor: disabledBgColor,
|
||||||
disabledForegroundColor: disabledFgColor,
|
disabledForegroundColor: disabledFgColor,
|
||||||
minimumSize: const Size(0, 40),
|
minimumSize: const Size(0, 40),
|
||||||
|
|
@ -134,8 +128,8 @@ class _MyButtonState extends State<MyButton> {
|
||||||
);
|
);
|
||||||
case MyButtonVariant.secondaryDense:
|
case MyButtonVariant.secondaryDense:
|
||||||
buttonStyle = FilledButton.styleFrom(
|
buttonStyle = FilledButton.styleFrom(
|
||||||
backgroundColor: isDark ? Colors.grey[800] : Colors.grey[200],
|
backgroundColor: colors.surfaceContainerHigh,
|
||||||
foregroundColor: isDark ? Colors.white : Colors.black87,
|
foregroundColor: colors.onSurface,
|
||||||
disabledBackgroundColor: disabledBgColor,
|
disabledBackgroundColor: disabledBgColor,
|
||||||
disabledForegroundColor: disabledFgColor,
|
disabledForegroundColor: disabledFgColor,
|
||||||
minimumSize: const Size(0, 40),
|
minimumSize: const Size(0, 40),
|
||||||
|
|
@ -153,8 +147,8 @@ class _MyButtonState extends State<MyButton> {
|
||||||
);
|
);
|
||||||
case MyButtonVariant.secondaryMiddle:
|
case MyButtonVariant.secondaryMiddle:
|
||||||
buttonStyle = FilledButton.styleFrom(
|
buttonStyle = FilledButton.styleFrom(
|
||||||
backgroundColor: isDark ? Colors.grey[800] : Colors.grey[200],
|
backgroundColor: colors.surfaceContainerHigh,
|
||||||
foregroundColor: isDark ? Colors.white : Colors.black87,
|
foregroundColor: colors.onSurface,
|
||||||
disabledBackgroundColor: disabledBgColor,
|
disabledBackgroundColor: disabledBgColor,
|
||||||
disabledForegroundColor: disabledFgColor,
|
disabledForegroundColor: disabledFgColor,
|
||||||
minimumSize: const Size(0, 48),
|
minimumSize: const Size(0, 48),
|
||||||
|
|
@ -172,8 +166,8 @@ class _MyButtonState extends State<MyButton> {
|
||||||
);
|
);
|
||||||
case MyButtonVariant.secondaryTiny:
|
case MyButtonVariant.secondaryTiny:
|
||||||
buttonStyle = FilledButton.styleFrom(
|
buttonStyle = FilledButton.styleFrom(
|
||||||
backgroundColor: isDark ? Colors.grey[800] : Colors.grey[200],
|
backgroundColor: colors.surfaceContainerHigh,
|
||||||
foregroundColor: isDark ? Colors.white : Colors.black87,
|
foregroundColor: colors.onSurface,
|
||||||
disabledBackgroundColor: disabledBgColor,
|
disabledBackgroundColor: disabledBgColor,
|
||||||
disabledForegroundColor: disabledFgColor,
|
disabledForegroundColor: disabledFgColor,
|
||||||
minimumSize: const Size(40, 23),
|
minimumSize: const Size(40, 23),
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,9 @@ class MyChip extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = isDarkMode(context);
|
final colors = context.color;
|
||||||
final background = selected
|
final background = selected ? colors.primary : colors.surfaceContainerHigh;
|
||||||
? context.color.primary
|
final foreground = selected ? colors.onPrimary : colors.onSurface;
|
||||||
: isDark
|
|
||||||
? Colors.grey[800]!
|
|
||||||
: Colors.grey[200]!;
|
|
||||||
final foreground = selected
|
|
||||||
? Colors.black87
|
|
||||||
: isDark
|
|
||||||
? Colors.white
|
|
||||||
: Colors.black87;
|
|
||||||
Widget chip = AnimatedContainer(
|
Widget chip = AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 150),
|
duration: const Duration(milliseconds: 150),
|
||||||
height: 36,
|
height: 36,
|
||||||
|
|
|
||||||
|
|
@ -29,23 +29,19 @@ class _MyIconButtonState extends State<MyIconButton> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isEnabled = widget.onPressed != null || widget.onLongPress != null;
|
final isEnabled = widget.onPressed != null || widget.onLongPress != null;
|
||||||
final isDark = isDarkMode(context);
|
final colors = context.color;
|
||||||
final disabledBgColor = isDark
|
final disabledBgColor = colors.surfaceContainerHighest;
|
||||||
? const Color(0xFF353535)
|
final disabledFgColor = colors.onSurface.withValues(alpha: 0.38);
|
||||||
: const Color(0xFFE0E0E0);
|
|
||||||
final disabledFgColor = isDark
|
|
||||||
? const Color(0xFF757575)
|
|
||||||
: const Color(0xFF9E9E9E);
|
|
||||||
|
|
||||||
late final Color bgColor;
|
late final Color bgColor;
|
||||||
late final Color fgColor;
|
late final Color fgColor;
|
||||||
|
|
||||||
if (widget.variant == MyIconButtonVariant.primary) {
|
if (widget.variant == MyIconButtonVariant.primary) {
|
||||||
bgColor = context.color.primary;
|
bgColor = context.color.primary;
|
||||||
fgColor = Colors.black87;
|
fgColor = colors.onPrimary;
|
||||||
} else {
|
} else {
|
||||||
bgColor = isDark ? Colors.grey[800]! : Colors.grey[200]!;
|
bgColor = colors.surfaceContainerHigh;
|
||||||
fgColor = isDark ? Colors.white : Colors.black87;
|
fgColor = colors.onSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
final childButton = FilledButton(
|
final childButton = FilledButton(
|
||||||
|
|
|
||||||
|
|
@ -45,23 +45,11 @@ class MyInput extends StatefulWidget {
|
||||||
class _MyInputState extends State<MyInput> {
|
class _MyInputState extends State<MyInput> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = isDarkMode(context);
|
final colors = context.color;
|
||||||
|
final inputFillColor = colors.surfaceContainerLow;
|
||||||
final inputFillColor = isDark
|
final inputBorderColor = colors.outlineVariant.withValues(alpha: 0.6);
|
||||||
? Colors.white.withValues(alpha: 0.08)
|
final inputHintColor = colors.onSurfaceVariant.withValues(alpha: 0.75);
|
||||||
: Colors.black.withValues(alpha: 0.05);
|
final prefixIconColor = colors.onSurfaceVariant;
|
||||||
|
|
||||||
final inputBorderColor = isDark
|
|
||||||
? Colors.white.withValues(alpha: 0.05)
|
|
||||||
: Colors.black.withValues(alpha: 0.05);
|
|
||||||
|
|
||||||
final inputHintColor = isDark
|
|
||||||
? Colors.white.withValues(alpha: 0.5)
|
|
||||||
: Colors.black.withValues(alpha: 0.5);
|
|
||||||
|
|
||||||
final prefixIconColor = isDark
|
|
||||||
? Colors.white.withValues(alpha: 0.6)
|
|
||||||
: Colors.black.withValues(alpha: 0.6);
|
|
||||||
|
|
||||||
return ReactiveTapFeedback(
|
return ReactiveTapFeedback(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
|
|
@ -109,7 +97,7 @@ class _MyInputState extends State<MyInput> {
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: widget.dense ? 16 : 18,
|
fontSize: widget.dense ? 16 : 18,
|
||||||
fontWeight: widget.fontWeight ?? FontWeight.w500,
|
fontWeight: widget.fontWeight ?? FontWeight.w500,
|
||||||
color: isDark ? Colors.white : Colors.black87,
|
color: colors.onSurface,
|
||||||
),
|
),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
isDense: widget.dense,
|
isDense: widget.dense,
|
||||||
|
|
@ -139,25 +127,25 @@ class _MyInputState extends State<MyInput> {
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(widget.dense ? 12 : 18),
|
borderRadius: BorderRadius.circular(widget.dense ? 12 : 18),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: isDark ? Colors.white : Colors.black87,
|
color: colors.onSurface,
|
||||||
width: 2,
|
width: 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
errorBorder: OutlineInputBorder(
|
errorBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(widget.dense ? 12 : 18),
|
borderRadius: BorderRadius.circular(widget.dense ? 12 : 18),
|
||||||
borderSide: const BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.redAccent,
|
color: colors.error,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
focusedErrorBorder: OutlineInputBorder(
|
focusedErrorBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(widget.dense ? 12 : 18),
|
borderRadius: BorderRadius.circular(widget.dense ? 12 : 18),
|
||||||
borderSide: const BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.redAccent,
|
color: colors.error,
|
||||||
width: 2,
|
width: 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
errorStyle: const TextStyle(
|
errorStyle: TextStyle(
|
||||||
color: Colors.redAccent,
|
color: colors.error,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
|
|
@ -173,7 +161,7 @@ class _MyInputState extends State<MyInput> {
|
||||||
suffixIcon: widget.suffixIcon != null
|
suffixIcon: widget.suffixIcon != null
|
||||||
? IconTheme(
|
? IconTheme(
|
||||||
data: IconThemeData(
|
data: IconThemeData(
|
||||||
color: isDark ? Colors.white : Colors.black87,
|
color: colors.onSurface,
|
||||||
),
|
),
|
||||||
child: widget.suffixIcon!,
|
child: widget.suffixIcon!,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -10,23 +10,23 @@ class ProBadge extends StatelessWidget {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.amber.shade700,
|
color: context.appColor(AppColor.premium),
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const FaIcon(
|
FaIcon(
|
||||||
FontAwesomeIcons.star,
|
FontAwesomeIcons.star,
|
||||||
size: 10,
|
size: 10,
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.onWarning),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
context.lang.backupCloudProBadge,
|
context.lang.backupCloudProBadge,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.onWarning),
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
class VideoPlayerHelper extends StatefulWidget {
|
class VideoPlayerHelper extends StatefulWidget {
|
||||||
|
|
@ -98,15 +99,15 @@ class _VideoPlayerHelperState extends State<VideoPlayerHelper>
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
decoration: const BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black54,
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
_isPaused
|
_isPaused
|
||||||
? Icons.pause_rounded
|
? Icons.pause_rounded
|
||||||
: Icons.play_arrow_rounded,
|
: Icons.play_arrow_rounded,
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
size: 36,
|
size: 36,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,97 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
|
||||||
import 'package:twonly/src/database/tables/messages.table.dart';
|
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
|
||||||
|
|
||||||
class DefaultColors {
|
/// Every Twonly-specific color, with its light and dark values side by side.
|
||||||
static const messageSelf = Color.fromARGB(255, 58, 136, 102);
|
///
|
||||||
static const messageOther = Color.fromARGB(233, 68, 137, 255);
|
/// To add a color, add exactly one entry here and use it with:
|
||||||
|
/// `context.appColor(AppColor.yourColor)`.
|
||||||
|
enum AppColor {
|
||||||
|
// Chat bubbles
|
||||||
|
chatBubbleSent(light: Color(0xFFE8EBE9), dark: Color(0xFF292C2A)),
|
||||||
|
chatBubbleReceived(light: Color(0xFFF3F4F3), dark: Color(0xFF1D201E)),
|
||||||
|
onChatBubbleSent(light: Color(0xFF191C1A), dark: Color(0xFFF0F2F0)),
|
||||||
|
onChatBubbleReceived(light: Color(0xFF191C1A), dark: Color(0xFFE2E4E1)),
|
||||||
|
chatBubbleDeleted(light: Color(0xFFE1E4E1), dark: Color(0xFF101311)),
|
||||||
|
onChatBubbleDeleted(light: Color(0xFF626863), dark: Color(0xFF8A918B)),
|
||||||
|
chatMessageMeta(light: Color(0xFF69706A), dark: Color(0xFFAAB2AC)),
|
||||||
|
reactionBackground(light: Color(0xFFE1E5E2), dark: Color(0xFF343936)),
|
||||||
|
onReaction(light: Color(0xFF191C1A), dark: Color(0xFFF0F2F0)),
|
||||||
|
|
||||||
|
// Feedback and status
|
||||||
|
success(light: Color(0xFF177245), dark: Color(0xFF65D99A)),
|
||||||
|
onSuccess(light: Color(0xFFFFFFFF), dark: Color(0xFF00391F)),
|
||||||
|
successContainer(light: Color(0xFFC0F0D2), dark: Color(0xFF00522F)),
|
||||||
|
onSuccessContainer(light: Color(0xFF002112), dark: Color(0xFF83F7B5)),
|
||||||
|
warning(light: Color(0xFF8A5700), dark: Color(0xFFFFB95F)),
|
||||||
|
onWarning(light: Color(0xFFFFFFFF), dark: Color(0xFF492900)),
|
||||||
|
warningContainer(light: Color(0xFFFFDDB4), dark: Color(0xFF673D00)),
|
||||||
|
onWarningContainer(light: Color(0xFF2C1700), dark: Color(0xFFFFDDB4)),
|
||||||
|
info(light: Color(0xFF35618D), dark: Color(0xFF9FCBFF)),
|
||||||
|
onInfo(light: Color(0xFFFFFFFF), dark: Color(0xFF003257)),
|
||||||
|
infoContainer(light: Color(0xFFD1E4FF), dark: Color(0xFF164A70)),
|
||||||
|
onInfoContainer(light: Color(0xFF001D35), dark: Color(0xFFD1E4FF)),
|
||||||
|
premium(light: Color(0xFF8A6500), dark: Color(0xFFFFC94C)),
|
||||||
|
verificationPending(light: Color(0xFF007FA3), dark: Color(0xFF5DD5F5)),
|
||||||
|
verificationIdle(light: Color(0xFF7A8497), dark: Color(0xFFAEB8CC)),
|
||||||
|
|
||||||
|
// QR codes
|
||||||
|
qrForeground(light: Color(0xFF000000), dark: Color(0xFF000000)),
|
||||||
|
qrBackground(light: Color(0xFFFFFFFF), dark: Color(0xFFFFFFFF)),
|
||||||
|
|
||||||
|
// Controls displayed over photos and videos
|
||||||
|
mediaForeground(light: Color(0xFFFFFFFF), dark: Color(0xFFFFFFFF)),
|
||||||
|
mediaForegroundMuted(light: Color(0xB3FFFFFF), dark: Color(0xB3FFFFFF)),
|
||||||
|
mediaBackground(light: Color(0xFF000000), dark: Color(0xFF000000)),
|
||||||
|
mediaScrim(light: Color(0x8A000000), dark: Color(0x8A000000)),
|
||||||
|
recording(light: Color(0xFFD32F2F), dark: Color(0xFFFF5252)),
|
||||||
|
scanHighlight(light: Color(0xFF76FF03), dark: Color(0xFF76FF03)),
|
||||||
|
|
||||||
|
// Message-kind indicators
|
||||||
|
messageText(light: Color(0xFF35618D), dark: Color(0xFF9FCBFF)),
|
||||||
|
messageRestore(light: Color(0xFFB85F00), dark: Color(0xFFFFB95F)),
|
||||||
|
messageWidget(light: Color(0xFF79528D), dark: Color(0xFFD7B9E8)),
|
||||||
|
messageVideo(light: Color(0xFF9C3A8D), dark: Color(0xFFFFA9E5)),
|
||||||
|
messageAudio(light: Color(0xFFB85F00), dark: Color(0xFFFFB68A)),
|
||||||
|
messageImage(light: Color(0xFFBA1A1A), dark: Color(0xFFFFB4AB));
|
||||||
|
|
||||||
|
const AppColor({required this.light, required this.dark});
|
||||||
|
|
||||||
|
final Color light;
|
||||||
|
final Color dark;
|
||||||
|
|
||||||
|
Color resolve(Brightness brightness) {
|
||||||
|
return brightness == Brightness.dark ? dark : light;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Color getMessageColorFromType(
|
/// Resolves [AppColor] tokens for one theme and keeps theme transitions smooth.
|
||||||
Message message,
|
@immutable
|
||||||
MediaFile? mediaFile,
|
class AppColors extends ThemeExtension<AppColors> {
|
||||||
BuildContext context,
|
const AppColors.light()
|
||||||
) {
|
: _brightness = Brightness.light,
|
||||||
Color color;
|
_interpolatedColors = null;
|
||||||
|
|
||||||
if (message.type == MessageType.restoreFlameCounter.name) {
|
const AppColors.dark()
|
||||||
color = Colors.orange;
|
: _brightness = Brightness.dark,
|
||||||
} else if (message.type == MessageType.text.name) {
|
_interpolatedColors = null;
|
||||||
color = Colors.blueAccent;
|
|
||||||
} else if (message.isWidgetMedia) {
|
const AppColors._interpolated(this._interpolatedColors) : _brightness = null;
|
||||||
color = const Color.fromARGB(255, 155, 89, 182);
|
|
||||||
} else if (mediaFile != null) {
|
final Brightness? _brightness;
|
||||||
if (mediaFile.requiresAuthentication) {
|
final Map<AppColor, Color>? _interpolatedColors;
|
||||||
color = context.color.primary;
|
|
||||||
} else {
|
Color operator [](AppColor color) {
|
||||||
if (mediaFile.type == MediaType.video) {
|
return _interpolatedColors?[color] ?? color.resolve(_brightness!);
|
||||||
color = const Color.fromARGB(255, 243, 33, 208);
|
|
||||||
} else if (mediaFile.type == MediaType.audio) {
|
|
||||||
color = const Color.fromARGB(255, 252, 149, 85);
|
|
||||||
} else {
|
|
||||||
color = Colors.redAccent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AppColors copyWith() => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
AppColors lerp(covariant AppColors? other, double t) {
|
||||||
|
if (other == null) return this;
|
||||||
|
return AppColors._interpolated({
|
||||||
|
for (final color in AppColor.values)
|
||||||
|
color: Color.lerp(this[color], other[color], t)!,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return (isDarkMode(context)) ? Colors.white : Colors.black;
|
|
||||||
}
|
|
||||||
return color;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,55 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/visual/themes/colors.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
import 'package:twonly/src/visual/themes/light.dart';
|
||||||
|
|
||||||
ThemeData getDarkTheme([Color primary = defaultPrimaryColor]) {
|
Color _onPrimary(Color primary) {
|
||||||
final base = ThemeData.dark().copyWith(
|
return ThemeData.estimateBrightnessForColor(primary) == Brightness.dark
|
||||||
colorScheme: ColorScheme.fromSeed(
|
? const Color(0xFFFFFFFF)
|
||||||
|
: const Color(0xFF082117);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorScheme _darkColorScheme(Color primary) {
|
||||||
|
return ColorScheme.fromSeed(
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
seedColor: primary,
|
seedColor: primary,
|
||||||
primary: primary,
|
primary: primary,
|
||||||
surface: const Color.fromARGB(255, 20, 18, 23),
|
).copyWith(
|
||||||
surfaceContainer: const Color.fromARGB(255, 45, 41, 54),
|
onPrimary: _onPrimary(primary),
|
||||||
surfaceContainerLow: const Color.fromARGB(255, 38, 34, 45),
|
surface: const Color(0xFF111412),
|
||||||
surfaceContainerHigh: const Color.fromARGB(255, 52, 48, 62),
|
onSurface: const Color(0xFFE2E4E1),
|
||||||
),
|
onSurfaceVariant: const Color(0xFFBEC9C0),
|
||||||
|
surfaceDim: const Color(0xFF111412),
|
||||||
|
surfaceBright: const Color(0xFF373A37),
|
||||||
|
surfaceContainerLowest: const Color(0xFF0B0E0C),
|
||||||
|
surfaceContainerLow: const Color(0xFF171A18),
|
||||||
|
surfaceContainer: const Color(0xFF1D211E),
|
||||||
|
surfaceContainerHigh: const Color(0xFF282C29),
|
||||||
|
surfaceContainerHighest: const Color(0xFF333734),
|
||||||
|
outline: const Color(0xFF89938B),
|
||||||
|
outlineVariant: const Color(0xFF404842),
|
||||||
|
inverseSurface: const Color(0xFFE2E4E1),
|
||||||
|
onInverseSurface: const Color(0xFF2E312F),
|
||||||
|
shadow: const Color(0xFF000000),
|
||||||
|
scrim: const Color(0xFF000000),
|
||||||
|
error: const Color(0xFFFFB4AB),
|
||||||
|
onError: const Color(0xFF690005),
|
||||||
|
errorContainer: const Color(0xFF93000A),
|
||||||
|
onErrorContainer: const Color(0xFFFFDAD6),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThemeData getDarkTheme([Color primary = defaultPrimaryColor]) {
|
||||||
|
final colorScheme = _darkColorScheme(primary);
|
||||||
|
final base = ThemeData(
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
colorScheme: colorScheme,
|
||||||
|
scaffoldBackgroundColor: colorScheme.surface,
|
||||||
inputDecorationTheme: const InputDecorationTheme(
|
inputDecorationTheme: const InputDecorationTheme(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
|
extensions: const [AppColors.dark()],
|
||||||
);
|
);
|
||||||
return base.copyWith(
|
return base.copyWith(
|
||||||
textTheme: base.textTheme.apply(
|
textTheme: base.textTheme.apply(
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,56 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/visual/themes/colors.dart';
|
||||||
|
|
||||||
const defaultPrimaryColor = Color(0xFF32be80);
|
const defaultPrimaryColor = Color(0xFF32BE80);
|
||||||
|
|
||||||
ThemeData getLightTheme([Color primary = defaultPrimaryColor]) {
|
Color _onPrimary(Color primary) {
|
||||||
final base = ThemeData(
|
return ThemeData.estimateBrightnessForColor(primary) == Brightness.dark
|
||||||
colorScheme: ColorScheme.fromSeed(
|
? const Color(0xFFFFFFFF)
|
||||||
|
: const Color(0xFF082117);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Material colors for the light theme. Twonly-specific light/dark pairs live
|
||||||
|
/// together in `colors.dart`.
|
||||||
|
ColorScheme _lightColorScheme(Color primary) {
|
||||||
|
return ColorScheme.fromSeed(
|
||||||
seedColor: primary,
|
seedColor: primary,
|
||||||
primary: primary,
|
primary: primary,
|
||||||
surface: Colors.white,
|
).copyWith(
|
||||||
),
|
onPrimary: _onPrimary(primary),
|
||||||
scaffoldBackgroundColor: Colors.white,
|
surface: const Color(0xFFFFFFFF),
|
||||||
|
onSurface: const Color(0xFF191C1A),
|
||||||
|
onSurfaceVariant: const Color(0xFF414943),
|
||||||
|
surfaceDim: const Color(0xFFD9DCD9),
|
||||||
|
surfaceBright: const Color(0xFFFFFFFF),
|
||||||
|
surfaceContainerLowest: const Color(0xFFFFFFFF),
|
||||||
|
surfaceContainerLow: const Color(0xFFF5F7F5),
|
||||||
|
surfaceContainer: const Color(0xFFEFF2EF),
|
||||||
|
surfaceContainerHigh: const Color(0xFFE9ECE9),
|
||||||
|
surfaceContainerHighest: const Color(0xFFE3E7E3),
|
||||||
|
outline: const Color(0xFF717971),
|
||||||
|
outlineVariant: const Color(0xFFC1C9C1),
|
||||||
|
inverseSurface: const Color(0xFF2E312F),
|
||||||
|
onInverseSurface: const Color(0xFFF0F2F0),
|
||||||
|
shadow: const Color(0xFF000000),
|
||||||
|
scrim: const Color(0xFF000000),
|
||||||
|
error: const Color(0xFFBA1A1A),
|
||||||
|
onError: const Color(0xFFFFFFFF),
|
||||||
|
errorContainer: const Color(0xFFFFDAD6),
|
||||||
|
onErrorContainer: const Color(0xFF410002),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThemeData getLightTheme([Color primary = defaultPrimaryColor]) {
|
||||||
|
final colorScheme = _lightColorScheme(primary);
|
||||||
|
final base = ThemeData(
|
||||||
|
colorScheme: colorScheme,
|
||||||
|
scaffoldBackgroundColor: colorScheme.surface,
|
||||||
inputDecorationTheme: const InputDecorationTheme(
|
inputDecorationTheme: const InputDecorationTheme(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
|
extensions: const [AppColors.light()],
|
||||||
);
|
);
|
||||||
return base.copyWith(
|
return base.copyWith(
|
||||||
textTheme: base.textTheme.apply(
|
textTheme: base.textTheme.apply(
|
||||||
|
|
@ -28,20 +64,17 @@ final ThemeData lightTheme = getLightTheme();
|
||||||
|
|
||||||
final ButtonStyle primaryColorButtonStyle = FilledButton.styleFrom(
|
final ButtonStyle primaryColorButtonStyle = FilledButton.styleFrom(
|
||||||
backgroundColor: defaultPrimaryColor,
|
backgroundColor: defaultPrimaryColor,
|
||||||
foregroundColor: Colors.black87,
|
foregroundColor: const Color(0xFF082117),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ButtonStyle secondaryGreyButtonStyle(BuildContext context) {
|
ButtonStyle secondaryGreyButtonStyle(BuildContext context) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
return FilledButton.styleFrom(
|
return FilledButton.styleFrom(
|
||||||
backgroundColor: isDarkMode(context) ? Colors.grey[800] : Colors.grey[200],
|
backgroundColor: colors.surfaceContainerHigh,
|
||||||
foregroundColor: isDarkMode(context) ? Colors.white : Colors.black87,
|
foregroundColor: colors.onSurface,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/helpers/media_view_sizing.helper.dart';
|
import 'package:twonly/src/visual/helpers/media_view_sizing.helper.dart';
|
||||||
import 'package:twonly/src/visual/helpers/screenshot.helper.dart';
|
import 'package:twonly/src/visual/helpers/screenshot.helper.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/main_camera_controller.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||||
|
|
@ -96,7 +97,9 @@ class MainCameraPreview extends StatelessWidget {
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: Colors.white.withAlpha(150),
|
color: context.appColor(
|
||||||
|
AppColor.mediaForegroundMuted,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/zoom_tutorial_overlay.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/zoom_tutorial_overlay.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/face_filters.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/face_filters.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/main_camera_controller.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||||
|
|
@ -66,13 +67,13 @@ class CameraBottomControls extends StatelessWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (!isVideoRecording)
|
if (!isVideoRecording)
|
||||||
_buildSideButtonLeft()
|
_buildSideButtonLeft(context)
|
||||||
else
|
else
|
||||||
_buildLockOrStopButton(),
|
_buildLockOrStopButton(context),
|
||||||
_buildShutterButton(),
|
_buildShutterButton(context),
|
||||||
if (!isVideoRecording)
|
if (!isVideoRecording)
|
||||||
if (isFront)
|
if (isFront)
|
||||||
_buildSideButtonRight()
|
_buildSideButtonRight(context)
|
||||||
else
|
else
|
||||||
const SizedBox(width: 80)
|
const SizedBox(width: 80)
|
||||||
else
|
else
|
||||||
|
|
@ -85,7 +86,7 @@ class CameraBottomControls extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLockOrStopButton() {
|
Widget _buildLockOrStopButton(BuildContext context) {
|
||||||
if (videoRecordingLocked) {
|
if (videoRecordingLocked) {
|
||||||
// Show stop button
|
// Show stop button
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
|
|
@ -100,7 +101,7 @@ class CameraBottomControls extends StatelessWidget {
|
||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.red,
|
color: context.appColor(AppColor.recording),
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -113,7 +114,7 @@ class CameraBottomControls extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSideButtonLeft() {
|
Widget _buildSideButtonLeft(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onPressSideButtonLeft,
|
onTap: onPressSideButtonLeft,
|
||||||
child: Align(
|
child: Align(
|
||||||
|
|
@ -128,7 +129,7 @@ class CameraBottomControls extends StatelessWidget {
|
||||||
? FontAwesomeIcons.xmark
|
? FontAwesomeIcons.xmark
|
||||||
: FontAwesomeIcons.arrowLeft
|
: FontAwesomeIcons.arrowLeft
|
||||||
: FontAwesomeIcons.photoFilm,
|
: FontAwesomeIcons.photoFilm,
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
size: 25,
|
size: 25,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -137,7 +138,7 @@ class CameraBottomControls extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildShutterButton() {
|
Widget _buildShutterButton(BuildContext context) {
|
||||||
return StreamBuilder(
|
return StreamBuilder(
|
||||||
stream: userService.onUserUpdated,
|
stream: userService.onUserUpdated,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
|
@ -156,7 +157,9 @@ class CameraBottomControls extends StatelessWidget {
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
width: 7,
|
width: 7,
|
||||||
color: isVideoRecording ? Colors.red : Colors.white,
|
color: isVideoRecording
|
||||||
|
? context.appColor(AppColor.recording)
|
||||||
|
: context.appColor(AppColor.mediaForeground),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: mc.currentFilterType.preview,
|
child: mc.currentFilterType.preview,
|
||||||
|
|
@ -168,7 +171,7 @@ class CameraBottomControls extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSideButtonRight() {
|
Widget _buildSideButtonRight(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onPressSideButtonRight,
|
onTap: onPressSideButtonRight,
|
||||||
child: Align(
|
child: Align(
|
||||||
|
|
@ -184,7 +187,7 @@ class CameraBottomControls extends StatelessWidget {
|
||||||
? FontAwesomeIcons.xmark
|
? FontAwesomeIcons.xmark
|
||||||
: FontAwesomeIcons.arrowRight
|
: FontAwesomeIcons.arrowRight
|
||||||
: FontAwesomeIcons.faceGrinTongueSquint,
|
: FontAwesomeIcons.faceGrinTongueSquint,
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
size: 25,
|
size: 25,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -249,10 +252,10 @@ class _AnimatedLockButtonState extends State<_AnimatedLockButton>
|
||||||
height: 50,
|
height: 50,
|
||||||
width: 80,
|
width: 80,
|
||||||
padding: const EdgeInsets.all(2),
|
padding: const EdgeInsets.all(2),
|
||||||
child: const Center(
|
child: Center(
|
||||||
child: FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.lock,
|
FontAwesomeIcons.lock,
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
size: 25,
|
size: 25,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ class CameraScannedOverlay extends StatelessWidget {
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.shareFromSquare,
|
FontAwesomeIcons.shareFromSquare,
|
||||||
color: isDarkMode(context) ? Colors.white : Colors.black,
|
color: context.color.onSurface,
|
||||||
size: 17,
|
size: 17,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
/// Full-screen white overlay used as a "flash" when taking selfies
|
/// Full-screen white overlay used as a "flash" when taking selfies
|
||||||
/// with the front camera and flash enabled.
|
/// with the front camera and flash enabled.
|
||||||
|
|
@ -11,7 +12,7 @@ class CameraSelfieFlash extends StatelessWidget {
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(22),
|
borderRadius: BorderRadius.circular(22),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -42,14 +42,14 @@ class CameraTopActions extends StatelessWidget {
|
||||||
: Icons.flash_off_rounded,
|
: Icons.flash_off_rounded,
|
||||||
tooltipText: context.lang.toggleFlashLight,
|
tooltipText: context.lang.toggleFlashLight,
|
||||||
color: selectedCameraDetails.isFlashOn
|
color: selectedCameraDetails.isFlashOn
|
||||||
? Colors.white
|
? context.appColor(AppColor.mediaForeground)
|
||||||
: Colors.white.withAlpha(160),
|
: context.appColor(AppColor.mediaForegroundMuted),
|
||||||
onPressed: onToggleFlash,
|
onPressed: onToggleFlash,
|
||||||
),
|
),
|
||||||
if (!hasAudioPermission)
|
if (!hasAudioPermission)
|
||||||
ActionButton(
|
ActionButton(
|
||||||
Icons.mic_off_rounded,
|
Icons.mic_off_rounded,
|
||||||
color: Colors.white.withAlpha(160),
|
color: context.appColor(AppColor.mediaForegroundMuted),
|
||||||
tooltipText: 'Allow microphone access for video recording.',
|
tooltipText: 'Allow microphone access for video recording.',
|
||||||
onPressed: onRequestMicrophone,
|
onPressed: onRequestMicrophone,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -105,13 +105,13 @@ class _ZoomTutorialOverlayState extends State<ZoomTutorialOverlay>
|
||||||
vertical: 6,
|
vertical: 6,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black,
|
color: context.appColor(AppColor.mediaBackground),
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.lang.dragToZoom,
|
context.lang.dragToZoom,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
letterSpacing: 0.3,
|
letterSpacing: 0.3,
|
||||||
|
|
@ -131,13 +131,15 @@ class _ZoomTutorialOverlayState extends State<ZoomTutorialOverlay>
|
||||||
height: 42,
|
height: 42,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: Colors.white.withValues(alpha: 0.5),
|
color: context.appColor(
|
||||||
|
AppColor.mediaForegroundMuted,
|
||||||
),
|
),
|
||||||
child: const Center(
|
),
|
||||||
|
child: Center(
|
||||||
child: FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.handPointer,
|
FontAwesomeIcons.handPointer,
|
||||||
size: 18,
|
size: 18,
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,9 @@ class _CameraPreviewControllerViewState
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
widget.mainController.scanHighlightColor = context.appColor(
|
||||||
|
AppColor.scanHighlight,
|
||||||
|
);
|
||||||
if (AppState.hasCameraPermissions) {
|
if (AppState.hasCameraPermissions) {
|
||||||
return CameraPreviewView(
|
return CameraPreviewView(
|
||||||
sendToGroup: widget.sendToGroup,
|
sendToGroup: widget.sendToGroup,
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ class MainCameraController {
|
||||||
bool _isBusy = false;
|
bool _isBusy = false;
|
||||||
bool _isBusyFaces = false;
|
bool _isBusyFaces = false;
|
||||||
CustomPaint? qrCodePain;
|
CustomPaint? qrCodePain;
|
||||||
|
Color? scanHighlightColor;
|
||||||
CustomPaint? facePaint;
|
CustomPaint? facePaint;
|
||||||
Offset? focusPointOffset;
|
Offset? focusPointOffset;
|
||||||
|
|
||||||
|
|
@ -529,12 +530,14 @@ class MainCameraController {
|
||||||
final controller = cameraController;
|
final controller = cameraController;
|
||||||
if (inputImage.metadata?.size != null &&
|
if (inputImage.metadata?.size != null &&
|
||||||
inputImage.metadata?.rotation != null &&
|
inputImage.metadata?.rotation != null &&
|
||||||
controller != null) {
|
controller != null &&
|
||||||
|
scanHighlightColor != null) {
|
||||||
final painter = BarcodeDetectorPainter(
|
final painter = BarcodeDetectorPainter(
|
||||||
barcodes,
|
barcodes,
|
||||||
inputImage.metadata!.size,
|
inputImage.metadata!.size,
|
||||||
inputImage.metadata!.rotation,
|
inputImage.metadata!.rotation,
|
||||||
controller.description.lensDirection,
|
controller.description.lensDirection,
|
||||||
|
scanHighlightColor!,
|
||||||
);
|
);
|
||||||
qrCodePain = CustomPaint(painter: painter);
|
qrCodePain = CustomPaint(painter: painter);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,19 +10,21 @@ class BarcodeDetectorPainter extends CustomPainter {
|
||||||
this.imageSize,
|
this.imageSize,
|
||||||
this.rotation,
|
this.rotation,
|
||||||
this.cameraLensDirection,
|
this.cameraLensDirection,
|
||||||
|
this.color,
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<Barcode> barcodes;
|
final List<Barcode> barcodes;
|
||||||
final Size imageSize;
|
final Size imageSize;
|
||||||
final InputImageRotation rotation;
|
final InputImageRotation rotation;
|
||||||
final CameraLensDirection cameraLensDirection;
|
final CameraLensDirection cameraLensDirection;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
final paint = Paint()
|
final paint = Paint()
|
||||||
..style = PaintingStyle.stroke
|
..style = PaintingStyle.stroke
|
||||||
..strokeWidth = 3.0
|
..strokeWidth = 3.0
|
||||||
..color = Colors.lightGreenAccent;
|
..color = color;
|
||||||
|
|
||||||
for (final barcode in barcodes) {
|
for (final barcode in barcodes) {
|
||||||
final cornerPoints = <Offset>[];
|
final cornerPoints = <Offset>[];
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
|
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
abstract class FaceFilterPainter extends CustomPainter {
|
abstract class FaceFilterPainter extends CustomPainter {
|
||||||
FaceFilterPainter(
|
FaceFilterPainter(
|
||||||
|
|
@ -34,7 +35,7 @@ class Preview extends StatelessWidget {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: Colors.grey.withValues(alpha: 0.2),
|
color: context.color.surfaceContainerHighest,
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: child,
|
child: child,
|
||||||
|
|
|
||||||
|
|
@ -89,12 +89,14 @@ class SaveToGalleryButtonState extends State<SaveToGalleryButton> {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (_imageSaving || widget.isLoading)
|
if (_imageSaving || widget.isLoading)
|
||||||
const SizedBox(
|
SizedBox(
|
||||||
width: 12,
|
width: 12,
|
||||||
height: 12,
|
height: 12,
|
||||||
child: CircularProgressIndicator.adaptive(
|
child: CircularProgressIndicator.adaptive(
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
valueColor: AlwaysStoppedAnimation(Colors.white),
|
valueColor: AlwaysStoppedAnimation(
|
||||||
|
context.appColor(AppColor.mediaForeground),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else if (_imageSaved)
|
else if (_imageSaved)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
class ShowTitleText extends StatelessWidget {
|
class ShowTitleText extends StatelessWidget {
|
||||||
const ShowTitleText({
|
const ShowTitleText({
|
||||||
|
|
@ -15,13 +16,13 @@ class ShowTitleText extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final textStyle = TextStyle(
|
final textStyle = TextStyle(
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: isLink ? 14 : 24,
|
fontSize: isLink ? 14 : 24,
|
||||||
decoration: TextDecoration.none,
|
decoration: TextDecoration.none,
|
||||||
shadows: const [
|
shadows: [
|
||||||
Shadow(
|
Shadow(
|
||||||
color: Color.fromARGB(122, 0, 0, 0),
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
blurRadius: 5,
|
blurRadius: 5,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
class VideoRecordingTimer extends StatelessWidget {
|
class VideoRecordingTimer extends StatelessWidget {
|
||||||
const VideoRecordingTimer({
|
const VideoRecordingTimer({
|
||||||
|
|
@ -38,19 +39,23 @@ class VideoRecordingTimer extends StatelessWidget {
|
||||||
maxRecordingTime.inMilliseconds)
|
maxRecordingTime.inMilliseconds)
|
||||||
.clamp(0.0, 1.0),
|
.clamp(0.0, 1.0),
|
||||||
strokeWidth: 4,
|
strokeWidth: 4,
|
||||||
valueColor: const AlwaysStoppedAnimation<Color>(Colors.red),
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
backgroundColor: Colors.grey[300],
|
context.appColor(AppColor.recording),
|
||||||
|
),
|
||||||
|
backgroundColor: context.appColor(
|
||||||
|
AppColor.mediaForegroundMuted,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
elapsed.inSeconds.toString(),
|
elapsed.inSeconds.toString(),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
shadows: [
|
shadows: [
|
||||||
Shadow(
|
Shadow(
|
||||||
color: Color.fromARGB(122, 0, 0, 0),
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
blurRadius: 5,
|
blurRadius: 5,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'dart:math';
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||||
|
|
||||||
String beautifulZoomScale(double scale) {
|
String beautifulZoomScale(double scale) {
|
||||||
|
|
@ -60,7 +61,7 @@ class CameraZoomButtons extends StatelessWidget {
|
||||||
|
|
||||||
final zoomButtonStyle = TextButton.styleFrom(
|
final zoomButtonStyle = TextButton.styleFrom(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: context.appColor(AppColor.mediaForeground),
|
||||||
minimumSize: const Size(40, 40),
|
minimumSize: const Size(40, 40),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
|
@ -90,7 +91,7 @@ class CameraZoomButtons extends StatelessWidget {
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(40),
|
borderRadius: BorderRadius.circular(40),
|
||||||
child: ColoredBox(
|
child: ColoredBox(
|
||||||
color: const Color.fromARGB(90, 0, 0, 0),
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -98,7 +99,9 @@ class CameraZoomButtons extends StatelessWidget {
|
||||||
TextButton(
|
TextButton(
|
||||||
style: zoomButtonStyle.copyWith(
|
style: zoomButtonStyle.copyWith(
|
||||||
foregroundColor: WidgetStateProperty.all(
|
foregroundColor: WidgetStateProperty.all(
|
||||||
isSmallerFocused ? Colors.yellow : Colors.white,
|
isSmallerFocused
|
||||||
|
? context.appColor(AppColor.warning)
|
||||||
|
: context.appColor(AppColor.mediaForeground),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
|
@ -121,7 +124,9 @@ class CameraZoomButtons extends StatelessWidget {
|
||||||
TextButton(
|
TextButton(
|
||||||
style: zoomButtonStyle.copyWith(
|
style: zoomButtonStyle.copyWith(
|
||||||
foregroundColor: WidgetStateProperty.all(
|
foregroundColor: WidgetStateProperty.all(
|
||||||
isMiddleFocused ? Colors.yellow : Colors.white,
|
isMiddleFocused
|
||||||
|
? context.appColor(AppColor.warning)
|
||||||
|
: context.appColor(AppColor.mediaForeground),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
|
@ -142,7 +147,9 @@ class CameraZoomButtons extends StatelessWidget {
|
||||||
TextButton(
|
TextButton(
|
||||||
style: zoomButtonStyle.copyWith(
|
style: zoomButtonStyle.copyWith(
|
||||||
foregroundColor: WidgetStateProperty.all(
|
foregroundColor: WidgetStateProperty.all(
|
||||||
(scaleFactor >= 2) ? Colors.yellow : Colors.white,
|
(scaleFactor >= 2)
|
||||||
|
? context.appColor(AppColor.warning)
|
||||||
|
: context.appColor(AppColor.mediaForeground),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ class QrCodeScannerViewState extends State<QrCodeScannerView> {
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black.withValues(alpha: 0.7),
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|
@ -96,8 +96,8 @@ class QrCodeScannerViewState extends State<QrCodeScannerView> {
|
||||||
getContactDisplayName(widget.contact!),
|
getContactDisplayName(widget.contact!),
|
||||||
)
|
)
|
||||||
: context.lang.qrScannerVerifyHint,
|
: context.lang.qrScannerVerifyHint,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
|
|
||||||
|
|
@ -429,13 +429,13 @@ class _ShareImageView extends State<ShareImageView> {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (!mediaStoreFutureReady || sendingImage)
|
if (!mediaStoreFutureReady || sendingImage)
|
||||||
const SizedBox(
|
SizedBox(
|
||||||
height: 14,
|
height: 14,
|
||||||
width: 14,
|
width: 14,
|
||||||
child: CircularProgressIndicator.adaptive(
|
child: CircularProgressIndicator.adaptive(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
valueColor: AlwaysStoppedAnimation(
|
valueColor: AlwaysStoppedAnimation(
|
||||||
Colors.black87,
|
context.color.onPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,10 @@ class BestFriendsSelector extends StatelessWidget {
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.outline.withAlpha(50),
|
color: Theme.of(context).colorScheme.outline.withAlpha(50),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
blurRadius: 10.9,
|
blurRadius: 10.9,
|
||||||
color: Color.fromRGBO(0, 0, 0, 0.1),
|
color: context.color.shadow.withValues(alpha: 0.1),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
|
@ -135,10 +135,10 @@ class UserCheckbox extends StatelessWidget {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.outline.withAlpha(50),
|
color: Theme.of(context).colorScheme.outline.withAlpha(50),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
blurRadius: 10.9,
|
blurRadius: 10.9,
|
||||||
color: Color.fromRGBO(0, 0, 0, 0.1),
|
color: context.color.shadow.withValues(alpha: 0.1),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ class _SelectShowTimeState extends State<SelectShowTime> {
|
||||||
height: 350,
|
height: 350,
|
||||||
padding: const EdgeInsets.only(top: 6),
|
padding: const EdgeInsets.only(top: 6),
|
||||||
margin: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
margin: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||||
color: CupertinoColors.systemBackground.resolveFrom(context),
|
color: Theme.of(context).colorScheme.surface,
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
@ -37,7 +37,7 @@ class _SelectShowTimeState extends State<SelectShowTime> {
|
||||||
margin: const EdgeInsets.all(10),
|
margin: const EdgeInsets.all(10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(32),
|
borderRadius: BorderRadius.circular(32),
|
||||||
color: Colors.grey,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
height: 3,
|
height: 3,
|
||||||
width: 60,
|
width: 60,
|
||||||
|
|
|
||||||
|
|
@ -545,9 +545,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
await askToCloseThenClose();
|
await askToCloseThenClose();
|
||||||
},
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: widget.sharedFromGallery
|
backgroundColor: widget.sharedFromGallery ? null : Colors.transparent,
|
||||||
? null
|
|
||||||
: Colors.white.withAlpha(0),
|
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
body: Stack(
|
body: Stack(
|
||||||
key: _editorStackKey,
|
key: _editorStackKey,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
class ActionButton extends StatelessWidget {
|
class ActionButton extends StatelessWidget {
|
||||||
const ActionButton(
|
const ActionButton(
|
||||||
|
|
@ -27,11 +28,11 @@ class ActionButton extends StatelessWidget {
|
||||||
icon as FaIconData?,
|
icon as FaIconData?,
|
||||||
size: 25,
|
size: 25,
|
||||||
color: disable
|
color: disable
|
||||||
? const Color.fromARGB(154, 255, 255, 255)
|
? context.appColor(AppColor.mediaForegroundMuted)
|
||||||
: color ?? Colors.white,
|
: color ?? context.appColor(AppColor.mediaForeground),
|
||||||
shadows: const [
|
shadows: [
|
||||||
Shadow(
|
Shadow(
|
||||||
color: Color.fromARGB(122, 0, 0, 0),
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
blurRadius: 5,
|
blurRadius: 5,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -40,11 +41,11 @@ class ActionButton extends StatelessWidget {
|
||||||
icon as IconData?,
|
icon as IconData?,
|
||||||
size: 30,
|
size: 30,
|
||||||
color: disable
|
color: disable
|
||||||
? const Color.fromARGB(154, 255, 255, 255)
|
? context.appColor(AppColor.mediaForegroundMuted)
|
||||||
: color ?? Colors.white,
|
: color ?? context.appColor(AppColor.mediaForeground),
|
||||||
shadows: const [
|
shadows: [
|
||||||
Shadow(
|
Shadow(
|
||||||
color: Color.fromARGB(122, 0, 0, 0),
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
blurRadius: 5,
|
blurRadius: 5,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ Future<void> showDisplayTimePicker(
|
||||||
|
|
||||||
await showModalBottomSheet<void>(
|
await showModalBottomSheet<void>(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
builder: (sheetContext) {
|
builder: (sheetContext) {
|
||||||
return SelectShowTime(
|
return SelectShowTime(
|
||||||
initialItem: initialItem,
|
initialItem: initialItem,
|
||||||
|
|
|
||||||
|
|
@ -65,12 +65,14 @@ class EditorBottomBar extends StatelessWidget {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (isSending)
|
if (isSending)
|
||||||
const SizedBox(
|
SizedBox(
|
||||||
height: 12,
|
height: 12,
|
||||||
width: 12,
|
width: 12,
|
||||||
child: CircularProgressIndicator.adaptive(
|
child: CircularProgressIndicator.adaptive(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
valueColor: AlwaysStoppedAnimation(Colors.black87),
|
valueColor: AlwaysStoppedAnimation(
|
||||||
|
context.color.onPrimary,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ class EditorSideToolbar extends StatelessWidget {
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final layer = await showModalBottomSheet<Layer>(
|
final layer = await showModalBottomSheet<Layer>(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: context.color.surface,
|
||||||
builder: (context) => const EmojiPickerBottom(),
|
builder: (context) => const EmojiPickerBottom(),
|
||||||
);
|
);
|
||||||
if (layer == null) return;
|
if (layer == null) return;
|
||||||
|
|
@ -124,7 +124,7 @@ class EditorSideToolbar extends StatelessWidget {
|
||||||
tooltipText: context.lang.shareImageSendToWidget,
|
tooltipText: context.lang.shareImageSendToWidget,
|
||||||
color: sendToWidget || highlightWidgetOption
|
color: sendToWidget || highlightWidgetOption
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Colors.white,
|
: context.appColor(AppColor.mediaForeground),
|
||||||
disable: isUpdatingWidgetMode,
|
disable: isUpdatingWidgetMode,
|
||||||
onPressed: onToggleSendToWidget,
|
onPressed: onToggleSendToWidget,
|
||||||
),
|
),
|
||||||
|
|
@ -148,7 +148,7 @@ class EditorSideToolbar extends StatelessWidget {
|
||||||
tooltipText: 'Trim video',
|
tooltipText: 'Trim video',
|
||||||
color: trimmerVisible
|
color: trimmerVisible
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Colors.white,
|
: context.appColor(AppColor.mediaForeground),
|
||||||
onPressed: onToggleTrimmer,
|
onPressed: onToggleTrimmer,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -160,8 +160,8 @@ class EditorSideToolbar extends StatelessWidget {
|
||||||
: Icons.volume_up_rounded,
|
: Icons.volume_up_rounded,
|
||||||
tooltipText: 'Enable Audio in Video',
|
tooltipText: 'Enable Audio in Video',
|
||||||
color: (mediaService.removeAudio)
|
color: (mediaService.removeAudio)
|
||||||
? Colors.white.withAlpha(160)
|
? context.appColor(AppColor.mediaForegroundMuted)
|
||||||
: Colors.white,
|
: context.appColor(AppColor.mediaForeground),
|
||||||
onPressed: onToggleAudio,
|
onPressed: onToggleAudio,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -170,7 +170,7 @@ class EditorSideToolbar extends StatelessWidget {
|
||||||
ActionButton(
|
ActionButton(
|
||||||
Icons.crop_rotate_outlined,
|
Icons.crop_rotate_outlined,
|
||||||
tooltipText: 'Crop or rotate image',
|
tooltipText: 'Crop or rotate image',
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
layerStack.toggleBackgroundEditing();
|
layerStack.toggleBackgroundEditing();
|
||||||
onChanged();
|
onChanged();
|
||||||
|
|
@ -184,7 +184,7 @@ class EditorSideToolbar extends StatelessWidget {
|
||||||
tooltipText: context.lang.protectAsARealTwonly,
|
tooltipText: context.lang.protectAsARealTwonly,
|
||||||
color: media.requiresAuthentication
|
color: media.requiresAuthentication
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Colors.white,
|
: context.appColor(AppColor.mediaForeground),
|
||||||
onPressed: onToggleRequiresAuth,
|
onPressed: onToggleRequiresAuth,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -109,8 +109,8 @@ class _DrawLayerState extends State<DrawLayer> {
|
||||||
FontAwesomeIcons.arrowRotateLeft,
|
FontAwesomeIcons.arrowRotateLeft,
|
||||||
tooltipText: context.lang.undo,
|
tooltipText: context.lang.undo,
|
||||||
color: widget.layerData.control.paths.isNotEmpty
|
color: widget.layerData.control.paths.isNotEmpty
|
||||||
? Colors.white
|
? context.appColor(AppColor.mediaForeground)
|
||||||
: Colors.white.withAlpha(80),
|
: context.appColor(AppColor.mediaForegroundMuted),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (widget.layerData.control.paths.isEmpty) return;
|
if (widget.layerData.control.paths.isEmpty) return;
|
||||||
skipNextEvent = true;
|
skipNextEvent = true;
|
||||||
|
|
@ -123,8 +123,8 @@ class _DrawLayerState extends State<DrawLayer> {
|
||||||
tooltipText: context.lang.redo,
|
tooltipText: context.lang.redo,
|
||||||
FontAwesomeIcons.arrowRotateRight,
|
FontAwesomeIcons.arrowRotateRight,
|
||||||
color: undoList.isNotEmpty
|
color: undoList.isNotEmpty
|
||||||
? Colors.white
|
? context.appColor(AppColor.mediaForeground)
|
||||||
: Colors.white.withAlpha(80),
|
: context.appColor(AppColor.mediaForegroundMuted),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (undoList.isEmpty) return;
|
if (undoList.isEmpty) return;
|
||||||
|
|
||||||
|
|
@ -226,7 +226,7 @@ class MagnifyingGlass extends StatelessWidget {
|
||||||
color: color,
|
color: color,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
width: 2,
|
width: 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/share_image_editor_components/action_button.dart';
|
import 'package:twonly/src/visual/views/camera/share_image_editor_components/action_button.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart';
|
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart';
|
||||||
|
|
||||||
|
|
@ -171,7 +172,9 @@ class _EmojiLayerState extends State<EmojiLayer> {
|
||||||
child: ActionButton(
|
child: ActionButton(
|
||||||
FontAwesomeIcons.trashCan,
|
FontAwesomeIcons.trashCan,
|
||||||
tooltipText: '',
|
tooltipText: '',
|
||||||
color: deleteLayer ? Colors.red : Colors.white,
|
color: deleteLayer
|
||||||
|
? context.appColor(AppColor.recording)
|
||||||
|
: context.appColor(AppColor.mediaForeground),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart';
|
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/filters/datetime_filter.dart';
|
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/filters/datetime_filter.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/filters/image_filter.dart';
|
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/filters/image_filter.dart';
|
||||||
|
|
@ -45,11 +46,11 @@ class FilterText extends StatelessWidget {
|
||||||
this.text, {
|
this.text, {
|
||||||
super.key,
|
super.key,
|
||||||
this.fontSize = 24,
|
this.fontSize = 24,
|
||||||
this.color = Colors.white,
|
this.color,
|
||||||
});
|
});
|
||||||
final String text;
|
final String text;
|
||||||
final double fontSize;
|
final double fontSize;
|
||||||
final Color color;
|
final Color? color;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -58,10 +59,10 @@ class FilterText extends StatelessWidget {
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
color: color,
|
color: color ?? context.appColor(AppColor.mediaForeground),
|
||||||
shadows: const [
|
shadows: [
|
||||||
Shadow(
|
Shadow(
|
||||||
color: Color.fromARGB(122, 0, 0, 0),
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
blurRadius: 5,
|
blurRadius: 5,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import 'package:intl/intl.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/filter.layer.dart';
|
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/filter.layer.dart';
|
||||||
|
|
||||||
class DateTimeFilter extends StatelessWidget {
|
class DateTimeFilter extends StatelessWidget {
|
||||||
const DateTimeFilter({super.key, this.color = Colors.white});
|
const DateTimeFilter({super.key, this.color});
|
||||||
|
|
||||||
final Color color;
|
final Color? color;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/src/providers/image_editor.provider.dart';
|
import 'package:twonly/src/providers/image_editor.provider.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/share_image_editor_components/action_button.dart';
|
import 'package:twonly/src/visual/views/camera/share_image_editor_components/action_button.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart';
|
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart';
|
||||||
|
|
||||||
|
|
@ -113,7 +114,7 @@ class _TextViewState extends State<TextLayer> {
|
||||||
right: 0,
|
right: 0,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black.withAlpha(100),
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
),
|
),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
|
|
@ -151,8 +152,8 @@ class _TextViewState extends State<TextLayer> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -219,13 +220,13 @@ class _TextViewState extends State<TextLayer> {
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black.withAlpha(100),
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.layerData.text,
|
widget.layerData.text,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -245,7 +246,9 @@ class _TextViewState extends State<TextLayer> {
|
||||||
child: ActionButton(
|
child: ActionButton(
|
||||||
FontAwesomeIcons.trashCan,
|
FontAwesomeIcons.trashCan,
|
||||||
tooltipText: '',
|
tooltipText: '',
|
||||||
color: deleteLayer ? Colors.red : Colors.white,
|
color: deleteLayer
|
||||||
|
? context.appColor(AppColor.recording)
|
||||||
|
: context.appColor(AppColor.mediaForeground),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
/// Cuts a recorded video down to the part the user wants to send.
|
/// Cuts a recorded video down to the part the user wants to send.
|
||||||
|
|
@ -183,7 +184,7 @@ class _VideoTrimmerState extends State<VideoTrimmer> {
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
// Translucent rather than opaque: the frames being cut away stay
|
// Translucent rather than opaque: the frames being cut away stay
|
||||||
// visible underneath, which is most of what makes a cut readable.
|
// visible underneath, which is most of what makes a cut readable.
|
||||||
color: Colors.black.withAlpha(90),
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
@ -243,10 +244,15 @@ class _VideoTrimmerState extends State<VideoTrimmer> {
|
||||||
: _format(_duration),
|
: _format(_duration),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: Colors.white.withAlpha(isTrimmed ? 235 : 150),
|
color: isTrimmed
|
||||||
|
? context.appColor(AppColor.mediaForeground)
|
||||||
|
: context.appColor(AppColor.mediaForegroundMuted),
|
||||||
fontFeatures: const [FontFeature.tabularFigures()],
|
fontFeatures: const [FontFeature.tabularFigures()],
|
||||||
shadows: const [
|
shadows: [
|
||||||
Shadow(color: Color.fromARGB(122, 0, 0, 0), blurRadius: 4),
|
Shadow(
|
||||||
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
|
blurRadius: 4,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -287,7 +293,9 @@ class _TrimTrack extends StatelessWidget {
|
||||||
right: handleWidth,
|
right: handleWidth,
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withAlpha(46),
|
color: context
|
||||||
|
.appColor(AppColor.mediaForeground)
|
||||||
|
.withValues(alpha: 0.18),
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -307,8 +315,14 @@ class _TrimTrack extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_handle(left: startOffset),
|
_handle(
|
||||||
_handle(left: endOffset + handleWidth),
|
left: startOffset,
|
||||||
|
foregroundColor: context.appColor(AppColor.mediaForeground),
|
||||||
|
),
|
||||||
|
_handle(
|
||||||
|
left: endOffset + handleWidth,
|
||||||
|
foregroundColor: context.appColor(AppColor.mediaForeground),
|
||||||
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
left: playheadOffset + handleWidth - 1,
|
left: playheadOffset + handleWidth - 1,
|
||||||
top: 4,
|
top: 4,
|
||||||
|
|
@ -316,10 +330,13 @@ class _TrimTrack extends StatelessWidget {
|
||||||
width: 3,
|
width: 3,
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
borderRadius: BorderRadius.circular(2),
|
borderRadius: BorderRadius.circular(2),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(color: Color.fromARGB(122, 0, 0, 0), blurRadius: 4),
|
BoxShadow(
|
||||||
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
|
blurRadius: 4,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -328,7 +345,7 @@ class _TrimTrack extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _handle({required double left}) {
|
Widget _handle({required double left, required Color foregroundColor}) {
|
||||||
return Positioned(
|
return Positioned(
|
||||||
left: left,
|
left: left,
|
||||||
top: 0,
|
top: 0,
|
||||||
|
|
@ -346,7 +363,7 @@ class _TrimTrack extends StatelessWidget {
|
||||||
width: 2,
|
width: 2,
|
||||||
height: 14,
|
height: 14,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withAlpha(220),
|
color: foregroundColor,
|
||||||
borderRadius: BorderRadius.circular(1),
|
borderRadius: BorderRadius.circular(1),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -348,7 +348,6 @@ class _ChatListViewState extends State<ChatListView>
|
||||||
final plan = context.select<PurchasesProvider, SubscriptionPlan>(
|
final plan = context.select<PurchasesProvider, SubscriptionPlan>(
|
||||||
(p) => p.plan,
|
(p) => p.plan,
|
||||||
);
|
);
|
||||||
final dark = isDarkMode(context);
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Row(
|
title: Row(
|
||||||
|
|
@ -392,7 +391,7 @@ class _ChatListViewState extends State<ChatListView>
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: dark ? Colors.black : Colors.white,
|
color: context.color.onPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -421,11 +420,11 @@ class _ChatListViewState extends State<ChatListView>
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: NotificationBadgeComp(
|
child: NotificationBadgeComp(
|
||||||
backgroundColor: dark ? Colors.white : Colors.black,
|
backgroundColor: context.color.inverseSurface,
|
||||||
textColor: dark ? Colors.black : Colors.white,
|
textColor: context.color.onInverseSurface,
|
||||||
count: badgeCount.toString(),
|
count: badgeCount.toString(),
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
color: badgeCount > 0 ? Colors.black : null,
|
color: badgeCount > 0 ? context.color.onPrimary : null,
|
||||||
key: searchForOtherUsers,
|
key: searchForOtherUsers,
|
||||||
icon: const FaIcon(FontAwesomeIcons.userPlus, size: 18),
|
icon: const FaIcon(FontAwesomeIcons.userPlus, size: 18),
|
||||||
onPressed: () => context.push(Routes.chatsAddNewUser),
|
onPressed: () => context.push(Routes.chatsAddNewUser),
|
||||||
|
|
@ -555,12 +554,12 @@ class _ChatListViewState extends State<ChatListView>
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
heroTag: 'qrcode_fab',
|
heroTag: 'qrcode_fab',
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
backgroundColor: dark ? Colors.grey[800] : Colors.grey[200],
|
backgroundColor: context.color.surfaceContainerHigh,
|
||||||
foregroundColor: dark ? Colors.white : Colors.black87,
|
foregroundColor: context.color.onSurface,
|
||||||
onPressed: () => context.push(Routes.settingsPublicProfile),
|
onPressed: () => context.push(Routes.settingsPublicProfile),
|
||||||
child: FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.qrcode,
|
FontAwesomeIcons.qrcode,
|
||||||
color: dark ? Colors.white : Colors.black87,
|
color: context.color.onSurface,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
@ -568,11 +567,11 @@ class _ChatListViewState extends State<ChatListView>
|
||||||
heroTag: 'new_chat_fab',
|
heroTag: 'new_chat_fab',
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
backgroundColor: context.color.primary,
|
backgroundColor: context.color.primary,
|
||||||
foregroundColor: Colors.black87,
|
foregroundColor: context.color.onPrimary,
|
||||||
onPressed: () => context.push(Routes.chatsStartNewChat),
|
onPressed: () => context.push(Routes.chatsStartNewChat),
|
||||||
child: const FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.penToSquare,
|
FontAwesomeIcons.penToSquare,
|
||||||
color: Colors.black87,
|
color: context.color.onPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,10 @@ class NewsIconButtonComp extends StatelessWidget {
|
||||||
return NotificationBadgeComp(
|
return NotificationBadgeComp(
|
||||||
count: count.toString(),
|
count: count.toString(),
|
||||||
backgroundColor: context.color.primary,
|
backgroundColor: context.color.primary,
|
||||||
textColor: Colors.black87,
|
textColor: context.color.onPrimary,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () => context.push(Routes.settingsHelpNews),
|
onPressed: () => context.push(Routes.settingsHelpNews),
|
||||||
color: Colors.grey,
|
color: context.color.onSurfaceVariant,
|
||||||
tooltip: context.lang.settingsHelpNews,
|
tooltip: context.lang.settingsHelpNews,
|
||||||
icon: const FaIcon(FontAwesomeIcons.bullhorn, size: 19),
|
icon: const FaIcon(FontAwesomeIcons.bullhorn, size: 19),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ class _TypingIndicatorSubtitleCompState
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 3),
|
padding: const EdgeInsets.symmetric(vertical: 3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: getMessageColor(true),
|
color: getMessageColor(context, true),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Transform.scale(
|
child: Transform.scale(
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/contact_groups.comp.dart';
|
import 'package:twonly/src/visual/components/contact_groups.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
||||||
import 'package:twonly/src/visual/themes/colors.dart';
|
|
||||||
import 'package:twonly/src/visual/views/chats/chat_messages_components/animated_new_message.dart';
|
import 'package:twonly/src/visual/views/chats/chat_messages_components/animated_new_message.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/chat_messages_components/blink.component.dart';
|
import 'package:twonly/src/visual/views/chats/chat_messages_components/blink.component.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/chat_messages_components/chat_group_action.dart';
|
import 'package:twonly/src/visual/views/chats/chat_messages_components/chat_group_action.dart';
|
||||||
|
|
@ -819,8 +818,10 @@ class _ChatMessagesViewState extends State<ChatMessagesView>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Color getMessageColor(bool isOther) {
|
Color getMessageColor(BuildContext context, bool isOther) {
|
||||||
return isOther ? DefaultColors.messageSelf : DefaultColors.messageOther;
|
return isOther
|
||||||
|
? context.appColor(AppColor.chatBubbleReceived)
|
||||||
|
: context.appColor(AppColor.chatBubbleSent);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChatItem {
|
class ChatItem {
|
||||||
|
|
|
||||||
|
|
@ -80,10 +80,10 @@ class _AllReactionsViewState extends State<AllReactionsView> {
|
||||||
topRight: Radius.circular(32),
|
topRight: Radius.circular(32),
|
||||||
),
|
),
|
||||||
color: context.color.surface,
|
color: context.color.surface,
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
blurRadius: 10.9,
|
blurRadius: 10.9,
|
||||||
color: Color.fromRGBO(0, 0, 0, 0.1),
|
color: context.color.shadow.withValues(alpha: 0.1),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -93,7 +93,7 @@ class _AllReactionsViewState extends State<AllReactionsView> {
|
||||||
margin: const EdgeInsets.all(30),
|
margin: const EdgeInsets.all(30),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(32),
|
borderRadius: BorderRadius.circular(32),
|
||||||
color: Colors.grey,
|
color: context.color.outline,
|
||||||
),
|
),
|
||||||
height: 3,
|
height: 3,
|
||||||
width: 60,
|
width: 60,
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,10 @@ class MessageHistoryView extends StatelessWidget {
|
||||||
topRight: Radius.circular(32),
|
topRight: Radius.circular(32),
|
||||||
),
|
),
|
||||||
color: context.color.surface,
|
color: context.color.surface,
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
blurRadius: 10.9,
|
blurRadius: 10.9,
|
||||||
color: Color.fromRGBO(0, 0, 0, 0.1),
|
color: context.color.shadow.withValues(alpha: 0.1),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -43,7 +43,7 @@ class MessageHistoryView extends StatelessWidget {
|
||||||
margin: const EdgeInsets.all(30),
|
margin: const EdgeInsets.all(30),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(32),
|
borderRadius: BorderRadius.circular(32),
|
||||||
color: Colors.grey,
|
color: context.color.outline,
|
||||||
),
|
),
|
||||||
height: 3,
|
height: 3,
|
||||||
width: 60,
|
width: 60,
|
||||||
|
|
|
||||||
|
|
@ -85,10 +85,10 @@ class _ShareAdditionalViewState extends State<ShareAdditionalView> {
|
||||||
topRight: Radius.circular(32),
|
topRight: Radius.circular(32),
|
||||||
),
|
),
|
||||||
color: context.color.surface,
|
color: context.color.surface,
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
blurRadius: 10.9,
|
blurRadius: 10.9,
|
||||||
color: Color.fromRGBO(0, 0, 0, 0.1),
|
color: context.color.shadow.withValues(alpha: 0.1),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -98,7 +98,7 @@ class _ShareAdditionalViewState extends State<ShareAdditionalView> {
|
||||||
margin: const EdgeInsets.only(top: 30),
|
margin: const EdgeInsets.only(top: 30),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(32),
|
borderRadius: BorderRadius.circular(32),
|
||||||
color: Colors.grey,
|
color: context.color.outline,
|
||||||
),
|
),
|
||||||
height: 3,
|
height: 3,
|
||||||
width: 60,
|
width: 60,
|
||||||
|
|
@ -145,9 +145,7 @@ class _ShareAdditionalViewState extends State<ShareAdditionalView> {
|
||||||
color: context.color.surfaceContainer,
|
color: context.color.surfaceContainer,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: icon is IconData
|
child: icon is IconData ? Icon(icon) : FaIcon(icon as FaIconData?),
|
||||||
? Icon(icon)
|
|
||||||
: FaIcon(icon as FaIconData?),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(label, textAlign: TextAlign.center),
|
Text(label, textAlign: TextAlign.center),
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ class _WebxdcStoreViewState extends State<WebxdcStoreView> {
|
||||||
margin: const EdgeInsets.only(top: 12, bottom: 8),
|
margin: const EdgeInsets.only(top: 12, bottom: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(32),
|
borderRadius: BorderRadius.circular(32),
|
||||||
color: Colors.grey,
|
color: context.color.outline,
|
||||||
),
|
),
|
||||||
height: 3,
|
height: 3,
|
||||||
width: 60,
|
width: 60,
|
||||||
|
|
|
||||||
|
|
@ -161,13 +161,13 @@ class _ChatGroupActionState extends State<ChatGroupAction> {
|
||||||
child: FaIcon(
|
child: FaIcon(
|
||||||
icon,
|
icon,
|
||||||
size: 10,
|
size: 10,
|
||||||
color: Colors.grey,
|
color: context.color.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const WidgetSpan(child: SizedBox(width: 8)),
|
const WidgetSpan(child: SizedBox(width: 8)),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: text,
|
text: text,
|
||||||
style: const TextStyle(color: Colors.grey),
|
style: TextStyle(color: context.color.onSurfaceVariant),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class ReactionRow extends StatelessWidget {
|
||||||
// ignore: inference_failure_on_function_invocation
|
// ignore: inference_failure_on_function_invocation
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: context.color.surface,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AllReactionsView(
|
return AllReactionsView(
|
||||||
message: message,
|
message: message,
|
||||||
|
|
@ -103,9 +103,7 @@ class ReactionRow extends StatelessWidget {
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(),
|
border: Border.all(),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
color: isDarkMode(context)
|
color: context.appColor(AppColor.reactionBackground),
|
||||||
? const Color.fromARGB(255, 74, 74, 74)
|
|
||||||
: const Color.fromARGB(255, 197, 197, 197),
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -119,9 +117,7 @@ class ReactionRow extends StatelessWidget {
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: isDarkMode(context)
|
color: context.appColor(AppColor.onReaction),
|
||||||
? Colors.white
|
|
||||||
: Colors.black,
|
|
||||||
decoration: TextDecoration.none,
|
decoration: TextDecoration.none,
|
||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,7 @@ class _ChatAudioEntryState extends State<ChatAudioEntry> {
|
||||||
? InChatAudioPlayer(
|
? InChatAudioPlayer(
|
||||||
path: mediaService.tempPath.path,
|
path: mediaService.tempPath.path,
|
||||||
message: message,
|
message: message,
|
||||||
|
foregroundColor: info.textColor,
|
||||||
trailing: messageTime,
|
trailing: messageTime,
|
||||||
)
|
)
|
||||||
: Container())
|
: Container())
|
||||||
|
|
@ -137,12 +138,14 @@ class InChatAudioPlayer extends StatefulWidget {
|
||||||
const InChatAudioPlayer({
|
const InChatAudioPlayer({
|
||||||
required this.path,
|
required this.path,
|
||||||
required this.message,
|
required this.message,
|
||||||
|
required this.foregroundColor,
|
||||||
this.trailing,
|
this.trailing,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String path;
|
final String path;
|
||||||
final Message message;
|
final Message message;
|
||||||
|
final Color foregroundColor;
|
||||||
|
|
||||||
/// Shown below the waveform, next to the remaining playback time.
|
/// Shown below the waveform, next to the remaining playback time.
|
||||||
final Widget? trailing;
|
final Widget? trailing;
|
||||||
|
|
@ -158,11 +161,14 @@ class _InChatAudioPlayerState extends State<InChatAudioPlayer> {
|
||||||
static const double _cursorHeight = 20;
|
static const double _cursorHeight = 20;
|
||||||
static const double _playSlotWidth = 34;
|
static const double _playSlotWidth = 34;
|
||||||
static const List<double> _rates = [1, 1.5, 2, 0.5];
|
static const List<double> _rates = [1, 1.5, 2, 0.5];
|
||||||
static const _waveStyle = PlayerWaveStyle(
|
PlayerWaveStyle get _waveStyle => PlayerWaveStyle(
|
||||||
spacing: 4,
|
spacing: 4,
|
||||||
waveThickness: 2.5,
|
waveThickness: 2.5,
|
||||||
scaleFactor: 80,
|
scaleFactor: 80,
|
||||||
showSeekLine: false,
|
showSeekLine: false,
|
||||||
|
fixedWaveColor: widget.foregroundColor.withValues(alpha: 0.45),
|
||||||
|
liveWaveColor: widget.foregroundColor,
|
||||||
|
backgroundColor: widget.foregroundColor.withAlpha(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
AudioPlayback? _playback;
|
AudioPlayback? _playback;
|
||||||
|
|
@ -307,7 +313,7 @@ class _InChatAudioPlayerState extends State<InChatAudioPlayer> {
|
||||||
? FontAwesomeIcons.solidCirclePause
|
? FontAwesomeIcons.solidCirclePause
|
||||||
: FontAwesomeIcons.solidCirclePlay,
|
: FontAwesomeIcons.solidCirclePlay,
|
||||||
size: 28,
|
size: 28,
|
||||||
color: Colors.white,
|
color: widget.foregroundColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -347,7 +353,7 @@ class _InChatAudioPlayerState extends State<InChatAudioPlayer> {
|
||||||
child: Container(
|
child: Container(
|
||||||
width: _cursorWidth,
|
width: _cursorWidth,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: widget.foregroundColor,
|
||||||
borderRadius: BorderRadius.circular(2),
|
borderRadius: BorderRadius.circular(2),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -367,8 +373,8 @@ class _InChatAudioPlayerState extends State<InChatAudioPlayer> {
|
||||||
child: Text(
|
child: Text(
|
||||||
formatMsToMinSec(remaining),
|
formatMsToMinSec(remaining),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: widget.foregroundColor,
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -95,12 +95,13 @@ class _ChatContactsEntryState extends State<ChatContactsEntry> {
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
Divider(
|
Divider(
|
||||||
height: 1,
|
height: 1,
|
||||||
color: Colors.white.withValues(alpha: 0.2),
|
color: widget.info.textColor.withValues(alpha: 0.2),
|
||||||
),
|
),
|
||||||
_ContactRow(
|
_ContactRow(
|
||||||
contact: data.contacts[i],
|
contact: data.contacts[i],
|
||||||
message: widget.message,
|
message: widget.message,
|
||||||
contactsById: widget.contactsById,
|
contactsById: widget.contactsById,
|
||||||
|
foregroundColor: widget.info.textColor,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
@ -114,11 +115,13 @@ class _ContactRow extends StatefulWidget {
|
||||||
const _ContactRow({
|
const _ContactRow({
|
||||||
required this.contact,
|
required this.contact,
|
||||||
required this.message,
|
required this.message,
|
||||||
|
required this.foregroundColor,
|
||||||
this.contactsById,
|
this.contactsById,
|
||||||
});
|
});
|
||||||
|
|
||||||
final SharedContact contact;
|
final SharedContact contact;
|
||||||
final Message message;
|
final Message message;
|
||||||
|
final Color foregroundColor;
|
||||||
final Map<int, Contact>? contactsById;
|
final Map<int, Contact>? contactsById;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -220,36 +223,36 @@ class _ContactRowState extends State<_ContactRow> {
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
|
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const FaIcon(
|
FaIcon(
|
||||||
FontAwesomeIcons.user,
|
FontAwesomeIcons.user,
|
||||||
color: Colors.white,
|
color: widget.foregroundColor,
|
||||||
size: 16,
|
size: 16,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: BetterText(
|
child: BetterText(
|
||||||
text: widget.contact.displayName,
|
text: widget.contact.displayName,
|
||||||
textColor: Colors.white,
|
textColor: widget.foregroundColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.message.senderId != null && !isAdded) ...[
|
if (widget.message.senderId != null && !isAdded) ...[
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
if (_isLoading)
|
if (_isLoading)
|
||||||
const SizedBox(
|
SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
child: CircularProgressIndicator.adaptive(
|
child: CircularProgressIndicator.adaptive(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
Colors.white,
|
widget.foregroundColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
const FaIcon(
|
FaIcon(
|
||||||
FontAwesomeIcons.userPlus,
|
FontAwesomeIcons.userPlus,
|
||||||
color: Colors.white,
|
color: widget.foregroundColor,
|
||||||
size: 16,
|
size: 16,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,7 @@ class ChatDateChip extends StatelessWidget {
|
||||||
return Center(
|
return Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isDarkMode(context)
|
color: context.color.surfaceContainerHigh,
|
||||||
? const Color.fromARGB(255, 38, 38, 38)
|
|
||||||
: Colors.black.withAlpha(40),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
|
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
|
||||||
|
|
@ -43,7 +41,7 @@ class ChatDateChip extends StatelessWidget {
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
color: isDarkMode(context) ? Colors.white : Colors.grey,
|
color: context.color.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class ChatFlameRestoredEntry extends StatelessWidget {
|
||||||
text: context.lang.chatEntryFlameRestored(
|
text: context.lang.chatEntryFlameRestored(
|
||||||
data.restoredFlameCounter.toInt(),
|
data.restoredFlameCounter.toInt(),
|
||||||
),
|
),
|
||||||
textColor: isDarkMode(context) ? Colors.black : Colors.black,
|
textColor: info.textColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@ import 'package:twonly/src/services/mediafiles/media_download_policy.dart'
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/elements/better_text.element.dart';
|
import 'package:twonly/src/visual/elements/better_text.element.dart';
|
||||||
import 'package:twonly/src/visual/themes/colors.dart';
|
|
||||||
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/common.dart';
|
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/common.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/chat_messages_components/in_chat_media_viewer.dart';
|
import 'package:twonly/src/visual/views/chats/chat_messages_components/in_chat_media_viewer.dart';
|
||||||
|
import 'package:twonly/src/visual/views/chats/chat_messages_components/message_color.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/media_viewer.view.dart';
|
import 'package:twonly/src/visual/views/chats/media_viewer.view.dart';
|
||||||
|
|
||||||
class ChatMediaEntry extends StatefulWidget {
|
class ChatMediaEntry extends StatefulWidget {
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,12 @@ class ChatUnknownEntry extends StatelessWidget {
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.only(left: 10, top: 6, bottom: 6, right: 10),
|
padding: const EdgeInsets.only(left: 10, top: 6, bottom: 6, right: 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isDarkMode(context) ? Colors.black : Colors.grey,
|
color: context.appColor(AppColor.chatBubbleDeleted),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: BetterText(
|
child: BetterText(
|
||||||
text: context.lang.updateTwonlyMessage,
|
text: context.lang.updateTwonlyMessage,
|
||||||
textColor: isDarkMode(context)
|
textColor: context.appColor(AppColor.onChatBubbleDeleted),
|
||||||
? const Color.fromARGB(255, 99, 99, 99)
|
|
||||||
: Colors.black,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,10 @@ BubbleInfo getBubbleInfo(
|
||||||
) {
|
) {
|
||||||
final info = BubbleInfo()
|
final info = BubbleInfo()
|
||||||
..text = message.content ?? ''
|
..text = message.content ?? ''
|
||||||
..textColor = Colors.white
|
..textColor = message.senderId != null
|
||||||
..color = getMessageColor(message.senderId != null)
|
? context.appColor(AppColor.onChatBubbleReceived)
|
||||||
|
: context.appColor(AppColor.onChatBubbleSent)
|
||||||
|
..color = getMessageColor(context, message.senderId != null)
|
||||||
..displayTime = !combineTextMessageWithNext(message, nextMessage)
|
..displayTime = !combineTextMessageWithNext(message, nextMessage)
|
||||||
..displayUserName = ''
|
..displayUserName = ''
|
||||||
..minWidth = minWidth
|
..minWidth = minWidth
|
||||||
|
|
@ -61,7 +63,7 @@ BubbleInfo getBubbleInfo(
|
||||||
..expanded = false
|
..expanded = false
|
||||||
..color = message.quotesMessageId != null
|
..color = message.quotesMessageId != null
|
||||||
? Colors.transparent
|
? Colors.transparent
|
||||||
: getMessageColor(message.senderId != null);
|
: getMessageColor(context, message.senderId != null);
|
||||||
if (message.isDeletedFromSender) {
|
if (message.isDeletedFromSender) {
|
||||||
info
|
info
|
||||||
..color = context.color.surfaceBright
|
..color = context.color.surfaceBright
|
||||||
|
|
@ -73,10 +75,8 @@ BubbleInfo getBubbleInfo(
|
||||||
if (message.isDeletedFromSender) {
|
if (message.isDeletedFromSender) {
|
||||||
info
|
info
|
||||||
..text = context.lang.messageWasDeleted
|
..text = context.lang.messageWasDeleted
|
||||||
..color = isDarkMode(context) ? Colors.black : Colors.grey;
|
..color = context.appColor(AppColor.chatBubbleDeleted)
|
||||||
if (isDarkMode(context)) {
|
..textColor = context.appColor(AppColor.onChatBubbleDeleted);
|
||||||
info.textColor = const Color.fromARGB(255, 99, 99, 99);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,10 @@ class FriendlyMessageTime extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final statusIcon = _buildStatusIcon(context, Colors.grey.shade400);
|
final statusIcon = _buildStatusIcon(
|
||||||
|
context,
|
||||||
|
context.appColor(AppColor.chatMessageMeta),
|
||||||
|
);
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 6),
|
padding: const EdgeInsets.only(left: 6),
|
||||||
|
|
@ -34,7 +37,7 @@ class FriendlyMessageTime extends StatelessWidget {
|
||||||
height: 10,
|
height: 10,
|
||||||
child: FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.pencil,
|
FontAwesomeIcons.pencil,
|
||||||
color: color ?? Colors.white.withAlpha(150),
|
color: color ?? context.appColor(AppColor.chatMessageMeta),
|
||||||
size: 10,
|
size: 10,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -48,7 +51,7 @@ class FriendlyMessageTime extends StatelessWidget {
|
||||||
),
|
),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
color: color ?? Colors.white.withAlpha(150),
|
color: color ?? context.appColor(AppColor.chatMessageMeta),
|
||||||
decoration: TextDecoration.none,
|
decoration: TextDecoration.none,
|
||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -145,10 +145,7 @@ class _InChatMediaViewerState extends State<InChatMediaViewer> {
|
||||||
minHeight: 39,
|
minHeight: 39,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: widget.info.color.withValues(alpha: 0.3),
|
color: widget.info.color,
|
||||||
border: Border.all(
|
|
||||||
color: widget.info.color.withValues(alpha: 0.4),
|
|
||||||
),
|
|
||||||
borderRadius: widget.borderRadius,
|
borderRadius: widget.borderRadius,
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -166,9 +163,7 @@ class _InChatMediaViewerState extends State<InChatMediaViewer> {
|
||||||
if (widget.info.displayTime || widget.message.modifiedAt != null)
|
if (widget.info.displayTime || widget.message.modifiedAt != null)
|
||||||
FriendlyMessageTime(
|
FriendlyMessageTime(
|
||||||
message: widget.message,
|
message: widget.message,
|
||||||
color: isDarkMode(context)
|
color: context.appColor(AppColor.chatMessageMeta),
|
||||||
? Colors.white.withAlpha(100)
|
|
||||||
: Colors.black.withAlpha(100),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
|
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
|
Color getMessageColorFromType(
|
||||||
|
Message message,
|
||||||
|
MediaFile? mediaFile,
|
||||||
|
BuildContext context,
|
||||||
|
) {
|
||||||
|
if (message.type == MessageType.restoreFlameCounter.name) {
|
||||||
|
return context.appColor(AppColor.messageRestore);
|
||||||
|
}
|
||||||
|
if (message.type == MessageType.text.name) {
|
||||||
|
return context.appColor(AppColor.messageText);
|
||||||
|
}
|
||||||
|
if (message.isWidgetMedia) {
|
||||||
|
return context.appColor(AppColor.messageWidget);
|
||||||
|
}
|
||||||
|
if (mediaFile == null) return context.color.onSurface;
|
||||||
|
if (mediaFile.requiresAuthentication) return context.color.primary;
|
||||||
|
return switch (mediaFile.type) {
|
||||||
|
MediaType.video => context.appColor(AppColor.messageVideo),
|
||||||
|
MediaType.audio => context.appColor(AppColor.messageAudio),
|
||||||
|
_ => context.appColor(AppColor.messageImage),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -116,7 +116,7 @@ class MessageContextMenu extends StatelessWidget {
|
||||||
final layer =
|
final layer =
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
context: navigator.context,
|
context: navigator.context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: context.color.surface,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return const EmojiPickerBottom();
|
return const EmojiPickerBottom();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -324,7 +324,7 @@ class _MessageInputState extends State<MessageInput>
|
||||||
// ignore: inference_failure_on_function_invocation
|
// ignore: inference_failure_on_function_invocation
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: context.color.surface,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return ShareAdditionalView(
|
return ShareAdditionalView(
|
||||||
group: widget.group,
|
group: widget.group,
|
||||||
|
|
@ -457,10 +457,12 @@ class _MessageInputState extends State<MessageInput>
|
||||||
),
|
),
|
||||||
child: FadeTransition(
|
child: FadeTransition(
|
||||||
opacity: _recordingBlink,
|
opacity: _recordingBlink,
|
||||||
child: const FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.microphone,
|
FontAwesomeIcons.microphone,
|
||||||
size: 20,
|
size: 20,
|
||||||
color: Colors.red,
|
color: context.appColor(
|
||||||
|
AppColor.recording,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -470,9 +472,7 @@ class _MessageInputState extends State<MessageInput>
|
||||||
_currentDuration,
|
_currentDuration,
|
||||||
),
|
),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: isDarkMode(context)
|
color: context.color.onSurface,
|
||||||
? Colors.white
|
|
||||||
: Colors.black,
|
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -491,8 +491,10 @@ class _MessageInputState extends State<MessageInput>
|
||||||
onTap: _cancelAudioRecording,
|
onTap: _cancelAudioRecording,
|
||||||
child: Text(
|
child: Text(
|
||||||
context.lang.voiceMessageCancel,
|
context.lang.voiceMessageCancel,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.red,
|
color: context.appColor(
|
||||||
|
AppColor.recording,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -591,9 +593,7 @@ class _MessageInputState extends State<MessageInput>
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
90,
|
90,
|
||||||
),
|
),
|
||||||
color: isDarkMode(context)
|
color: context.color.surface,
|
||||||
? Colors.black
|
|
||||||
: Colors.white,
|
|
||||||
),
|
),
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
@ -622,7 +622,9 @@ class _MessageInputState extends State<MessageInput>
|
||||||
right: -20,
|
right: -20,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.red,
|
color: context.appColor(
|
||||||
|
AppColor.recording,
|
||||||
|
),
|
||||||
borderRadius: BorderRadius.circular(90),
|
borderRadius: BorderRadius.circular(90),
|
||||||
),
|
),
|
||||||
width: 60,
|
width: 60,
|
||||||
|
|
@ -644,7 +646,7 @@ class _MessageInputState extends State<MessageInput>
|
||||||
color:
|
color:
|
||||||
(_recordingState ==
|
(_recordingState ==
|
||||||
RecordingState.recording)
|
RecordingState.recording)
|
||||||
? Colors.white
|
? context.color.onError
|
||||||
: null,
|
: null,
|
||||||
(_recordingState == RecordingState.none)
|
(_recordingState == RecordingState.none)
|
||||||
? FontAwesomeIcons.microphone
|
? FontAwesomeIcons.microphone
|
||||||
|
|
@ -712,11 +714,11 @@ class _MessageInputState extends State<MessageInput>
|
||||||
),
|
),
|
||||||
searchViewConfig: SearchViewConfig(
|
searchViewConfig: SearchViewConfig(
|
||||||
backgroundColor: context.color.surfaceContainer,
|
backgroundColor: context.color.surfaceContainer,
|
||||||
buttonIconColor: Colors.white,
|
buttonIconColor: context.color.onSurface,
|
||||||
),
|
),
|
||||||
categoryViewConfig: CategoryViewConfig(
|
categoryViewConfig: CategoryViewConfig(
|
||||||
backgroundColor: context.color.surfaceContainer,
|
backgroundColor: context.color.surfaceContainer,
|
||||||
dividerColor: Colors.white,
|
dividerColor: context.color.outlineVariant,
|
||||||
indicatorColor: context.color.primary,
|
indicatorColor: context.color.primary,
|
||||||
iconColorSelected: context.color.primary,
|
iconColorSelected: context.color.primary,
|
||||||
iconColor: context.color.secondary,
|
iconColor: context.color.secondary,
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/animate_icon.comp.dart';
|
import 'package:twonly/src/visual/components/animate_icon.comp.dart';
|
||||||
import 'package:twonly/src/visual/themes/colors.dart';
|
|
||||||
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart';
|
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/chat_messages_components/file_limit_reached.dialog.dart';
|
import 'package:twonly/src/visual/views/chats/chat_messages_components/file_limit_reached.dialog.dart';
|
||||||
|
import 'package:twonly/src/visual/views/chats/chat_messages_components/message_color.dart';
|
||||||
|
|
||||||
enum MessageSendState {
|
enum MessageSendState {
|
||||||
received,
|
received,
|
||||||
|
|
@ -310,7 +310,11 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
|
||||||
widget.group!.draftMessage != null &&
|
widget.group!.draftMessage != null &&
|
||||||
widget.group!.draftMessage != '') {
|
widget.group!.draftMessage != '') {
|
||||||
icons = [
|
icons = [
|
||||||
const FaIcon(FontAwesomeIcons.pen, size: 12, color: Colors.grey),
|
FaIcon(
|
||||||
|
FontAwesomeIcons.pen,
|
||||||
|
size: 12,
|
||||||
|
color: context.color.onSurfaceVariant,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
textWidget = Text(
|
textWidget = Text(
|
||||||
'${context.lang.draftMessage}: ${substringBy(widget.group!.draftMessage!, 10)}',
|
'${context.lang.draftMessage}: ${substringBy(widget.group!.draftMessage!, 10)}',
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class ResponseContainer extends StatelessWidget {
|
||||||
maxWidth: MediaQuery.sizeOf(context).width * 0.8,
|
maxWidth: MediaQuery.sizeOf(context).width * 0.8,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: getMessageColor(msg.senderId != null),
|
color: getMessageColor(context, msg.senderId != null),
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
),
|
),
|
||||||
child: IntrinsicWidth(
|
child: IntrinsicWidth(
|
||||||
|
|
@ -187,7 +187,7 @@ class _ResponsePreviewState extends State<ResponsePreview> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
String? subtitle;
|
String? subtitle;
|
||||||
var color = const Color.fromARGB(233, 68, 137, 255);
|
var color = context.appColor(AppColor.messageText);
|
||||||
|
|
||||||
if (_message != null) {
|
if (_message != null) {
|
||||||
if (_message!.type == MessageType.text.name) {
|
if (_message!.type == MessageType.text.name) {
|
||||||
|
|
@ -238,7 +238,7 @@ class _ResponsePreviewState extends State<ResponsePreview> {
|
||||||
subtitle = _storyAlive ? context.lang.story : context.lang.storyExpired;
|
subtitle = _storyAlive ? context.lang.story : context.lang.storyExpired;
|
||||||
}
|
}
|
||||||
|
|
||||||
color = getMessageColor(_message!.senderId != null);
|
color = getMessageColor(context, _message!.senderId != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
final hasImage =
|
final hasImage =
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/chat_messages.view.dart';
|
import 'package:twonly/src/visual/views/chats/chat_messages.view.dart';
|
||||||
|
|
||||||
|
|
@ -131,7 +132,7 @@ class _TypingIndicatorState extends State<TypingIndicator> {
|
||||||
vertical: 6,
|
vertical: 6,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: getMessageColor(true),
|
color: getMessageColor(context, true),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: AnimatedTypingDots(
|
child: AnimatedTypingDots(
|
||||||
|
|
@ -246,8 +247,8 @@ class _AnimatedDot extends AnimatedWidget {
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
decoration: const BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.onChatBubbleReceived),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -870,10 +870,10 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: showSendTextMessageInput
|
color: showSendTextMessageInput
|
||||||
? null
|
? null
|
||||||
: const Color.fromARGB(255, 126, 126, 126),
|
: context.appColor(AppColor.mediaForegroundMuted),
|
||||||
shadows: const [
|
shadows: [
|
||||||
Shadow(
|
Shadow(
|
||||||
color: Color.fromARGB(122, 0, 0, 0),
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
blurRadius: 5,
|
blurRadius: 5,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import 'package:photo_view/photo_view.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart'
|
import 'package:twonly/src/database/tables/mediafiles.table.dart'
|
||||||
show MediaType;
|
show MediaType;
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
class MediaContentRenderer extends StatelessWidget {
|
class MediaContentRenderer extends StatelessWidget {
|
||||||
|
|
@ -47,10 +48,10 @@ class MediaContentRenderer extends StatelessWidget {
|
||||||
initialScale: PhotoViewComputedScale.contained,
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
minScale: PhotoViewComputedScale.contained,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
errorBuilder: (context, error, stackTrace) {
|
errorBuilder: (context, error, stackTrace) {
|
||||||
return const Center(
|
return Center(
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.broken_image_outlined,
|
Icons.broken_image_outlined,
|
||||||
color: Colors.white38,
|
color: context.appColor(AppColor.mediaForegroundMuted),
|
||||||
size: 64,
|
size: 64,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/database/daos/stories.dao.dart';
|
import 'package:twonly/src/database/daos/stories.dao.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/elements/my_icon_button.element.dart';
|
import 'package:twonly/src/visual/elements/my_icon_button.element.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/media_viewer_components/story_viewers.bottom_sheet.dart';
|
import 'package:twonly/src/visual/views/chats/media_viewer_components/story_viewers.bottom_sheet.dart';
|
||||||
|
|
||||||
|
|
@ -23,7 +24,7 @@ class OwnStoryBottomBar extends StatelessWidget {
|
||||||
Future<void> _showViewers(BuildContext context, MediaFile media) async {
|
Future<void> _showViewers(BuildContext context, MediaFile media) async {
|
||||||
await showModalBottomSheet<void>(
|
await showModalBottomSheet<void>(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: context.appColor(AppColor.mediaBackground),
|
||||||
builder: (context) => StoryViewersBottomSheet(mediaFile: media),
|
builder: (context) => StoryViewersBottomSheet(mediaFile: media),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -283,7 +283,10 @@ class EmojiFloatWidgetState extends State<EmojiFloatWidget>
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return IgnorePointer(
|
return IgnorePointer(
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
painter: _ParticlePainter(List<_Particle>.from(_particles)),
|
painter: _ParticlePainter(
|
||||||
|
List<_Particle>.from(_particles),
|
||||||
|
context.appColor(AppColor.mediaBackground),
|
||||||
|
),
|
||||||
size: Size.infinite,
|
size: Size.infinite,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -339,8 +342,9 @@ class _Particle {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ParticlePainter extends CustomPainter {
|
class _ParticlePainter extends CustomPainter {
|
||||||
_ParticlePainter(this.particles);
|
_ParticlePainter(this.particles, this.particleColor);
|
||||||
final List<_Particle> particles;
|
final List<_Particle> particles;
|
||||||
|
final Color particleColor;
|
||||||
|
|
||||||
static final _textPainter = TextPainter(textDirection: TextDirection.ltr);
|
static final _textPainter = TextPainter(textDirection: TextDirection.ltr);
|
||||||
|
|
||||||
|
|
@ -351,7 +355,7 @@ class _ParticlePainter extends CustomPainter {
|
||||||
text: p.emoji,
|
text: p.emoji,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 24 * p.currentScale,
|
fontSize: 24 * p.currentScale,
|
||||||
color: Colors.black.withValues(alpha: p.opacity),
|
color: particleColor.withValues(alpha: p.opacity),
|
||||||
fontFamily: Platform.isAndroid ? 'sans-serif' : null,
|
fontFamily: Platform.isAndroid ? 'sans-serif' : null,
|
||||||
fontFamilyFallback: Platform.isAndroid
|
fontFamilyFallback: Platform.isAndroid
|
||||||
? const ['NotoColorEmoji']
|
? const ['NotoColorEmoji']
|
||||||
|
|
|
||||||
|
|
@ -94,10 +94,10 @@ class _StoryViewersBottomSheetState extends State<StoryViewersBottomSheet> {
|
||||||
topRight: Radius.circular(32),
|
topRight: Radius.circular(32),
|
||||||
),
|
),
|
||||||
color: context.color.surface,
|
color: context.color.surface,
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
blurRadius: 10.9,
|
blurRadius: 10.9,
|
||||||
color: Color.fromRGBO(0, 0, 0, 0.1),
|
color: context.color.shadow.withValues(alpha: 0.1),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -107,7 +107,7 @@ class _StoryViewersBottomSheetState extends State<StoryViewersBottomSheet> {
|
||||||
margin: const EdgeInsets.only(top: 20, bottom: 8),
|
margin: const EdgeInsets.only(top: 20, bottom: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(32),
|
borderRadius: BorderRadius.circular(32),
|
||||||
color: Colors.grey,
|
color: context.color.outline,
|
||||||
),
|
),
|
||||||
height: 3,
|
height: 3,
|
||||||
width: 60,
|
width: 60,
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ class _MessageInfoViewState extends State<MessageInfoView> {
|
||||||
// ignore: inference_failure_on_function_invocation
|
// ignore: inference_failure_on_function_invocation
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: context.color.surface,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return MessageHistoryView(
|
return MessageHistoryView(
|
||||||
message: widget.message,
|
message: widget.message,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
||||||
import 'package:twonly/src/visual/context_menu/group.context_menu.dart';
|
import 'package:twonly/src/visual/context_menu/group.context_menu.dart';
|
||||||
import 'package:twonly/src/visual/context_menu/user.context_menu.dart';
|
import 'package:twonly/src/visual/context_menu/user.context_menu.dart';
|
||||||
import 'package:twonly/src/visual/decorations/input_text.decoration.dart';
|
import 'package:twonly/src/visual/elements/my_input.element.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/chat_messages.view.dart';
|
import 'package:twonly/src/visual/views/chats/chat_messages.view.dart';
|
||||||
|
|
||||||
class StartNewChatView extends StatefulWidget {
|
class StartNewChatView extends StatefulWidget {
|
||||||
|
|
@ -65,6 +65,7 @@ class _StartNewChatView extends State<StartNewChatView> {
|
||||||
void dispose() {
|
void dispose() {
|
||||||
allNonDirectGroupsSub.cancel();
|
allNonDirectGroupsSub.cancel();
|
||||||
contactSub.cancel();
|
contactSub.cancel();
|
||||||
|
searchUserName.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,22 +143,20 @@ class _StartNewChatView extends State<StartNewChatView> {
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
bottom: 40,
|
bottom: 40,
|
||||||
left: 10,
|
left: 10,
|
||||||
top: 20,
|
top: 0,
|
||||||
right: 10,
|
right: 10,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
child: TextField(
|
child: MyInput(
|
||||||
onChanged: (_) async {
|
|
||||||
await filterUsers();
|
|
||||||
},
|
|
||||||
controller: searchUserName,
|
controller: searchUserName,
|
||||||
decoration: getInputDecoration(
|
dense: true,
|
||||||
context,
|
fontWeight: FontWeight.normal,
|
||||||
context.lang.startNewChatSearchHint,
|
hintText: context.lang.startNewChatSearchHint,
|
||||||
),
|
prefixIcon: const Icon(Icons.search, size: 20),
|
||||||
|
onChanged: (_) => unawaited(filterUsers()),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
|
@ -171,12 +170,15 @@ class _StartNewChatView extends State<StartNewChatView> {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(context.lang.newGroup),
|
title: Text(context.lang.newGroup),
|
||||||
leading: const CircleAvatar(
|
leading: const SizedBox.square(
|
||||||
|
dimension: 26,
|
||||||
|
child: Center(
|
||||||
child: FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.userGroup,
|
FontAwesomeIcons.userGroup,
|
||||||
size: 13,
|
size: 13,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
onTap: () => context.push(
|
onTap: () => context.push(
|
||||||
Routes.groupCreateSelectMember(null),
|
Routes.groupCreateSelectMember(null),
|
||||||
),
|
),
|
||||||
|
|
@ -185,12 +187,15 @@ class _StartNewChatView extends State<StartNewChatView> {
|
||||||
if (i == 1) {
|
if (i == 1) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(context.lang.startNewChatNewContact),
|
title: Text(context.lang.startNewChatNewContact),
|
||||||
leading: const CircleAvatar(
|
leading: const SizedBox.square(
|
||||||
|
dimension: 26,
|
||||||
|
child: Center(
|
||||||
child: FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.userPlus,
|
FontAwesomeIcons.userPlus,
|
||||||
size: 13,
|
size: 13,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
onTap: () => context.push(Routes.chatsAddNewUser),
|
onTap: () => context.push(Routes.chatsAddNewUser),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,9 +52,9 @@ class OpenRequestsListComp extends StatelessWidget {
|
||||||
).merge(secondaryGreyButtonStyle(context)),
|
).merge(secondaryGreyButtonStyle(context)),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
Icon(
|
||||||
Icons.person_off_rounded,
|
Icons.person_off_rounded,
|
||||||
color: Color.fromARGB(164, 244, 67, 54),
|
color: context.color.error,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
context.lang.contactActionBlock,
|
context.lang.contactActionBlock,
|
||||||
|
|
@ -89,7 +89,7 @@ class OpenRequestsListComp extends StatelessWidget {
|
||||||
).merge(secondaryGreyButtonStyle(context)),
|
).merge(secondaryGreyButtonStyle(context)),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.check, color: Colors.green),
|
Icon(Icons.check, color: context.appColor(AppColor.success)),
|
||||||
Text(
|
Text(
|
||||||
context.lang.contactActionAccept,
|
context.lang.contactActionAccept,
|
||||||
style: const TextStyle(fontSize: 10),
|
style: const TextStyle(fontSize: 10),
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
||||||
import 'package:twonly/src/visual/elements/better_list_title.element.dart';
|
import 'package:twonly/src/visual/elements/better_list_title.element.dart';
|
||||||
import 'package:twonly/src/visual/views/contact/contact_components/mutual_groups_expansion_tile.comp.dart';
|
import 'package:twonly/src/visual/views/contact/contact_components/mutual_groups_expansion_tile.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/contact/contact_components/restore_flame.comp.dart';
|
import 'package:twonly/src/visual/views/contact/contact_components/restore_flame.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/contact/contact_components/user_discovery_contact_settings.comp.dart';
|
|
||||||
import 'package:twonly/src/visual/views/contact/contact_components/verification_expansion_tile.comp.dart';
|
import 'package:twonly/src/visual/views/contact/contact_components/verification_expansion_tile.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/contact/select_contact_groups.view.dart';
|
import 'package:twonly/src/visual/views/contact/select_contact_groups.view.dart';
|
||||||
import 'package:twonly/src/visual/views/groups/group.view.dart';
|
import 'package:twonly/src/visual/views/groups/group.view.dart';
|
||||||
|
|
@ -301,9 +300,6 @@ class _ContactViewState extends State<ContactView> {
|
||||||
MutualGroupsExpansionTileComp(
|
MutualGroupsExpansionTileComp(
|
||||||
contact: contact,
|
contact: contact,
|
||||||
),
|
),
|
||||||
UserDiscoveryContactSettingsComp(
|
|
||||||
contact: contact,
|
|
||||||
),
|
|
||||||
const Divider(),
|
const Divider(),
|
||||||
BetterListTile(
|
BetterListTile(
|
||||||
icon: FontAwesomeIcons.flag,
|
icon: FontAwesomeIcons.flag,
|
||||||
|
|
@ -331,7 +327,7 @@ class _ContactViewState extends State<ContactView> {
|
||||||
BetterListTile(
|
BetterListTile(
|
||||||
icon: FontAwesomeIcons.userMinus,
|
icon: FontAwesomeIcons.userMinus,
|
||||||
iconSize: 16,
|
iconSize: 16,
|
||||||
color: Colors.red,
|
color: context.color.error,
|
||||||
text: context.lang.contactRemove,
|
text: context.lang.contactRemove,
|
||||||
onTap: () => handleUserRemoveRequest(contact),
|
onTap: () => handleUserRemoveRequest(contact),
|
||||||
),
|
),
|
||||||
|
|
@ -382,12 +378,16 @@ class _ContactViewState extends State<ContactView> {
|
||||||
SignalVersion.v1 => 'Signal Protocol (v1)',
|
SignalVersion.v1 => 'Signal Protocol (v1)',
|
||||||
SignalVersion.v2 => 'PQXDH (v2)',
|
SignalVersion.v2 => 'PQXDH (v2)',
|
||||||
}}',
|
}}',
|
||||||
style: const TextStyle(color: Colors.grey),
|
style: TextStyle(
|
||||||
|
color: context.color.onSurfaceVariant,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
'Can send widgets: ${contact.widgetSharingAllowed ? 'Yes' : 'No'}',
|
'Can send widgets: ${contact.widgetSharingAllowed ? 'Yes' : 'No'}',
|
||||||
style: const TextStyle(color: Colors.grey),
|
style: TextStyle(
|
||||||
|
color: context.color.onSurfaceVariant,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
import 'package:drift/drift.dart' hide Column;
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:twonly/locator.dart';
|
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
|
||||||
import 'package:twonly/src/services/user_discovery.service.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
|
||||||
import 'package:twonly/src/visual/elements/better_list_title.element.dart';
|
|
||||||
import 'package:twonly/src/visual/views/settings/privacy/user_discovery.view.dart';
|
|
||||||
|
|
||||||
class UserDiscoveryContactSettingsComp extends StatelessWidget {
|
|
||||||
const UserDiscoveryContactSettingsComp({
|
|
||||||
required this.contact,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final Contact contact;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (!userService.currentUser.isUserDiscoveryEnabled) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userService.currentUser.userDiscoveryRequiresManualApproval &&
|
|
||||||
contact.userDiscoveryManualApproved != true) {
|
|
||||||
return BetterListTile(
|
|
||||||
icon: FontAwesomeIcons.usersViewfinder,
|
|
||||||
text: context.lang.userDiscoverySettingsTitle,
|
|
||||||
subtitle: Text(
|
|
||||||
context.lang.contactUserDiscoveryManualApprovalPending,
|
|
||||||
style: const TextStyle(fontSize: 10),
|
|
||||||
),
|
|
||||||
trailing: TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await twonlyDB.contactsDao.updateContact(
|
|
||||||
contact.userId,
|
|
||||||
const ContactsCompanion(
|
|
||||||
userDiscoveryManualApproved: Value(true),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
context.lang.contactUserDiscoveryManualApprovalApprove,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return BetterListTile(
|
|
||||||
icon: FontAwesomeIcons.usersViewfinder,
|
|
||||||
text: context.lang.userDiscoverySettingsTitle,
|
|
||||||
onTap: () => context.navPush(const UserDiscoverySettingsView()),
|
|
||||||
subtitle:
|
|
||||||
!contact.userDiscoveryExcluded &&
|
|
||||||
contact.mediaSendCounter <
|
|
||||||
userService.currentUser.requiredSendImages
|
|
||||||
? Text(
|
|
||||||
context.lang.contactUserDiscoveryImagesLeft(
|
|
||||||
userService.currentUser.requiredSendImages -
|
|
||||||
contact.mediaSendCounter,
|
|
||||||
getContactDisplayName(contact),
|
|
||||||
),
|
|
||||||
style: const TextStyle(fontSize: 9),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
trailing: Transform.scale(
|
|
||||||
scale: 0.8,
|
|
||||||
child: Switch.adaptive(
|
|
||||||
value: !contact.userDiscoveryExcluded,
|
|
||||||
onChanged: (a) async {
|
|
||||||
await UserDiscoveryService.changeExclusionForContact(
|
|
||||||
contact.userId,
|
|
||||||
!a,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -187,7 +187,7 @@ class _ContactGroupSettingsViewState extends State<ContactGroupSettingsView> {
|
||||||
Future<void> _selectEmoji() async {
|
Future<void> _selectEmoji() async {
|
||||||
final result = await showModalBottomSheet<dynamic>(
|
final result = await showModalBottomSheet<dynamic>(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: context.color.surface,
|
||||||
builder: (context) => const EmojiPickerBottom(),
|
builder: (context) => const EmojiPickerBottom(),
|
||||||
);
|
);
|
||||||
if (result is EmojiLayerData && mounted) {
|
if (result is EmojiLayerData && mounted) {
|
||||||
|
|
@ -330,9 +330,7 @@ class _ContactGroupSettingsViewState extends State<ContactGroupSettingsView> {
|
||||||
color: color == noContactGroupBackgroundColor ? null : Color(color),
|
color: color == noContactGroupBackgroundColor ? null : Color(color),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: selected
|
color: selected ? context.color.onSurface : context.color.outline,
|
||||||
? (isDarkMode(context) ? Colors.white : Colors.black)
|
|
||||||
: Theme.of(context).colorScheme.outline,
|
|
||||||
width: selected ? 3 : 1,
|
width: selected ? 3 : 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -431,10 +429,10 @@ class _ContactGroupSettingsViewState extends State<ContactGroupSettingsView> {
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: context.lang.deleteContactGroup,
|
tooltip: context.lang.deleteContactGroup,
|
||||||
onPressed: _delete,
|
onPressed: _delete,
|
||||||
icon: const FaIcon(
|
icon: FaIcon(
|
||||||
FontAwesomeIcons.trashCan,
|
FontAwesomeIcons.trashCan,
|
||||||
size: 18,
|
size: 18,
|
||||||
color: Colors.red,
|
color: context.color.error,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@ class CriticalErrorView extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const FaIcon(
|
FaIcon(
|
||||||
FontAwesomeIcons.triangleExclamation,
|
FontAwesomeIcons.triangleExclamation,
|
||||||
size: 80,
|
size: 80,
|
||||||
color: Colors.redAccent,
|
color: context.color.error,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
Text(
|
Text(
|
||||||
|
|
|
||||||
|
|
@ -322,7 +322,7 @@ class _GroupViewState extends State<GroupView> {
|
||||||
if (!_group!.leftGroup)
|
if (!_group!.leftGroup)
|
||||||
BetterListTile(
|
BetterListTile(
|
||||||
icon: FontAwesomeIcons.rightFromBracket,
|
icon: FontAwesomeIcons.rightFromBracket,
|
||||||
color: Colors.red,
|
color: context.color.error,
|
||||||
text: context.lang.leaveGroup,
|
text: context.lang.leaveGroup,
|
||||||
onTap: _leaveGroup,
|
onTap: _leaveGroup,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ class MemoriesCloudBackupPromoComp extends StatelessWidget {
|
||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(24),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withValues(alpha: 0.05),
|
color: context.color.shadow.withValues(alpha: 0.05),
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
offset: const Offset(0, 4),
|
offset: const Offset(0, 4),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,11 @@ class MemoriesFlashbackBannerComp extends StatelessWidget {
|
||||||
width: 120,
|
width: 120,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black12,
|
color: context.color.shadow.withValues(alpha: 0.12),
|
||||||
blurRadius: 6,
|
blurRadius: 6,
|
||||||
offset: Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -68,11 +68,15 @@ class MemoriesFlashbackBannerComp extends StatelessWidget {
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
errorBuilder: (context, error, stackTrace) {
|
errorBuilder: (context, error, stackTrace) {
|
||||||
return ColoredBox(
|
return ColoredBox(
|
||||||
color: Colors.grey.shade800,
|
color: context.appColor(
|
||||||
child: const Center(
|
AppColor.mediaBackground,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.broken_image_outlined,
|
Icons.broken_image_outlined,
|
||||||
color: Colors.white30,
|
color: context.appColor(
|
||||||
|
AppColor.mediaForegroundMuted,
|
||||||
|
),
|
||||||
size: 32,
|
size: 32,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -86,7 +90,7 @@ class MemoriesFlashbackBannerComp extends StatelessWidget {
|
||||||
begin: Alignment.bottomCenter,
|
begin: Alignment.bottomCenter,
|
||||||
end: Alignment.center,
|
end: Alignment.center,
|
||||||
colors: [
|
colors: [
|
||||||
Colors.black.withValues(alpha: 0.7),
|
context.appColor(AppColor.mediaScrim),
|
||||||
Colors.transparent,
|
Colors.transparent,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -100,8 +104,10 @@ class MemoriesFlashbackBannerComp extends StatelessWidget {
|
||||||
child: Text(
|
child: Text(
|
||||||
text,
|
text,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: context.appColor(
|
||||||
|
AppColor.mediaForeground,
|
||||||
|
),
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/model/memory_item.model.dart';
|
import 'package:twonly/src/model/memory_item.model.dart';
|
||||||
import 'package:twonly/src/services/memories/memories_cloud.service.dart';
|
import 'package:twonly/src/services/memories/memories_cloud.service.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/selectable_thumbnail.comp.dart';
|
import 'package:twonly/src/visual/components/selectable_thumbnail.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/memories/components/memory_transition_painter.dart';
|
import 'package:twonly/src/visual/views/memories/components/memory_transition_painter.dart';
|
||||||
|
|
||||||
|
|
@ -234,11 +235,11 @@ class _MemoriesThumbnailCompState extends State<MemoriesThumbnailComp> {
|
||||||
}
|
}
|
||||||
Log.warn(error);
|
Log.warn(error);
|
||||||
return ColoredBox(
|
return ColoredBox(
|
||||||
color: Colors.grey.shade200,
|
color: context.color.surfaceContainerHigh,
|
||||||
child: const Center(
|
child: Center(
|
||||||
child: FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.image,
|
FontAwesomeIcons.image,
|
||||||
color: Colors.black26,
|
color: context.color.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -251,37 +252,43 @@ class _MemoriesThumbnailCompState extends State<MemoriesThumbnailComp> {
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
ColoredBox(
|
ColoredBox(
|
||||||
color: Colors.grey.shade200,
|
color: context.color.surfaceContainerHigh,
|
||||||
child: const Center(
|
child: Center(
|
||||||
child: FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.image,
|
FontAwesomeIcons.image,
|
||||||
color: Colors.black26,
|
color: context.color.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (isVideo)
|
if (isVideo)
|
||||||
const Positioned.fill(
|
Positioned.fill(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.circlePlay,
|
FontAwesomeIcons.circlePlay,
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
size: 32,
|
size: 32,
|
||||||
shadows: [
|
shadows: [
|
||||||
Shadow(color: Colors.black54, blurRadius: 6),
|
Shadow(
|
||||||
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
|
blurRadius: 6,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (media.mediaFile.isFavorite)
|
if (media.mediaFile.isFavorite)
|
||||||
const Positioned(
|
Positioned(
|
||||||
bottom: 6,
|
bottom: 6,
|
||||||
left: 6,
|
left: 6,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.favorite,
|
Icons.favorite,
|
||||||
color: Colors.redAccent,
|
color: context.color.error,
|
||||||
size: 16,
|
size: 16,
|
||||||
shadows: [
|
shadows: [
|
||||||
Shadow(color: Colors.black54, blurRadius: 4),
|
Shadow(
|
||||||
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
|
blurRadius: 4,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -294,17 +301,17 @@ class _MemoriesThumbnailCompState extends State<MemoriesThumbnailComp> {
|
||||||
switch (media.mediaFile.cloudState) {
|
switch (media.mediaFile.cloudState) {
|
||||||
case CloudState.none:
|
case CloudState.none:
|
||||||
iconData = Icons.cloud_off_outlined;
|
iconData = Icons.cloud_off_outlined;
|
||||||
color = Colors.white54;
|
color = context.appColor(AppColor.mediaForegroundMuted);
|
||||||
case CloudState.pending:
|
case CloudState.pending:
|
||||||
iconData = Icons.cloud_upload_outlined;
|
iconData = Icons.cloud_upload_outlined;
|
||||||
color = Colors.white70;
|
color = context.appColor(AppColor.mediaForegroundMuted);
|
||||||
case CloudState.uploaded:
|
case CloudState.uploaded:
|
||||||
if (hasStored) {
|
if (hasStored) {
|
||||||
iconData = Icons.cloud_done_outlined;
|
iconData = Icons.cloud_done_outlined;
|
||||||
color = Colors.white;
|
color = context.appColor(AppColor.mediaForeground);
|
||||||
} else {
|
} else {
|
||||||
iconData = Icons.cloud_outlined;
|
iconData = Icons.cloud_outlined;
|
||||||
color = Colors.white;
|
color = context.appColor(AppColor.mediaForeground);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,8 +322,11 @@ class _MemoriesThumbnailCompState extends State<MemoriesThumbnailComp> {
|
||||||
iconData,
|
iconData,
|
||||||
color: color,
|
color: color,
|
||||||
size: 16,
|
size: 16,
|
||||||
shadows: const [
|
shadows: [
|
||||||
Shadow(color: Colors.black54, blurRadius: 4),
|
Shadow(
|
||||||
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
|
blurRadius: 4,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -85,15 +85,15 @@ class MemoriesSelectionMenuComp extends StatelessWidget {
|
||||||
value: 'delete',
|
value: 'delete',
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
Icon(
|
||||||
Icons.delete_forever_outlined,
|
Icons.delete_forever_outlined,
|
||||||
size: 20,
|
size: 20,
|
||||||
color: Colors.redAccent,
|
color: context.color.error,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(
|
Text(
|
||||||
context.lang.memoriesMenuDelete,
|
context.lang.memoriesMenuDelete,
|
||||||
style: const TextStyle(color: Colors.redAccent),
|
style: TextStyle(color: context.color.error),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class SynchronizedViewerActionsToolbarComp extends StatelessWidget {
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black.withValues(alpha: 0.50),
|
color: context.appColor(AppColor.mediaScrim),
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
|
@ -40,26 +40,28 @@ class SynchronizedViewerActionsToolbarComp extends StatelessWidget {
|
||||||
if (showStoreButton)
|
if (showStoreButton)
|
||||||
_ToolbarAction(
|
_ToolbarAction(
|
||||||
icon: isImageSaving
|
icon: isImageSaving
|
||||||
? const SizedBox(
|
? SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
child: CircularProgressIndicator.adaptive(
|
child: CircularProgressIndicator.adaptive(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
valueColor: AlwaysStoppedAnimation(Colors.white),
|
valueColor: AlwaysStoppedAnimation(
|
||||||
|
context.appColor(AppColor.mediaForeground),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const FaIcon(
|
: FaIcon(
|
||||||
FontAwesomeIcons.floppyDisk,
|
FontAwesomeIcons.floppyDisk,
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
size: 18,
|
size: 18,
|
||||||
),
|
),
|
||||||
label: context.lang.galleryActionSave,
|
label: context.lang.galleryActionSave,
|
||||||
onTap: isImageSaving ? null : onStore,
|
onTap: isImageSaving ? null : onStore,
|
||||||
),
|
),
|
||||||
_ToolbarAction(
|
_ToolbarAction(
|
||||||
icon: const FaIcon(
|
icon: FaIcon(
|
||||||
FontAwesomeIcons.download,
|
FontAwesomeIcons.download,
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
size: 19,
|
size: 19,
|
||||||
),
|
),
|
||||||
label: context.lang.galleryActionExport,
|
label: context.lang.galleryActionExport,
|
||||||
|
|
@ -68,7 +70,9 @@ class SynchronizedViewerActionsToolbarComp extends StatelessWidget {
|
||||||
_ToolbarAction(
|
_ToolbarAction(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
isFavorite ? Icons.favorite : Icons.favorite_border,
|
isFavorite ? Icons.favorite : Icons.favorite_border,
|
||||||
color: isFavorite ? Colors.redAccent : Colors.white,
|
color: isFavorite
|
||||||
|
? context.color.error
|
||||||
|
: context.appColor(AppColor.mediaForeground),
|
||||||
size: 22,
|
size: 22,
|
||||||
),
|
),
|
||||||
label: isFavorite
|
label: isFavorite
|
||||||
|
|
@ -77,18 +81,18 @@ class SynchronizedViewerActionsToolbarComp extends StatelessWidget {
|
||||||
onTap: onToggleFavorite,
|
onTap: onToggleFavorite,
|
||||||
),
|
),
|
||||||
_ToolbarAction(
|
_ToolbarAction(
|
||||||
icon: const Icon(
|
icon: Icon(
|
||||||
Icons.delete,
|
Icons.delete,
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
size: 22,
|
size: 22,
|
||||||
),
|
),
|
||||||
label: context.lang.galleryActionDelete,
|
label: context.lang.galleryActionDelete,
|
||||||
onTap: onDelete,
|
onTap: onDelete,
|
||||||
),
|
),
|
||||||
_ToolbarAction(
|
_ToolbarAction(
|
||||||
icon: const FaIcon(
|
icon: FaIcon(
|
||||||
FontAwesomeIcons.shareNodes,
|
FontAwesomeIcons.shareNodes,
|
||||||
color: Colors.white,
|
color: context.appColor(AppColor.mediaForeground),
|
||||||
size: 19,
|
size: 19,
|
||||||
),
|
),
|
||||||
label: context.lang.galleryActionShare,
|
label: context.lang.galleryActionShare,
|
||||||
|
|
@ -135,7 +139,7 @@ class _ToolbarAction extends StatelessWidget {
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white.withValues(alpha: 0.9),
|
color: context.appColor(AppColor.mediaForegroundMuted),
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
letterSpacing: 0.1,
|
letterSpacing: 0.1,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:photo_view/photo_view.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/model/memory_item.model.dart';
|
import 'package:twonly/src/model/memory_item.model.dart';
|
||||||
import 'package:twonly/src/services/memories/memories_cloud.service.dart';
|
import 'package:twonly/src/services/memories/memories_cloud.service.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/helpers/video_player_file.helper.dart';
|
import 'package:twonly/src/visual/helpers/video_player_file.helper.dart';
|
||||||
|
|
||||||
class SynchronizedViewerItemComp extends StatefulWidget {
|
class SynchronizedViewerItemComp extends StatefulWidget {
|
||||||
|
|
@ -124,10 +125,10 @@ class _SynchronizedViewerItemCompState
|
||||||
hash: item.mediaService.mediaFile.blurhash!,
|
hash: item.mediaService.mediaFile.blurhash!,
|
||||||
optimizationMode: BlurHashOptimizationMode.approximation,
|
optimizationMode: BlurHashOptimizationMode.approximation,
|
||||||
)
|
)
|
||||||
: const Center(
|
: Center(
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.broken_image_outlined,
|
Icons.broken_image_outlined,
|
||||||
color: Colors.white38,
|
color: context.appColor(AppColor.mediaForegroundMuted),
|
||||||
size: 64,
|
size: 64,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -143,10 +144,10 @@ class _SynchronizedViewerItemCompState
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
),
|
),
|
||||||
errorBuilder: (context, error, stackTrace) {
|
errorBuilder: (context, error, stackTrace) {
|
||||||
return const Center(
|
return Center(
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.broken_image_outlined,
|
Icons.broken_image_outlined,
|
||||||
color: Colors.white38,
|
color: context.appColor(AppColor.mediaForegroundMuted),
|
||||||
size: 64,
|
size: 64,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -441,14 +441,14 @@ class MemoriesViewState extends State<MemoriesView>
|
||||||
Icon(
|
Icon(
|
||||||
Icons.photo_library_outlined,
|
Icons.photo_library_outlined,
|
||||||
size: 64,
|
size: 64,
|
||||||
color: Colors.grey.shade400,
|
color: context.color.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
context.lang.memoriesEmpty,
|
context.lang.memoriesEmpty,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.grey,
|
color: context.color.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -489,7 +489,7 @@ class MemoriesViewState extends State<MemoriesView>
|
||||||
children: [
|
children: [
|
||||||
if (_isUsageLimitReached)
|
if (_isUsageLimitReached)
|
||||||
Container(
|
Container(
|
||||||
color: Colors.redAccent,
|
color: context.color.error,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 8,
|
vertical: 8,
|
||||||
|
|
@ -497,8 +497,8 @@ class MemoriesViewState extends State<MemoriesView>
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.lang.memoriesBackupLimitReached,
|
context.lang.memoriesBackupLimitReached,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: context.color.onError,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
|
@ -656,7 +656,7 @@ class MemoriesViewState extends State<MemoriesView>
|
||||||
? Icons.favorite
|
? Icons.favorite
|
||||||
: Icons.favorite_border,
|
: Icons.favorite_border,
|
||||||
color: _filterFavoritesOnly
|
color: _filterFavoritesOnly
|
||||||
? Colors.redAccent
|
? context.color.error
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue