feat: device detail

This commit is contained in:
Tien Do Nam
2026-08-04 17:42:26 +02:00
parent e4070a2938
commit 51a0f1ec02
7 changed files with 43 additions and 6 deletions
+5 -2
View File
@@ -213,7 +213,9 @@
"verify": "Verify", "verify": "Verify",
"info": { "info": {
"name": "Name", "name": "Name",
"address": "Address" "address": "Address",
"version": "Version",
"protocol": "Protocol v{version}"
}, },
"logs": { "logs": {
"title": "Logs", "title": "Logs",
@@ -225,7 +227,8 @@
"verifyPage": { "verifyPage": {
"title": "Verify", "title": "Verify",
"icons": "Icons", "icons": "Icons",
"raw": "Raw" "raw": "Raw",
"question": "Does it look the same on the other device?"
}, },
"receivePage": { "receivePage": {
"subTitle": { "subTitle": {
+1 -1
View File
@@ -4,7 +4,7 @@
/// To regenerate, run: `dart run slang` /// To regenerate, run: `dart run slang`
/// ///
/// Locales: 55 /// Locales: 55
/// Strings: 18572 (337 per locale) /// Strings: 18575 (337 per locale)
// coverage:ignore-file // coverage:ignore-file
// ignore_for_file: type=lint, unused_import // ignore_for_file: type=lint, unused_import
+9
View File
@@ -427,6 +427,9 @@ class Translations$verifyPage$en {
/// en: 'Raw' /// en: 'Raw'
String get raw => 'Raw'; String get raw => 'Raw';
/// en: 'Does it look the same on the other device?'
String get question => 'Does it look the same on the other device?';
} }
// Path: receivePage // Path: receivePage
@@ -1322,6 +1325,12 @@ class Translations$deviceDetailsPage$info$en {
/// en: 'Address' /// en: 'Address'
String get address => 'Address'; String get address => 'Address';
/// en: 'Version'
String get version => 'Version';
/// en: 'Protocol v{version}'
String protocol({required Object version}) => 'Protocol v${version}';
} }
// Path: deviceDetailsPage.logs // Path: deviceDetailsPage.logs
+2 -1
View File
@@ -78,7 +78,7 @@ class _DeviceDetailsPageState extends State<DeviceDetailsPage> with Refena {
BigButton( BigButton(
icon: favoriteEntry != null ? Icons.favorite : Icons.favorite_border, icon: favoriteEntry != null ? Icons.favorite : Icons.favorite_border,
label: t.deviceDetailsPage.favorite, label: t.deviceDetailsPage.favorite,
filled: false, filled: favoriteEntry != null,
onTap: () async => await _toggleFavorite(favoriteEntry), onTap: () async => await _toggleFavorite(favoriteEntry),
), ),
const SizedBox(width: 20), const SizedBox(width: 20),
@@ -99,6 +99,7 @@ class _DeviceDetailsPageState extends State<DeviceDetailsPage> with Refena {
children: [ children: [
for (final entry in { for (final entry in {
t.deviceDetailsPage.info.name: device.alias, t.deviceDetailsPage.info.name: device.alias,
t.deviceDetailsPage.info.version: t.deviceDetailsPage.info.protocol(version: device.version),
if (device.ip != null) t.deviceDetailsPage.info.address: '${device.ip}:${device.port}', if (device.ip != null) t.deviceDetailsPage.info.address: '${device.ip}:${device.port}',
}.entries) }.entries)
TableRow( TableRow(
+5
View File
@@ -216,11 +216,13 @@ class SendTab extends StatelessWidget {
child: vm.sendMode == SendMode.multiple child: vm.sendMode == SendMode.multiple
? _MultiSendDeviceListTile( ? _MultiSendDeviceListTile(
device: device, device: device,
isFavorite: favoriteEntry != null,
nameOverride: favoriteEntry?.alias, nameOverride: favoriteEntry?.alias,
vm: vm, vm: vm,
) )
: DeviceListTile( : DeviceListTile(
device: device, device: device,
isFavorite: favoriteEntry != null,
nameOverride: favoriteEntry?.alias, nameOverride: favoriteEntry?.alias,
onDetailsTap: () async => await context.push(() => DeviceDetailsPage(device: device)), onDetailsTap: () async => await context.push(() => DeviceDetailsPage(device: device)),
onTap: () async => await vm.onTapDevice(context, device), onTap: () async => await vm.onTapDevice(context, device),
@@ -513,11 +515,13 @@ class _SendModeButton extends StatelessWidget {
/// An advanced list tile which shows the progress of the file transfer. /// An advanced list tile which shows the progress of the file transfer.
class _MultiSendDeviceListTile extends StatelessWidget { class _MultiSendDeviceListTile extends StatelessWidget {
final Device device; final Device device;
final bool isFavorite;
final String? nameOverride; final String? nameOverride;
final SendTabVm vm; final SendTabVm vm;
const _MultiSendDeviceListTile({ const _MultiSendDeviceListTile({
required this.device, required this.device,
required this.isFavorite,
required this.nameOverride, required this.nameOverride,
required this.vm, required this.vm,
}); });
@@ -548,6 +552,7 @@ class _MultiSendDeviceListTile extends StatelessWidget {
device: device, device: device,
info: info, info: info,
progress: progress, progress: progress,
isFavorite: isFavorite,
nameOverride: nameOverride, nameOverride: nameOverride,
onDetailsTap: () async => await context.push(() => DeviceDetailsPage(device: device)), onDetailsTap: () async => await context.push(() => DeviceDetailsPage(device: device)),
onTap: () async => await vm.onTapDeviceMultiSend(context, device), onTap: () async => await vm.onTapDeviceMultiSend(context, device),
+8
View File
@@ -66,6 +66,14 @@ class _VerifyPageState extends State<VerifyPage> {
), ),
_VerifyMode.raw => SelectableText(combined), _VerifyMode.raw => SelectableText(combined),
}, },
const SizedBox(height: 20),
Center(
child: Text(
t.verifyPage.question,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: Theme.of(context).colorScheme.onSurfaceVariant),
),
),
], ],
), ),
); );
+13 -2
View File
@@ -7,6 +7,7 @@ import 'package:localsend_isolates/model/device.dart';
class DeviceListTile extends StatelessWidget { class DeviceListTile extends StatelessWidget {
final Device device; final Device device;
final bool isFavorite;
/// If not null, this name is used instead of [Device.alias]. /// If not null, this name is used instead of [Device.alias].
/// This is the case when the device is marked as favorite. /// This is the case when the device is marked as favorite.
@@ -19,6 +20,7 @@ class DeviceListTile extends StatelessWidget {
const DeviceListTile({ const DeviceListTile({
required this.device, required this.device,
this.isFavorite = false,
this.nameOverride, this.nameOverride,
this.info, this.info,
this.progress, this.progress,
@@ -31,7 +33,16 @@ class DeviceListTile extends StatelessWidget {
final badgeColor = Color.lerp(Theme.of(context).colorScheme.secondaryContainer, Colors.white, 0.3)!; final badgeColor = Color.lerp(Theme.of(context).colorScheme.secondaryContainer, Colors.white, 0.3)!;
return CustomListTile( return CustomListTile(
icon: Icon(device.deviceType.icon, size: 46), icon: Icon(device.deviceType.icon, size: 46),
title: Text(nameOverride ?? device.alias, style: const TextStyle(fontSize: 20)), title: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(nameOverride ?? device.alias, style: const TextStyle(fontSize: 20)),
if (isFavorite) ...[
const SizedBox(width: 5),
Icon(Icons.check_circle, size: 16),
],
],
),
trailing: onDetailsTap != null trailing: onDetailsTap != null
? IconButton( ? IconButton(
icon: const Icon(Icons.info_outline), icon: const Icon(Icons.info_outline),
@@ -54,7 +65,7 @@ class DeviceListTile extends StatelessWidget {
DeviceBadge( DeviceBadge(
backgroundColor: badgeColor, backgroundColor: badgeColor,
foregroundColor: Theme.of(context).colorScheme.onSecondaryContainer, foregroundColor: Theme.of(context).colorScheme.onSecondaryContainer,
label: 'LAN • HTTP', label: 'HTTP',
) )
else else
DeviceBadge( DeviceBadge(