mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-21 03:13:18 +03:00
- UI display: display_name first
- Fallback: name
- Technical identity: still name
### What changed
- Added account display helpers and display_name state in user model:
- flutter/lib/models/user_model.dart:16
- Account/logout label now uses display_name (@name) when both exist:
- flutter/lib/mobile/pages/settings_page.dart:689
- flutter/lib/desktop/pages/desktop_setting_page.dart:2016
- flutter/lib/desktop/pages/desktop_setting_page.dart:2135
- Desktop Account info now shows both when applicable:
- Display Name: ...
- Username: ...
- flutter/lib/desktop/pages/desktop_setting_page.dart:2039
- Previously done group-list behavior remains:
- group user list displays display_name with name fallback
- flutter/lib/common/widgets/my_group.dart:187
- Persistence path for display_name remains enabled (including group cache/submodule field):
- libs/hbb_common/src/config.rs:2347
- src/client.rs:2630
- LoginRequest.my_name now resolves as:
1. OPTION_DISPLAY_NAME (manual override)
2. user_info.display_name
3. user_info.name
4. OS username fallback
This commit is contained in:
@@ -25,6 +25,7 @@ enum UserStatus { kDisabled, kNormal, kUnverified }
|
|||||||
// Is all the fields of the user needed?
|
// Is all the fields of the user needed?
|
||||||
class UserPayload {
|
class UserPayload {
|
||||||
String name = '';
|
String name = '';
|
||||||
|
String displayName = '';
|
||||||
String email = '';
|
String email = '';
|
||||||
String note = '';
|
String note = '';
|
||||||
String? verifier;
|
String? verifier;
|
||||||
@@ -33,6 +34,7 @@ class UserPayload {
|
|||||||
|
|
||||||
UserPayload.fromJson(Map<String, dynamic> json)
|
UserPayload.fromJson(Map<String, dynamic> json)
|
||||||
: name = json['name'] ?? '',
|
: name = json['name'] ?? '',
|
||||||
|
displayName = json['display_name'] ?? '',
|
||||||
email = json['email'] ?? '',
|
email = json['email'] ?? '',
|
||||||
note = json['note'] ?? '',
|
note = json['note'] ?? '',
|
||||||
verifier = json['verifier'],
|
verifier = json['verifier'],
|
||||||
@@ -46,6 +48,7 @@ class UserPayload {
|
|||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final Map<String, dynamic> map = {
|
final Map<String, dynamic> map = {
|
||||||
'name': name,
|
'name': name,
|
||||||
|
'display_name': displayName,
|
||||||
'status': status == UserStatus.kDisabled
|
'status': status == UserStatus.kDisabled
|
||||||
? 0
|
? 0
|
||||||
: status == UserStatus.kUnverified
|
: status == UserStatus.kUnverified
|
||||||
@@ -58,9 +61,14 @@ class UserPayload {
|
|||||||
Map<String, dynamic> toGroupCacheJson() {
|
Map<String, dynamic> toGroupCacheJson() {
|
||||||
final Map<String, dynamic> map = {
|
final Map<String, dynamic> map = {
|
||||||
'name': name,
|
'name': name,
|
||||||
|
'display_name': displayName,
|
||||||
};
|
};
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get displayNameOrName {
|
||||||
|
return displayName.trim().isEmpty ? name : displayName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PeerPayload {
|
class PeerPayload {
|
||||||
|
|||||||
@@ -158,9 +158,9 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
return Obx(() {
|
return Obx(() {
|
||||||
final userItems = gFFI.groupModel.users.where((p0) {
|
final userItems = gFFI.groupModel.users.where((p0) {
|
||||||
if (searchAccessibleItemNameText.isNotEmpty) {
|
if (searchAccessibleItemNameText.isNotEmpty) {
|
||||||
return p0.name
|
final search = searchAccessibleItemNameText.value.toLowerCase();
|
||||||
.toLowerCase()
|
return p0.name.toLowerCase().contains(search) ||
|
||||||
.contains(searchAccessibleItemNameText.value.toLowerCase());
|
p0.displayNameOrName.toLowerCase().contains(search);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}).toList();
|
}).toList();
|
||||||
@@ -187,6 +187,7 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
|
|
||||||
Widget _buildUserItem(UserPayload user) {
|
Widget _buildUserItem(UserPayload user) {
|
||||||
final username = user.name;
|
final username = user.name;
|
||||||
|
final displayName = user.displayNameOrName;
|
||||||
return InkWell(onTap: () {
|
return InkWell(onTap: () {
|
||||||
isSelectedDeviceGroup.value = false;
|
isSelectedDeviceGroup.value = false;
|
||||||
if (selectedAccessibleItemName.value != username) {
|
if (selectedAccessibleItemName.value != username) {
|
||||||
@@ -229,7 +230,7 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
).marginOnly(right: 4),
|
).marginOnly(right: 4),
|
||||||
if (isMe) Flexible(child: Text(username)),
|
if (isMe) Flexible(child: Text(displayName)),
|
||||||
if (isMe)
|
if (isMe)
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -246,7 +247,7 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!isMe) Expanded(child: Text(username)),
|
if (!isMe) Expanded(child: Text(displayName)),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(vertical: 4),
|
).paddingSymmetric(vertical: 4),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2016,7 +2016,9 @@ class _AccountState extends State<_Account> {
|
|||||||
|
|
||||||
Widget accountAction() {
|
Widget accountAction() {
|
||||||
return Obx(() => _Button(
|
return Obx(() => _Button(
|
||||||
gFFI.userModel.userName.value.isEmpty ? 'Login' : 'Logout',
|
gFFI.userModel.userName.value.isEmpty
|
||||||
|
? 'Login'
|
||||||
|
: 'Logout (${gFFI.userModel.accountLabelWithHandle})',
|
||||||
() => {
|
() => {
|
||||||
gFFI.userModel.userName.value.isEmpty
|
gFFI.userModel.userName.value.isEmpty
|
||||||
? loginDialog()
|
? loginDialog()
|
||||||
@@ -2037,6 +2039,9 @@ class _AccountState extends State<_Account> {
|
|||||||
offstage: gFFI.userModel.userName.value.isEmpty,
|
offstage: gFFI.userModel.userName.value.isEmpty,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
if (gFFI.userModel.displayName.value.trim().isNotEmpty &&
|
||||||
|
gFFI.userModel.displayName.value != gFFI.userModel.userName.value)
|
||||||
|
text('Display Name', gFFI.userModel.displayName.value),
|
||||||
text('Username', gFFI.userModel.userName.value),
|
text('Username', gFFI.userModel.userName.value),
|
||||||
// text('Group', gFFI.groupModel.groupName.value),
|
// text('Group', gFFI.groupModel.groupName.value),
|
||||||
],
|
],
|
||||||
@@ -2130,7 +2135,9 @@ class _PluginState extends State<_Plugin> {
|
|||||||
|
|
||||||
Widget accountAction() {
|
Widget accountAction() {
|
||||||
return Obx(() => _Button(
|
return Obx(() => _Button(
|
||||||
gFFI.userModel.userName.value.isEmpty ? 'Login' : 'Logout',
|
gFFI.userModel.userName.value.isEmpty
|
||||||
|
? 'Login'
|
||||||
|
: 'Logout (${gFFI.userModel.accountLabelWithHandle})',
|
||||||
() => {
|
() => {
|
||||||
gFFI.userModel.userName.value.isEmpty
|
gFFI.userModel.userName.value.isEmpty
|
||||||
? loginDialog()
|
? loginDialog()
|
||||||
|
|||||||
@@ -688,7 +688,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
SettingsTile(
|
SettingsTile(
|
||||||
title: Obx(() => Text(gFFI.userModel.userName.value.isEmpty
|
title: Obx(() => Text(gFFI.userModel.userName.value.isEmpty
|
||||||
? translate('Login')
|
? translate('Login')
|
||||||
: '${translate('Logout')} (${gFFI.userModel.userName.value})')),
|
: '${translate('Logout')} (${gFFI.userModel.accountLabelWithHandle})')),
|
||||||
leading: Icon(Icons.person),
|
leading: Icon(Icons.person),
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
if (gFFI.userModel.userName.value.isEmpty) {
|
if (gFFI.userModel.userName.value.isEmpty) {
|
||||||
|
|||||||
@@ -16,9 +16,23 @@ bool refreshingUser = false;
|
|||||||
|
|
||||||
class UserModel {
|
class UserModel {
|
||||||
final RxString userName = ''.obs;
|
final RxString userName = ''.obs;
|
||||||
|
final RxString displayName = ''.obs;
|
||||||
final RxBool isAdmin = false.obs;
|
final RxBool isAdmin = false.obs;
|
||||||
final RxString networkError = ''.obs;
|
final RxString networkError = ''.obs;
|
||||||
bool get isLogin => userName.isNotEmpty;
|
bool get isLogin => userName.isNotEmpty;
|
||||||
|
String get displayNameOrUserName =>
|
||||||
|
displayName.value.trim().isEmpty ? userName.value : displayName.value;
|
||||||
|
String get accountLabelWithHandle {
|
||||||
|
final username = userName.value.trim();
|
||||||
|
if (username.isEmpty) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
final preferred = displayName.value.trim();
|
||||||
|
if (preferred.isEmpty || preferred == username) {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
return '$preferred (@$username)';
|
||||||
|
}
|
||||||
WeakReference<FFI> parent;
|
WeakReference<FFI> parent;
|
||||||
|
|
||||||
UserModel(this.parent) {
|
UserModel(this.parent) {
|
||||||
@@ -98,7 +112,8 @@ class UserModel {
|
|||||||
_updateLocalUserInfo() {
|
_updateLocalUserInfo() {
|
||||||
final userInfo = getLocalUserInfo();
|
final userInfo = getLocalUserInfo();
|
||||||
if (userInfo != null) {
|
if (userInfo != null) {
|
||||||
userName.value = userInfo['name'];
|
userName.value = (userInfo['name'] ?? '').toString();
|
||||||
|
displayName.value = (userInfo['display_name'] ?? '').toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,10 +125,12 @@ class UserModel {
|
|||||||
await gFFI.groupModel.reset();
|
await gFFI.groupModel.reset();
|
||||||
}
|
}
|
||||||
userName.value = '';
|
userName.value = '';
|
||||||
|
displayName.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
_parseAndUpdateUser(UserPayload user) {
|
_parseAndUpdateUser(UserPayload user) {
|
||||||
userName.value = user.name;
|
userName.value = user.name;
|
||||||
|
displayName.value = user.displayName;
|
||||||
isAdmin.value = user.isAdmin;
|
isAdmin.value = user.isAdmin;
|
||||||
bind.mainSetLocalOption(key: 'user_info', value: jsonEncode(user));
|
bind.mainSetLocalOption(key: 'user_info', value: jsonEncode(user));
|
||||||
if (isWeb) {
|
if (isWeb) {
|
||||||
|
|||||||
Submodule libs/hbb_common updated: da339dca64...0b60b9ffa0
@@ -2630,10 +2630,12 @@ impl LoginConfigHandler {
|
|||||||
display_name =
|
display_name =
|
||||||
serde_json::from_str::<serde_json::Value>(&LocalConfig::get_option("user_info"))
|
serde_json::from_str::<serde_json::Value>(&LocalConfig::get_option("user_info"))
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
x.get("name")
|
x.get("display_name")
|
||||||
.map(|x| x.as_str().unwrap_or_default())
|
.and_then(|x| x.as_str())
|
||||||
|
.filter(|x| !x.is_empty())
|
||||||
|
.or_else(|| x.get("name").and_then(|x| x.as_str()))
|
||||||
|
.map(|x| x.to_owned())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_owned()
|
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ pub enum UserStatus {
|
|||||||
pub struct UserPayload {
|
pub struct UserPayload {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub display_name: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
pub email: Option<String>,
|
pub email: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub note: Option<String>,
|
pub note: Option<String>,
|
||||||
@@ -268,7 +270,12 @@ impl OidcSession {
|
|||||||
);
|
);
|
||||||
LocalConfig::set_option(
|
LocalConfig::set_option(
|
||||||
"user_info".to_owned(),
|
"user_info".to_owned(),
|
||||||
serde_json::json!({ "name": auth_body.user.name, "status": auth_body.user.status }).to_string(),
|
serde_json::json!({
|
||||||
|
"name": auth_body.user.name,
|
||||||
|
"display_name": auth_body.user.display_name,
|
||||||
|
"status": auth_body.user.status
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -358,6 +358,22 @@ function getUserName() {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAccountLabelWithHandle() {
|
||||||
|
try {
|
||||||
|
var user = JSON.parse(handler.get_local_option("user_info"));
|
||||||
|
var username = (user.name || '').trim();
|
||||||
|
if (!username) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
var displayName = (user.display_name || '').trim();
|
||||||
|
if (!displayName || displayName == username) {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
return displayName + " (@" + username + ")";
|
||||||
|
} catch(e) {}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
// Shared dialog functions
|
// Shared dialog functions
|
||||||
function open_custom_server_dialog() {
|
function open_custom_server_dialog() {
|
||||||
var configOptions = handler.get_options();
|
var configOptions = handler.get_options();
|
||||||
@@ -493,7 +509,7 @@ class MyIdMenu: Reactor.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderPop() {
|
function renderPop() {
|
||||||
var username = handler.get_local_option("access_token") ? getUserName() : '';
|
var accountLabel = handler.get_local_option("access_token") ? getAccountLabelWithHandle() : '';
|
||||||
return <popup>
|
return <popup>
|
||||||
<menu.context #config-options>
|
<menu.context #config-options>
|
||||||
{!disable_settings && <li #enable-keyboard><span>{svg_checkmark}</span>{translate('Enable keyboard/mouse')}</li>}
|
{!disable_settings && <li #enable-keyboard><span>{svg_checkmark}</span>{translate('Enable keyboard/mouse')}</li>}
|
||||||
@@ -521,8 +537,8 @@ class MyIdMenu: Reactor.Component {
|
|||||||
{!disable_settings && <DirectServer />}
|
{!disable_settings && <DirectServer />}
|
||||||
{!disable_settings && false && handler.using_public_server() && <li #allow-always-relay><span>{svg_checkmark}</span>{translate('Always connect via relay')}</li>}
|
{!disable_settings && false && handler.using_public_server() && <li #allow-always-relay><span>{svg_checkmark}</span>{translate('Always connect via relay')}</li>}
|
||||||
{!disable_change_id && handler.is_ok_change_id() ? <div .separator /> : ""}
|
{!disable_change_id && handler.is_ok_change_id() ? <div .separator /> : ""}
|
||||||
{!disable_account && (username ?
|
{!disable_account && (accountLabel ?
|
||||||
<li #logout>{translate('Logout')} ({username})</li> :
|
<li #logout>{translate('Logout')} ({accountLabel})</li> :
|
||||||
<li #login>{translate('Login')}</li>)}
|
<li #login>{translate('Login')}</li>)}
|
||||||
{!disable_change_id && !disable_settings && handler.is_ok_change_id() && key_confirmed && connect_status > 0 ? <li #change-id>{translate('Change ID')}</li> : ""}
|
{!disable_change_id && !disable_settings && handler.is_ok_change_id() && key_confirmed && connect_status > 0 ? <li #change-id>{translate('Change ID')}</li> : ""}
|
||||||
<div .separator />
|
<div .separator />
|
||||||
@@ -1430,6 +1446,9 @@ checkConnectStatus();
|
|||||||
|
|
||||||
function set_local_user_info(user) {
|
function set_local_user_info(user) {
|
||||||
var user_info = {name: user.name};
|
var user_info = {name: user.name};
|
||||||
|
if (user.display_name) {
|
||||||
|
user_info.display_name = user.display_name;
|
||||||
|
}
|
||||||
if (user.status) {
|
if (user.status) {
|
||||||
user_info.status = user.status;
|
user_info.status = user.status;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user