mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-05-25 07:22:13 +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 {
|
) async {
|
||||||
final stopwatch = Stopwatch()..start();
|
final stopwatch = Stopwatch()..start();
|
||||||
|
|
||||||
if (destinationFile.existsSync()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final bytes = sourceFile.readAsBytesSync();
|
await FlutterImageCompress.compressAndGetFile(
|
||||||
final result = await FlutterImageCompress.compressWithList(
|
sourceFile.absolute.path,
|
||||||
bytes,
|
destinationFile.absolute.path,
|
||||||
minWidth: 200,
|
minWidth: 300,
|
||||||
minHeight: 200,
|
minHeight: 300,
|
||||||
quality: 70,
|
quality: 100,
|
||||||
format: CompressFormat.webp,
|
format: CompressFormat.webp,
|
||||||
);
|
);
|
||||||
|
|
||||||
destinationFile.writeAsBytesSync(result);
|
|
||||||
stopwatch.stop();
|
stopwatch.stop();
|
||||||
Log.info(
|
Log.info(
|
||||||
'It took ${stopwatch.elapsedMilliseconds}ms to create the image thumbnail.',
|
'It took ${stopwatch.elapsedMilliseconds}ms to create the image thumbnail.',
|
||||||
|
|
@ -94,15 +88,15 @@ Future<bool> createThumbnailsForGif(
|
||||||
|
|
||||||
final thumbnail = img.copyResize(
|
final thumbnail = img.copyResize(
|
||||||
image,
|
image,
|
||||||
width: image.width > image.height ? 200 : null,
|
width: image.width > image.height ? 400 : null,
|
||||||
height: image.height >= image.width ? 200 : null,
|
height: image.height >= image.width ? 400 : null,
|
||||||
);
|
);
|
||||||
|
|
||||||
final pngBytes = img.encodePng(thumbnail);
|
final pngBytes = img.encodePng(thumbnail);
|
||||||
final webp = await FlutterImageCompress.compressWithList(
|
final webp = await FlutterImageCompress.compressWithList(
|
||||||
pngBytes,
|
pngBytes,
|
||||||
format: CompressFormat.webp,
|
format: CompressFormat.webp,
|
||||||
quality: 70,
|
quality: 85,
|
||||||
);
|
);
|
||||||
destinationFile.writeAsBytesSync(webp);
|
destinationFile.writeAsBytesSync(webp);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import 'package:twonly/src/utils/log.dart';
|
||||||
class MemoriesState {
|
class MemoriesState {
|
||||||
const MemoriesState({
|
const MemoriesState({
|
||||||
required this.filesToMigrate,
|
required this.filesToMigrate,
|
||||||
|
required this.totalFilesToMigrate,
|
||||||
required this.galleryItems,
|
required this.galleryItems,
|
||||||
required this.months,
|
required this.months,
|
||||||
required this.orderedByMonth,
|
required this.orderedByMonth,
|
||||||
|
|
@ -22,16 +23,21 @@ class MemoriesState {
|
||||||
});
|
});
|
||||||
|
|
||||||
final int filesToMigrate;
|
final int filesToMigrate;
|
||||||
|
final int totalFilesToMigrate;
|
||||||
final List<MemoryItem> galleryItems;
|
final List<MemoryItem> galleryItems;
|
||||||
final List<String> months;
|
final List<String> months;
|
||||||
final Map<String, List<int>> orderedByMonth;
|
final Map<String, List<int>> orderedByMonth;
|
||||||
final Map<int, List<MemoryItem>> galleryItemsLastYears;
|
final Map<int, List<MemoryItem>> galleryItemsLastYears;
|
||||||
|
|
||||||
bool get isLoading => filesToMigrate > 0;
|
bool get isLoading => filesToMigrate > 0;
|
||||||
|
double get migrationProgress => totalFilesToMigrate > 0
|
||||||
|
? (totalFilesToMigrate - filesToMigrate) / totalFilesToMigrate
|
||||||
|
: 0;
|
||||||
bool get isEmpty => galleryItems.isEmpty && filesToMigrate == 0;
|
bool get isEmpty => galleryItems.isEmpty && filesToMigrate == 0;
|
||||||
|
|
||||||
MemoriesState copyWith({
|
MemoriesState copyWith({
|
||||||
int? filesToMigrate,
|
int? filesToMigrate,
|
||||||
|
int? totalFilesToMigrate,
|
||||||
List<MemoryItem>? galleryItems,
|
List<MemoryItem>? galleryItems,
|
||||||
List<String>? months,
|
List<String>? months,
|
||||||
Map<String, List<int>>? orderedByMonth,
|
Map<String, List<int>>? orderedByMonth,
|
||||||
|
|
@ -39,6 +45,7 @@ class MemoriesState {
|
||||||
}) {
|
}) {
|
||||||
return MemoriesState(
|
return MemoriesState(
|
||||||
filesToMigrate: filesToMigrate ?? this.filesToMigrate,
|
filesToMigrate: filesToMigrate ?? this.filesToMigrate,
|
||||||
|
totalFilesToMigrate: totalFilesToMigrate ?? this.totalFilesToMigrate,
|
||||||
galleryItems: galleryItems ?? this.galleryItems,
|
galleryItems: galleryItems ?? this.galleryItems,
|
||||||
months: months ?? this.months,
|
months: months ?? this.months,
|
||||||
orderedByMonth: orderedByMonth ?? this.orderedByMonth,
|
orderedByMonth: orderedByMonth ?? this.orderedByMonth,
|
||||||
|
|
@ -63,6 +70,7 @@ class MemoriesService {
|
||||||
|
|
||||||
MemoriesState _currentState = const MemoriesState(
|
MemoriesState _currentState = const MemoriesState(
|
||||||
filesToMigrate: 0,
|
filesToMigrate: 0,
|
||||||
|
totalFilesToMigrate: 0,
|
||||||
galleryItems: [],
|
galleryItems: [],
|
||||||
months: [],
|
months: [],
|
||||||
orderedByMonth: {},
|
orderedByMonth: {},
|
||||||
|
|
@ -182,6 +190,7 @@ class MemoriesService {
|
||||||
|
|
||||||
return MemoriesState(
|
return MemoriesState(
|
||||||
filesToMigrate: filesToMigrate,
|
filesToMigrate: filesToMigrate,
|
||||||
|
totalFilesToMigrate: filesToMigrate, // Reset total when computing new state? No, keep existing total if migrating.
|
||||||
galleryItems: tempGalleryItems,
|
galleryItems: tempGalleryItems,
|
||||||
months: tempMonths,
|
months: tempMonths,
|
||||||
orderedByMonth: tempOrderedByMonth,
|
orderedByMonth: tempOrderedByMonth,
|
||||||
|
|
@ -195,7 +204,11 @@ class MemoriesService {
|
||||||
.getAllMediaFilesPendingMigration();
|
.getAllMediaFilesPendingMigration();
|
||||||
|
|
||||||
if (pendingFiles.isNotEmpty) {
|
if (pendingFiles.isNotEmpty) {
|
||||||
_updateMigrationCount(pendingFiles.length);
|
_currentState = _currentState.copyWith(
|
||||||
|
filesToMigrate: pendingFiles.length,
|
||||||
|
totalFilesToMigrate: pendingFiles.length,
|
||||||
|
);
|
||||||
|
_notifyState();
|
||||||
|
|
||||||
for (final mediaFile in pendingFiles) {
|
for (final mediaFile in pendingFiles) {
|
||||||
final mediaService = MediaFileService(mediaFile);
|
final mediaService = MediaFileService(mediaFile);
|
||||||
|
|
@ -261,7 +274,7 @@ class MemoriesService {
|
||||||
mediaFiles: mediaFiles,
|
mediaFiles: mediaFiles,
|
||||||
mediaIdToSender: mediaIdToSenderContact,
|
mediaIdToSender: mediaIdToSenderContact,
|
||||||
filesToMigrate: _currentState.filesToMigrate,
|
filesToMigrate: _currentState.filesToMigrate,
|
||||||
);
|
).copyWith(totalFilesToMigrate: _currentState.totalFilesToMigrate);
|
||||||
|
|
||||||
for (final item in newState.galleryItems) {
|
for (final item in newState.galleryItems) {
|
||||||
if (!item.mediaService.mediaFile.hasThumbnail &&
|
if (!item.mediaService.mediaFile.hasThumbnail &&
|
||||||
|
|
|
||||||
|
|
@ -45,12 +45,30 @@ class ChatAudioEntry extends StatelessWidget {
|
||||||
minWidth,
|
minWidth,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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(
|
return Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
||||||
minWidth: 250,
|
minWidth: 250,
|
||||||
),
|
),
|
||||||
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: info.color,
|
color: info.color,
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
|
|
@ -71,11 +89,17 @@ class ChatAudioEntry extends StatelessWidget {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
if (info.text != '')
|
if (isExpanded && info.text != '')
|
||||||
Expanded(
|
Expanded(
|
||||||
child: BetterText(text: info.text, textColor: info.textColor),
|
child: BetterText(
|
||||||
|
text: info.text,
|
||||||
|
textColor: info.textColor,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
else ...[
|
else if (info.text != '') ...[
|
||||||
|
BetterText(text: info.text, textColor: info.textColor),
|
||||||
|
SizedBox(width: spacerWidth),
|
||||||
|
] else ...[
|
||||||
if (mediaService.mediaFile.downloadState ==
|
if (mediaService.mediaFile.downloadState ==
|
||||||
DownloadState.ready ||
|
DownloadState.ready ||
|
||||||
mediaService.mediaFile.downloadState == null)
|
mediaService.mediaFile.downloadState == null)
|
||||||
|
|
@ -87,6 +111,7 @@ class ChatAudioEntry extends StatelessWidget {
|
||||||
: Container()
|
: Container()
|
||||||
else
|
else
|
||||||
MessageSendStateIcon([message], [mediaService.mediaFile]),
|
MessageSendStateIcon([message], [mediaService.mediaFile]),
|
||||||
|
SizedBox(width: spacerWidth),
|
||||||
],
|
],
|
||||||
if (info.displayTime || message.modifiedAt != null)
|
if (info.displayTime || message.modifiedAt != null)
|
||||||
FriendlyMessageTime(message: message),
|
FriendlyMessageTime(message: message),
|
||||||
|
|
@ -95,6 +120,8 @@ class ChatAudioEntry extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,12 +49,30 @@ class ChatTextEntry extends StatelessWidget {
|
||||||
minWidth,
|
minWidth,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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(
|
return Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
||||||
minWidth: minWidth,
|
minWidth: minWidth,
|
||||||
),
|
),
|
||||||
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: info.color,
|
color: info.color,
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
|
|
@ -75,14 +93,17 @@ class ChatTextEntry extends StatelessWidget {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
if (info.expanded)
|
if (isExpanded)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: BetterText(text: info.text, textColor: info.textColor),
|
child: BetterText(
|
||||||
|
text: info.text,
|
||||||
|
textColor: info.textColor,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
else ...[
|
else ...[
|
||||||
BetterText(text: info.text, textColor: info.textColor),
|
BetterText(text: info.text, textColor: info.textColor),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: info.spacerWidth,
|
width: spacerWidth,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
if (info.displayTime || message.modifiedAt != null)
|
if (info.displayTime || message.modifiedAt != null)
|
||||||
|
|
@ -92,5 +113,7 @@ class ChatTextEntry extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,11 @@ BubbleInfo getBubbleInfo(
|
||||||
info.spacerWidth = minWidth - measureTextWidth(info.text) - 53;
|
info.spacerWidth = minWidth - measureTextWidth(info.text) - 53;
|
||||||
if (info.spacerWidth < 0) info.spacerWidth = 0;
|
if (info.spacerWidth < 0) info.spacerWidth = 0;
|
||||||
|
|
||||||
info.expanded = false;
|
info
|
||||||
if (message.quotesMessageId == null) {
|
..expanded = false
|
||||||
info.color = getMessageColor(message.senderId != null);
|
..color = message.quotesMessageId != null
|
||||||
}
|
? Colors.transparent
|
||||||
|
: getMessageColor(message.senderId != null);
|
||||||
if (message.isDeletedFromSender) {
|
if (message.isDeletedFromSender) {
|
||||||
info
|
info
|
||||||
..color = context.color.surfaceBright
|
..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/alert.dialog.dart';
|
||||||
import 'package:twonly/src/visual/components/draggable_scrollbar.comp.dart';
|
import 'package:twonly/src/visual/components/draggable_scrollbar.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/snackbar.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/flashback_banner.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/memories/components/memory_thumbnail.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';
|
import 'package:twonly/src/visual/views/memories/components/selection_toolbar.comp.dart';
|
||||||
|
|
@ -293,29 +292,6 @@ class MemoriesViewState extends State<MemoriesView> {
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
final state = snapshot.data ?? _service.currentState;
|
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) {
|
if (state.isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -417,6 +393,30 @@ class MemoriesViewState extends State<MemoriesView> {
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
backgroundColor: context.color.surface,
|
backgroundColor: context.color.surface,
|
||||||
actions: [
|
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(
|
IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
_filterFavoritesOnly
|
_filterFavoritesOnly
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue