mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-05-25 05:42:11 +00:00
smaller ui fixes
This commit is contained in:
parent
91eedc76b0
commit
68c99c271f
6 changed files with 187 additions and 129 deletions
|
|
@ -47,21 +47,15 @@ Future<bool> createThumbnailsForImage(
|
|||
) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
|
||||
if (destinationFile.existsSync()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
final bytes = sourceFile.readAsBytesSync();
|
||||
final result = await FlutterImageCompress.compressWithList(
|
||||
bytes,
|
||||
minWidth: 200,
|
||||
minHeight: 200,
|
||||
quality: 70,
|
||||
await FlutterImageCompress.compressAndGetFile(
|
||||
sourceFile.absolute.path,
|
||||
destinationFile.absolute.path,
|
||||
minWidth: 300,
|
||||
minHeight: 300,
|
||||
quality: 100,
|
||||
format: CompressFormat.webp,
|
||||
);
|
||||
|
||||
destinationFile.writeAsBytesSync(result);
|
||||
stopwatch.stop();
|
||||
Log.info(
|
||||
'It took ${stopwatch.elapsedMilliseconds}ms to create the image thumbnail.',
|
||||
|
|
@ -94,15 +88,15 @@ Future<bool> createThumbnailsForGif(
|
|||
|
||||
final thumbnail = img.copyResize(
|
||||
image,
|
||||
width: image.width > image.height ? 200 : null,
|
||||
height: image.height >= image.width ? 200 : null,
|
||||
width: image.width > image.height ? 400 : null,
|
||||
height: image.height >= image.width ? 400 : null,
|
||||
);
|
||||
|
||||
final pngBytes = img.encodePng(thumbnail);
|
||||
final webp = await FlutterImageCompress.compressWithList(
|
||||
pngBytes,
|
||||
format: CompressFormat.webp,
|
||||
quality: 70,
|
||||
quality: 85,
|
||||
);
|
||||
destinationFile.writeAsBytesSync(webp);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import 'package:twonly/src/utils/log.dart';
|
|||
class MemoriesState {
|
||||
const MemoriesState({
|
||||
required this.filesToMigrate,
|
||||
required this.totalFilesToMigrate,
|
||||
required this.galleryItems,
|
||||
required this.months,
|
||||
required this.orderedByMonth,
|
||||
|
|
@ -22,16 +23,21 @@ class MemoriesState {
|
|||
});
|
||||
|
||||
final int filesToMigrate;
|
||||
final int totalFilesToMigrate;
|
||||
final List<MemoryItem> galleryItems;
|
||||
final List<String> months;
|
||||
final Map<String, List<int>> orderedByMonth;
|
||||
final Map<int, List<MemoryItem>> galleryItemsLastYears;
|
||||
|
||||
bool get isLoading => filesToMigrate > 0;
|
||||
double get migrationProgress => totalFilesToMigrate > 0
|
||||
? (totalFilesToMigrate - filesToMigrate) / totalFilesToMigrate
|
||||
: 0;
|
||||
bool get isEmpty => galleryItems.isEmpty && filesToMigrate == 0;
|
||||
|
||||
MemoriesState copyWith({
|
||||
int? filesToMigrate,
|
||||
int? totalFilesToMigrate,
|
||||
List<MemoryItem>? galleryItems,
|
||||
List<String>? months,
|
||||
Map<String, List<int>>? orderedByMonth,
|
||||
|
|
@ -39,6 +45,7 @@ class MemoriesState {
|
|||
}) {
|
||||
return MemoriesState(
|
||||
filesToMigrate: filesToMigrate ?? this.filesToMigrate,
|
||||
totalFilesToMigrate: totalFilesToMigrate ?? this.totalFilesToMigrate,
|
||||
galleryItems: galleryItems ?? this.galleryItems,
|
||||
months: months ?? this.months,
|
||||
orderedByMonth: orderedByMonth ?? this.orderedByMonth,
|
||||
|
|
@ -63,6 +70,7 @@ class MemoriesService {
|
|||
|
||||
MemoriesState _currentState = const MemoriesState(
|
||||
filesToMigrate: 0,
|
||||
totalFilesToMigrate: 0,
|
||||
galleryItems: [],
|
||||
months: [],
|
||||
orderedByMonth: {},
|
||||
|
|
@ -182,6 +190,7 @@ class MemoriesService {
|
|||
|
||||
return MemoriesState(
|
||||
filesToMigrate: filesToMigrate,
|
||||
totalFilesToMigrate: filesToMigrate, // Reset total when computing new state? No, keep existing total if migrating.
|
||||
galleryItems: tempGalleryItems,
|
||||
months: tempMonths,
|
||||
orderedByMonth: tempOrderedByMonth,
|
||||
|
|
@ -195,7 +204,11 @@ class MemoriesService {
|
|||
.getAllMediaFilesPendingMigration();
|
||||
|
||||
if (pendingFiles.isNotEmpty) {
|
||||
_updateMigrationCount(pendingFiles.length);
|
||||
_currentState = _currentState.copyWith(
|
||||
filesToMigrate: pendingFiles.length,
|
||||
totalFilesToMigrate: pendingFiles.length,
|
||||
);
|
||||
_notifyState();
|
||||
|
||||
for (final mediaFile in pendingFiles) {
|
||||
final mediaService = MediaFileService(mediaFile);
|
||||
|
|
@ -261,7 +274,7 @@ class MemoriesService {
|
|||
mediaFiles: mediaFiles,
|
||||
mediaIdToSender: mediaIdToSenderContact,
|
||||
filesToMigrate: _currentState.filesToMigrate,
|
||||
);
|
||||
).copyWith(totalFilesToMigrate: _currentState.totalFilesToMigrate);
|
||||
|
||||
for (final item in newState.galleryItems) {
|
||||
if (!item.mediaService.mediaFile.hasThumbnail &&
|
||||
|
|
|
|||
|
|
@ -45,55 +45,82 @@ class ChatAudioEntry extends StatelessWidget {
|
|||
minWidth,
|
||||
);
|
||||
|
||||
return Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
||||
minWidth: 250,
|
||||
),
|
||||
padding: const EdgeInsets.only(left: 10, top: 6, bottom: 6, right: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: info.color,
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (info.displayUserName != '')
|
||||
Text(
|
||||
info.displayUserName,
|
||||
textAlign: TextAlign.left,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final textWidth = measureTextWidth(info.text);
|
||||
const timeWidth = 60.0;
|
||||
final isExpanded =
|
||||
info.expanded ||
|
||||
(textWidth + timeWidth + 20 > constraints.maxWidth);
|
||||
final effectiveSpacerWidth =
|
||||
constraints.minWidth - textWidth - timeWidth;
|
||||
final spacerWidth = effectiveSpacerWidth > 0
|
||||
? effectiveSpacerWidth
|
||||
: 0.0;
|
||||
|
||||
return Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
||||
minWidth: 250,
|
||||
),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 10,
|
||||
top: 6,
|
||||
bottom: 6,
|
||||
right: 10,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: info.color,
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (info.text != '')
|
||||
Expanded(
|
||||
child: BetterText(text: info.text, textColor: info.textColor),
|
||||
)
|
||||
else ...[
|
||||
if (mediaService.mediaFile.downloadState ==
|
||||
DownloadState.ready ||
|
||||
mediaService.mediaFile.downloadState == null)
|
||||
mediaService.tempPath.existsSync()
|
||||
? InChatAudioPlayer(
|
||||
path: mediaService.tempPath.path,
|
||||
message: message,
|
||||
)
|
||||
: Container()
|
||||
else
|
||||
MessageSendStateIcon([message], [mediaService.mediaFile]),
|
||||
],
|
||||
if (info.displayTime || message.modifiedAt != null)
|
||||
FriendlyMessageTime(message: message),
|
||||
if (info.displayUserName != '')
|
||||
Text(
|
||||
info.displayUserName,
|
||||
textAlign: TextAlign.left,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
if (isExpanded && info.text != '')
|
||||
Expanded(
|
||||
child: BetterText(
|
||||
text: info.text,
|
||||
textColor: info.textColor,
|
||||
),
|
||||
)
|
||||
else if (info.text != '') ...[
|
||||
BetterText(text: info.text, textColor: info.textColor),
|
||||
SizedBox(width: spacerWidth),
|
||||
] else ...[
|
||||
if (mediaService.mediaFile.downloadState ==
|
||||
DownloadState.ready ||
|
||||
mediaService.mediaFile.downloadState == null)
|
||||
mediaService.tempPath.existsSync()
|
||||
? InChatAudioPlayer(
|
||||
path: mediaService.tempPath.path,
|
||||
message: message,
|
||||
)
|
||||
: Container()
|
||||
else
|
||||
MessageSendStateIcon([message], [mediaService.mediaFile]),
|
||||
SizedBox(width: spacerWidth),
|
||||
],
|
||||
if (info.displayTime || message.modifiedAt != null)
|
||||
FriendlyMessageTime(message: message),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,48 +49,71 @@ class ChatTextEntry extends StatelessWidget {
|
|||
minWidth,
|
||||
);
|
||||
|
||||
return Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
||||
minWidth: minWidth,
|
||||
),
|
||||
padding: const EdgeInsets.only(left: 10, top: 6, bottom: 6, right: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: info.color,
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (info.displayUserName != '')
|
||||
Text(
|
||||
info.displayUserName,
|
||||
textAlign: TextAlign.left,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final textWidth = measureTextWidth(info.text);
|
||||
const timeWidth = 60.0;
|
||||
final isExpanded =
|
||||
info.expanded ||
|
||||
(textWidth + timeWidth + 20 > constraints.maxWidth);
|
||||
final effectiveSpacerWidth =
|
||||
constraints.minWidth - textWidth - timeWidth;
|
||||
final spacerWidth = effectiveSpacerWidth > 0
|
||||
? effectiveSpacerWidth
|
||||
: 0.0;
|
||||
|
||||
return Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
||||
minWidth: minWidth,
|
||||
),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 10,
|
||||
top: 6,
|
||||
bottom: 6,
|
||||
right: 10,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: info.color,
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (info.expanded)
|
||||
Expanded(
|
||||
child: BetterText(text: info.text, textColor: info.textColor),
|
||||
)
|
||||
else ...[
|
||||
BetterText(text: info.text, textColor: info.textColor),
|
||||
SizedBox(
|
||||
width: info.spacerWidth,
|
||||
if (info.displayUserName != '')
|
||||
Text(
|
||||
info.displayUserName,
|
||||
textAlign: TextAlign.left,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (info.displayTime || message.modifiedAt != null)
|
||||
FriendlyMessageTime(message: message),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
if (isExpanded)
|
||||
Expanded(
|
||||
child: BetterText(
|
||||
text: info.text,
|
||||
textColor: info.textColor,
|
||||
),
|
||||
)
|
||||
else ...[
|
||||
BetterText(text: info.text, textColor: info.textColor),
|
||||
SizedBox(
|
||||
width: spacerWidth,
|
||||
),
|
||||
],
|
||||
if (info.displayTime || message.modifiedAt != null)
|
||||
FriendlyMessageTime(message: message),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,10 +50,11 @@ BubbleInfo getBubbleInfo(
|
|||
info.spacerWidth = minWidth - measureTextWidth(info.text) - 53;
|
||||
if (info.spacerWidth < 0) info.spacerWidth = 0;
|
||||
|
||||
info.expanded = false;
|
||||
if (message.quotesMessageId == null) {
|
||||
info.color = getMessageColor(message.senderId != null);
|
||||
}
|
||||
info
|
||||
..expanded = false
|
||||
..color = message.quotesMessageId != null
|
||||
? Colors.transparent
|
||||
: getMessageColor(message.senderId != null);
|
||||
if (message.isDeletedFromSender) {
|
||||
info
|
||||
..color = context.color.surfaceBright
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import 'package:twonly/src/utils/misc.dart';
|
|||
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
||||
import 'package:twonly/src/visual/components/draggable_scrollbar.comp.dart';
|
||||
import 'package:twonly/src/visual/components/snackbar.dart';
|
||||
import 'package:twonly/src/visual/loader/three_rotating_dots.loader.dart';
|
||||
import 'package:twonly/src/visual/views/memories/components/flashback_banner.comp.dart';
|
||||
import 'package:twonly/src/visual/views/memories/components/memory_thumbnail.comp.dart';
|
||||
import 'package:twonly/src/visual/views/memories/components/selection_toolbar.comp.dart';
|
||||
|
|
@ -293,29 +292,6 @@ class MemoriesViewState extends State<MemoriesView> {
|
|||
builder: (context, snapshot) {
|
||||
final state = snapshot.data ?? _service.currentState;
|
||||
|
||||
if (state.isLoading) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ThreeRotatingDots(
|
||||
size: 40,
|
||||
color: context.color.primary,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
context.lang.migrationOfMemories(state.filesToMigrate),
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (state.isEmpty) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
|
|
@ -417,6 +393,30 @@ class MemoriesViewState extends State<MemoriesView> {
|
|||
elevation: 0,
|
||||
backgroundColor: context.color.surface,
|
||||
actions: [
|
||||
if (state.isLoading)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
child: Center(
|
||||
child: Tooltip(
|
||||
message: context.lang.migrationOfMemories(
|
||||
state.filesToMigrate,
|
||||
),
|
||||
child: SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
value: state.migrationProgress,
|
||||
strokeWidth: 2.5,
|
||||
color: context.color.primary,
|
||||
backgroundColor: context.color.primary
|
||||
.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
_filterFavoritesOnly
|
||||
|
|
|
|||
Loading…
Reference in a new issue