Skip to content

Commit

Permalink
Add session manage page
Browse files Browse the repository at this point in the history
  • Loading branch information
lifegpc authored Oct 29, 2024
1 parent c2d7228 commit 57f54d3
Show file tree
Hide file tree
Showing 11 changed files with 653 additions and 2 deletions.
11 changes: 11 additions & 0 deletions lib/api/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,20 @@ abstract class _EHApi {
Future<ApiResult<bool>> deleteToken(
{@Part(name: "token") String? token,
@CancelRequest() CancelToken? cancel});
@DELETE('/token/manage')
@MultiPart()
Future<ApiResult<bool>> deleteTokenById(@Part(name: "id") int id,
{@CancelRequest() CancelToken? cancel});
@GET('/token')
Future<ApiResult<TokenWithUserInfo>> getToken(
{@Query("token") String? token, @CancelRequest() CancelToken? cancel});
@GET('/token/manage')
Future<ApiResult<List<TokenWithoutToken>>> getTokens(
{@Query("uid") int? uid,
@Query("offset") int? offset,
@Query("limit") int? limit,
@Query("all_user") bool? allUser,
@CancelRequest() CancelToken? cancel});
@GET('/shared_token')
Future<ApiResult<SharedToken>> getSharedToken(
{@CancelRequest() CancelToken? cancel});
Expand Down
100 changes: 100 additions & 0 deletions lib/api/client.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions lib/api/token.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,42 @@ class Token {
Map<String, dynamic> toJson() => _$TokenToJson(this);
}

@JsonSerializable()
class TokenWithoutToken {
const TokenWithoutToken({
required this.id,
required this.uid,
required this.expired,
required this.httpOnly,
required this.secure,
required this.lastUsed,
this.client,
this.device,
this.clientVersion,
this.clientPlatform,
});
final int id;
final int uid;
@JsonKey(fromJson: _fromJson, toJson: _toJson)
final DateTime expired;
@JsonKey(name: 'http_only')
final bool httpOnly;
final bool secure;
@JsonKey(fromJson: _fromJson, toJson: _toJson, name: 'last_used')
final DateTime lastUsed;
final String? client;
final String? device;
@JsonKey(name: 'client_version')
final String? clientVersion;
@JsonKey(name: 'client_platform')
final String? clientPlatform;
static DateTime _fromJson(String d) => DateTime.parse(d);
static String _toJson(DateTime d) => d.toIso8601String();
factory TokenWithoutToken.fromJson(Map<String, dynamic> json) =>
_$TokenWithoutTokenFromJson(json);
Map<String, dynamic> toJson() => _$TokenWithoutTokenToJson(this);
}

@JsonSerializable()
class TokenWithUserInfo {
const TokenWithUserInfo({
Expand Down
28 changes: 28 additions & 0 deletions lib/api/token.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions lib/auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class AuthInfo {
Future<void> checkSessionInfo() async {
final data = (await api.getToken()).unwrap();
_token = data.token;
listener.tryEmit("auth_token_updated", null);
final d = await device;
final cv = await clientVersion;
final cp = clientPlatform;
Expand Down Expand Up @@ -87,6 +88,7 @@ class AuthInfo {
clientVersion: ecv,
clientPlatform: ecp);
_token = re.unwrap();
listener.tryEmit("auth_token_updated", null);
} catch (e) {
_log.warning("Failed to update token:", e);
}
Expand Down
117 changes: 117 additions & 0 deletions lib/components/session_card.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:logging/logging.dart';
import '../api/token.dart';
import '../api/user.dart';
import '../globals.dart';
import '../main.dart';

final _log = Logger("SessionCard");

Future<void> _deleteSession(int id, String errmsg) async {
try {
(await api.deleteTokenById(id)).unwrap();
listener.tryEmit("delete_session", id);
} catch (e) {
_log.severe("Failed to delete session $id: $e");
final snack = SnackBar(content: Text("$errmsg$e"));
rootScaffoldMessengerKey.currentState?.showSnackBar(snack);
}
}

class SessionCard extends StatelessWidget {
const SessionCard(this.token, {this.user, super.key});
final BUser? user;
final TokenWithoutToken token;
String get device {
var s = "";
if (token.device != null) {
s = token.device!;
}
var c = "";
if (token.client != null) {
c = token.client!;
}
if (token.clientPlatform != null) {
if (c.isNotEmpty) {
c += " ${token.clientPlatform!}";
}
}
if (token.clientVersion != null) {
if (c.isNotEmpty) {
c += " ${token.clientVersion!}";
}
}
if (s.isEmpty) {
s = c;
} else if (c.isNotEmpty) {
s = "$s($c)";
}
return s;
}

@override
Widget build(BuildContext context) {
final i18n = AppLocalizations.of(context)!;
final expiredTime =
DateFormat.yMd(MainApp.of(context).lang.toLocale().toString())
.add_jms()
.format(token.expired);
final lastUsed =
DateFormat.yMd(MainApp.of(context).lang.toLocale().toString())
.add_jms()
.format(token.lastUsed);
return Card.outlined(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("${i18n.sessionId}${i18n.colon}${token.id}"),
Text("${i18n.expireTime}${i18n.colon}$expiredTime"),
Text("${i18n.lastUsedTime}${i18n.colon}$lastUsed"),
device.isEmpty
? Container()
: Text("${i18n.device}${i18n.colon}$device"),
user != null
? Text("${i18n.username}${i18n.colon}${user!.username}")
: Container(),
],
)),
IconButton(
onPressed: token.id != auth.token?.id
? () => showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(i18n.deleteSession),
content: Text(user != null
? i18n.deleteSessionForUserConfirm(
user!.username)
: i18n.deleteSessionConfirm),
actions: [
TextButton(
onPressed: () {
_deleteSession(
token.id, i18n.failedDeleteSession);
context.pop();
},
child: Text(i18n.yes)),
TextButton(
onPressed: () {
context.pop();
},
child: Text(i18n.no)),
]);
})
: null,
tooltip: i18n.delete,
icon: const Icon(Icons.delete))
])));
}
}
8 changes: 8 additions & 0 deletions lib/globals.dart
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ enum MoreVertSettings {
markAsAd,
markAsNonAd,
shareGallery,
sessions,
}

void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) {
Expand Down Expand Up @@ -207,6 +208,9 @@ void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) {
context.push("/dialog/gallery/share/$gid");
}
break;
case MoreVertSettings.sessions:
context.push("/sessions");
break;
default:
break;
}
Expand Down Expand Up @@ -234,6 +238,10 @@ List<PopupMenuEntry<MoreVertSettings>> buildMoreVertSettings(
list.add(PopupMenuItem(
value: MoreVertSettings.taskManager, child: Text(i18n.taskManager)));
}
if (path != "/sessions") {
list.add(PopupMenuItem(
value: MoreVertSettings.sessions, child: Text(i18n.sessionManagemant)));
}
if (path == "/gallery/:gid" && auth.canShareGallery == true) {
list.add(PopupMenuItem(
value: MoreVertSettings.shareGallery, child: Text(i18n.shareGallery)));
Expand Down
18 changes: 17 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -339,5 +339,21 @@
"ok": "Ok",
"changeSettings": "Change settings",
"createUpdateMeiliSearchDataTask": "Create sync meilisearch server's data task",
"updateMeiliSearchDataGidHelp": "If gallery id is not empty, only specified gallery will sync to meilisearch server."
"updateMeiliSearchDataGidHelp": "If gallery id is not empty, only specified gallery will sync to meilisearch server.",
"sessionManagemant": "Session Management",
"deleteSession": "Delete session",
"deleteSessionConfirm": "Do you want to delete session?",
"deleteSessionForUserConfirm": "Do you want to delete session for user {user}?",
"@deleteSessionForUserConfirm": {
"placeholders": {
"user": {
"type": "String"
}
}
},
"sessionId": "Session ID",
"lastUsedTime": "Last used time",
"device": "Device",
"failedDeleteSession": "Failed to delete session: ",
"allUser": "All users"
}
Loading

0 comments on commit 57f54d3

Please sign in to comment.