Skip to content

Commit fd48a33

Browse files
refactor(mobile): image thumbnails (#19821)
* image thumbnail refactor * minor const-ification in new thumbnail tile * underscore helper classes --------- Co-authored-by: Alex Tran <[email protected]>
1 parent a918481 commit fd48a33

File tree

6 files changed

+316
-227
lines changed

6 files changed

+316
-227
lines changed

mobile/lib/domain/models/asset/base_asset.model.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ sealed class BaseAsset {
4343
bool get isImage => type == AssetType.image;
4444
bool get isVideo => type == AssetType.video;
4545

46+
Duration get duration {
47+
final durationInSeconds = this.durationInSeconds;
48+
if (durationInSeconds != null) {
49+
return Duration(seconds: durationInSeconds);
50+
}
51+
return const Duration();
52+
}
53+
4654
bool get hasRemote =>
4755
storage == AssetState.remote || storage == AssetState.merged;
4856
bool get hasLocal =>

mobile/lib/extensions/duration_extensions.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,15 @@ extension TZOffsetExtension on Duration {
33
String formatAsOffset() =>
44
"${isNegative ? '-' : '+'}${inHours.abs().toString().padLeft(2, '0')}:${inMinutes.abs().remainder(60).toString().padLeft(2, '0')}";
55
}
6+
7+
extension DurationFormatExtension on Duration {
8+
String format() {
9+
final seconds = inSeconds.remainder(60).toString().padLeft(2, '0');
10+
final minutes = inMinutes.remainder(60).toString().padLeft(2, '0');
11+
if (inHours == 0) {
12+
return "$minutes:$seconds";
13+
}
14+
final hours = inHours.toString().padLeft(2, '0');
15+
return "$hours:$minutes:$seconds";
16+
}
17+
}

mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
22
import 'package:hooks_riverpod/hooks_riverpod.dart';
33
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
44
import 'package:immich_mobile/extensions/build_context_extensions.dart';
5+
import 'package:immich_mobile/extensions/duration_extensions.dart';
56
import 'package:immich_mobile/extensions/theme_extensions.dart';
67
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
78
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
@@ -76,23 +77,33 @@ class ThumbnailTile extends ConsumerWidget {
7677
alignment: Alignment.topRight,
7778
child: Padding(
7879
padding: const EdgeInsets.only(right: 10.0, top: 6.0),
79-
child: _VideoIndicator(asset.durationInSeconds ?? 0),
80+
child: _VideoIndicator(asset.duration),
8081
),
8182
),
8283
if (showStorageIndicator)
83-
Align(
84-
alignment: Alignment.bottomRight,
85-
child: Padding(
86-
padding: const EdgeInsets.only(right: 10.0, bottom: 6.0),
87-
child: _TileOverlayIcon(
88-
switch (asset.storage) {
89-
AssetState.local => Icons.cloud_off_outlined,
90-
AssetState.remote => Icons.cloud_outlined,
91-
AssetState.merged => Icons.cloud_done_outlined,
92-
},
84+
switch (asset.storage) {
85+
AssetState.local => const Align(
86+
alignment: Alignment.bottomRight,
87+
child: Padding(
88+
padding: EdgeInsets.only(right: 10.0, bottom: 6.0),
89+
child: _TileOverlayIcon(Icons.cloud_off_outlined),
90+
),
9391
),
94-
),
95-
),
92+
AssetState.remote => const Align(
93+
alignment: Alignment.bottomRight,
94+
child: Padding(
95+
padding: EdgeInsets.only(right: 10.0, bottom: 6.0),
96+
child: _TileOverlayIcon(Icons.cloud_outlined),
97+
),
98+
),
99+
AssetState.merged => const Align(
100+
alignment: Alignment.bottomRight,
101+
child: Padding(
102+
padding: EdgeInsets.only(right: 10.0, bottom: 6.0),
103+
child: _TileOverlayIcon(Icons.cloud_done_outlined),
104+
),
105+
),
106+
},
96107
if (asset.isFavorite)
97108
const Align(
98109
alignment: Alignment.bottomLeft,
@@ -138,7 +149,7 @@ class _SelectionIndicator extends StatelessWidget {
138149
@override
139150
Widget build(BuildContext context) {
140151
if (isLocked) {
141-
return Container(
152+
return DecoratedBox(
142153
decoration: BoxDecoration(
143154
shape: BoxShape.circle,
144155
color: color,
@@ -149,7 +160,7 @@ class _SelectionIndicator extends StatelessWidget {
149160
),
150161
);
151162
} else if (isSelected) {
152-
return Container(
163+
return DecoratedBox(
153164
decoration: BoxDecoration(
154165
shape: BoxShape.circle,
155166
color: color,
@@ -169,23 +180,8 @@ class _SelectionIndicator extends StatelessWidget {
169180
}
170181

171182
class _VideoIndicator extends StatelessWidget {
172-
final int durationInSeconds;
173-
const _VideoIndicator(this.durationInSeconds);
174-
175-
String _formatDuration(int durationInSec) {
176-
final int hours = durationInSec ~/ 3600;
177-
final int minutes = (durationInSec % 3600) ~/ 60;
178-
final int seconds = durationInSec % 60;
179-
180-
final String minutesPadded = minutes.toString().padLeft(2, '0');
181-
final String secondsPadded = seconds.toString().padLeft(2, '0');
182-
183-
if (hours > 0) {
184-
return "$hours:$minutesPadded:$secondsPadded"; // H:MM:SS
185-
} else {
186-
return "$minutesPadded:$secondsPadded"; // MM:SS
187-
}
188-
}
183+
final Duration duration;
184+
const _VideoIndicator(this.duration);
189185

190186
@override
191187
Widget build(BuildContext context) {
@@ -197,15 +193,15 @@ class _VideoIndicator extends StatelessWidget {
197193
crossAxisAlignment: CrossAxisAlignment.end,
198194
children: [
199195
Text(
200-
_formatDuration(durationInSeconds),
201-
style: TextStyle(
196+
duration.format(),
197+
style: const TextStyle(
202198
color: Colors.white,
203199
fontSize: 12,
204200
fontWeight: FontWeight.bold,
205201
shadows: [
206202
Shadow(
207203
blurRadius: 5.0,
208-
color: Colors.black.withValues(alpha: 0.6),
204+
color: Color.fromRGBO(0, 0, 0, 0.6),
209205
),
210206
],
211207
),
@@ -228,10 +224,10 @@ class _TileOverlayIcon extends StatelessWidget {
228224
color: Colors.white,
229225
size: 16,
230226
shadows: [
231-
Shadow(
227+
const Shadow(
232228
blurRadius: 5.0,
233-
color: Colors.black.withValues(alpha: 0.6),
234-
offset: const Offset(0.0, 0.0),
229+
color: Color.fromRGBO(0, 0, 0, 0.6),
230+
offset: Offset(0.0, 0.0),
235231
),
236232
],
237233
);

mobile/lib/utils/storage_indicator.dart

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)