Logout a single session of a authorized device

This commit is contained in:
2025-12-04 01:07:40 +08:00
parent b5262137ad
commit fe365e8c6d
2 changed files with 59 additions and 21 deletions

View File

@@ -1020,6 +1020,8 @@
"uploadFile": "Upload File", "uploadFile": "Upload File",
"authDeviceChallenges": "Device Usage", "authDeviceChallenges": "Device Usage",
"authDeviceHint": "Swipe left to edit label, swipe right to logout device.", "authDeviceHint": "Swipe left to edit label, swipe right to logout device.",
"authSessionLogout": "Logout Session",
"authSessionLogoutHint": "Are you sure you want to logout this session? This will terminate this specific login session.",
"settingsMessageDisplayStyle": "Message Display Style", "settingsMessageDisplayStyle": "Message Display Style",
"auto": "Auto", "auto": "Auto",
"manual": "Manual", "manual": "Manual",

View File

@@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/models/account.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
@@ -37,11 +38,13 @@ class _DeviceListTile extends StatelessWidget {
final SnAuthDeviceWithSession device; final SnAuthDeviceWithSession device;
final Function(String) updateDeviceLabel; final Function(String) updateDeviceLabel;
final Function(String) logoutDevice; final Function(String) logoutDevice;
final Function(String) logoutSession;
const _DeviceListTile({ const _DeviceListTile({
required this.device, required this.device,
required this.updateDeviceLabel, required this.updateDeviceLabel,
required this.logoutDevice, required this.logoutDevice,
required this.logoutSession,
}); });
@override @override
@@ -118,7 +121,11 @@ class _DeviceListTile extends StatelessWidget {
), ),
...device.sessions ...device.sessions
.map( .map(
(session) => Column( (session) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4, spacing: 4,
children: [ children: [
@@ -130,7 +137,9 @@ class _DeviceListTile extends StatelessWidget {
), ),
InfoRow( InfoRow(
label: 'lastActiveAt'.tr( label: 'lastActiveAt'.tr(
args: [session.lastGrantedAt.toLocal().formatSystem()], args: [
session.lastGrantedAt.toLocal().formatSystem(),
],
), ),
icon: Symbols.refresh_rounded, icon: Symbols.refresh_rounded,
), ),
@@ -145,6 +154,15 @@ class _DeviceListTile extends StatelessWidget {
icon: Symbols.dns, icon: Symbols.dns,
), ),
], ],
),
),
IconButton(
icon: Icon(Icons.logout),
tooltip: 'authSessionLogout'.tr(),
onPressed: () => logoutSession(session.id),
),
const Gap(4),
],
).padding(horizontal: 20, vertical: 8), ).padding(horizontal: 20, vertical: 8),
) )
.expand((element) => [element, const Divider(height: 1)]) .expand((element) => [element, const Divider(height: 1)])
@@ -178,6 +196,22 @@ class AccountSessionSheet extends HookConsumerWidget {
} }
} }
void logoutSession(String sessionId) async {
final confirm = await showConfirmAlert(
'authSessionLogoutHint'.tr(),
'authSessionLogout'.tr(),
isDanger: true,
);
if (!confirm || !context.mounted) return;
try {
final apiClient = ref.watch(apiClientProvider);
await apiClient.delete('/pass/accounts/me/sessions/$sessionId');
ref.invalidate(authDevicesProvider);
} catch (err) {
showErrorAlert(err);
}
}
void updateDeviceLabel(String sessionId) async { void updateDeviceLabel(String sessionId) async {
final controller = TextEditingController(); final controller = TextEditingController();
final label = await showDialog<String>( final label = await showDialog<String>(
@@ -265,6 +299,7 @@ class AccountSessionSheet extends HookConsumerWidget {
device: device, device: device,
updateDeviceLabel: updateDeviceLabel, updateDeviceLabel: updateDeviceLabel,
logoutDevice: logoutDevice, logoutDevice: logoutDevice,
logoutSession: logoutSession,
); );
} else { } else {
return Dismissible( return Dismissible(
@@ -320,6 +355,7 @@ class AccountSessionSheet extends HookConsumerWidget {
device: device, device: device,
updateDeviceLabel: updateDeviceLabel, updateDeviceLabel: updateDeviceLabel,
logoutDevice: logoutDevice, logoutDevice: logoutDevice,
logoutSession: logoutSession,
), ),
); );
} }