This commit is contained in:
rustdesk
2026-03-02 12:27:21 +08:00
parent 157dbdc543
commit 890282e385
16 changed files with 236 additions and 46 deletions

View File

@@ -26,6 +26,7 @@ enum UserStatus { kDisabled, kNormal, kUnverified }
class UserPayload {
String name = '';
String displayName = '';
String avatar = '';
String email = '';
String note = '';
String? verifier;
@@ -35,6 +36,7 @@ class UserPayload {
UserPayload.fromJson(Map<String, dynamic> json)
: name = json['name'] ?? '',
displayName = json['display_name'] ?? '',
avatar = json['avatar'] ?? '',
email = json['email'] ?? '',
note = json['note'] ?? '',
verifier = json['verifier'],
@@ -49,6 +51,7 @@ class UserPayload {
final Map<String, dynamic> map = {
'name': name,
'display_name': displayName,
'avatar': avatar,
'status': status == UserStatus.kDisabled
? 0
: status == UserStatus.kUnverified

View File

@@ -2026,28 +2026,79 @@ class _AccountState extends State<_Account> {
}
Widget useInfo() {
text(String key, String value) {
return Align(
alignment: Alignment.centerLeft,
child: SelectionArea(child: Text('${translate(key)}: $value'))
.marginSymmetric(vertical: 4),
);
}
return Obx(() => Offstage(
offstage: gFFI.userModel.userName.value.isEmpty,
child: Column(
children: [
if (gFFI.userModel.displayName.value.trim().isNotEmpty &&
gFFI.userModel.displayName.value.trim() !=
gFFI.userModel.userName.value.trim())
text('Display Name', gFFI.userModel.displayName.value.trim()),
text('Username', gFFI.userModel.userName.value),
// text('Group', gFFI.groupModel.groupName.value),
],
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(10),
),
child: Builder(builder: (context) {
final avatarWidget = _buildUserAvatar();
return Row(
children: [
if (avatarWidget != null) avatarWidget,
if (avatarWidget != null) const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
gFFI.userModel.displayNameOrUserName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 2),
SelectionArea(
child: Text(
'@${gFFI.userModel.userName.value}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
),
],
),
),
],
);
}),
),
)).marginOnly(left: 18, top: 16);
}
Widget? _buildUserAvatar() {
final avatar = gFFI.userModel.avatar.value.trim();
if (avatar.isEmpty) return null;
const radius = 22.0;
if (avatar.startsWith('data:image/')) {
final comma = avatar.indexOf(',');
if (comma > 0) {
try {
return CircleAvatar(
radius: radius,
backgroundImage: MemoryImage(base64Decode(avatar.substring(comma + 1))),
);
} catch (_) {
return null;
}
}
} else if (avatar.startsWith('http://') || avatar.startsWith('https://')) {
return CircleAvatar(
radius: radius,
backgroundImage: NetworkImage(avatar),
);
}
return null;
}
}
class _Checkbox extends StatefulWidget {

View File

@@ -1,6 +1,7 @@
// original cm window in Sciter version.
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart';
@@ -462,23 +463,7 @@ class _CmHeaderState extends State<_CmHeader>
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 70,
height: 70,
alignment: Alignment.center,
decoration: BoxDecoration(
color: str2color(client.name),
borderRadius: BorderRadius.circular(15.0),
),
child: Text(
client.name[0],
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 55,
),
),
).marginOnly(right: 10.0),
_buildClientAvatar().marginOnly(right: 10.0),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
@@ -582,6 +567,60 @@ class _CmHeaderState extends State<_CmHeader>
@override
bool get wantKeepAlive => true;
Widget _buildClientAvatar() {
const borderRadius = BorderRadius.all(Radius.circular(15.0));
final avatar = client.avatar.trim();
if (avatar.startsWith('data:image/')) {
final comma = avatar.indexOf(',');
if (comma > 0) {
try {
final bytes = base64Decode(avatar.substring(comma + 1));
return ClipRRect(
borderRadius: borderRadius,
child: Image.memory(
bytes,
width: 70,
height: 70,
fit: BoxFit.cover,
),
);
} catch (_) {}
}
} else if (avatar.startsWith('http://') || avatar.startsWith('https://')) {
return ClipRRect(
borderRadius: borderRadius,
child: Image.network(
avatar,
width: 70,
height: 70,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => _buildInitialAvatar(),
),
);
}
return _buildInitialAvatar();
}
Widget _buildInitialAvatar() {
return Container(
width: 70,
height: 70,
alignment: Alignment.center,
decoration: BoxDecoration(
color: str2color(client.name),
borderRadius: BorderRadius.circular(15.0),
),
child: Text(
client.name[0],
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 55,
),
),
);
}
}
class _PrivilegeBoard extends StatefulWidget {

View File

@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -841,13 +842,7 @@ class ClientInfo extends StatelessWidget {
flex: -1,
child: Padding(
padding: const EdgeInsets.only(right: 12),
child: CircleAvatar(
backgroundColor: str2color(
client.name,
Theme.of(context).brightness == Brightness.light
? 255
: 150),
child: Text(client.name[0])))),
child: _buildAvatar(context))),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -860,6 +855,31 @@ class ClientInfo extends StatelessWidget {
),
]));
}
Widget _buildAvatar(BuildContext context) {
final avatar = client.avatar.trim();
if (avatar.isNotEmpty) {
if (avatar.startsWith('data:image/')) {
final comma = avatar.indexOf(',');
if (comma > 0) {
try {
return CircleAvatar(
backgroundImage: MemoryImage(base64Decode(avatar.substring(comma + 1))),
);
} catch (_) {}
}
} else if (avatar.startsWith('http://') || avatar.startsWith('https://')) {
return CircleAvatar(backgroundImage: NetworkImage(avatar));
}
}
// Show character as before if no avatar
return CircleAvatar(
backgroundColor: str2color(
client.name,
Theme.of(context).brightness == Brightness.light ? 255 : 150),
child: Text(client.name[0]),
);
}
}
void androidChannelInit() {

View File

@@ -820,6 +820,7 @@ class Client {
bool isTerminal = false;
String portForward = "";
String name = "";
String avatar = "";
String peerId = ""; // peer user's id,show at app
bool keyboard = false;
bool clipboard = false;
@@ -847,6 +848,7 @@ class Client {
isTerminal = json['is_terminal'] ?? false;
portForward = json['port_forward'];
name = json['name'];
avatar = json['avatar'] ?? '';
peerId = json['peer_id'];
keyboard = json['keyboard'];
clipboard = json['clipboard'];
@@ -870,6 +872,7 @@ class Client {
data['is_terminal'] = isTerminal;
data['port_forward'] = portForward;
data['name'] = name;
data['avatar'] = avatar;
data['peer_id'] = peerId;
data['keyboard'] = keyboard;
data['clipboard'] = clipboard;

View File

@@ -17,6 +17,7 @@ bool refreshingUser = false;
class UserModel {
final RxString userName = ''.obs;
final RxString displayName = ''.obs;
final RxString avatar = ''.obs;
final RxBool isAdmin = false.obs;
final RxString networkError = ''.obs;
bool get isLogin => userName.isNotEmpty;
@@ -87,6 +88,7 @@ class UserModel {
}
final user = UserPayload.fromJson(data);
user.avatar = _resolveAvatar(user.avatar, url);
_parseAndUpdateUser(user);
} catch (e) {
debugPrint('Failed to refreshCurrentUser: $e');
@@ -114,6 +116,7 @@ class UserModel {
if (userInfo != null) {
userName.value = (userInfo['name'] ?? '').toString();
displayName.value = (userInfo['display_name'] ?? '').toString();
avatar.value = (userInfo['avatar'] ?? '').toString();
}
}
@@ -126,13 +129,16 @@ class UserModel {
}
userName.value = '';
displayName.value = '';
avatar.value = '';
}
_parseAndUpdateUser(UserPayload user) {
userName.value = user.name;
displayName.value = user.displayName;
avatar.value = user.avatar;
isAdmin.value = user.isAdmin;
bind.mainSetLocalOption(key: 'user_info', value: jsonEncode(user));
_updateLocalUserInfo();
if (isWeb) {
// ugly here, tmp solution
bind.mainSetLocalOption(key: 'verifier', value: user.verifier ?? '');