Skip to content
Open

sync #46

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/flutter-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ env:
# 2. Update the `VCPKG_COMMIT_ID` in `ci.yml` and `playground.yml`.
VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b"
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" # 2025.01.13, got "/opt/artifacts/vcpkg/vcpkg: No such file or directory" with latest version
VERSION: "1.4.5"
VERSION: "1.4.6"
NDK_VERSION: "r27c"
#signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/playground.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ env:
TAG_NAME: "nightly"
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b"
VERSION: "1.4.5"
VERSION: "1.4.6"
NDK_VERSION: "r26d"
#signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/winget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ jobs:
- uses: vedantmgoyal9/winget-releaser@main
with:
identifier: RustDesk.RustDesk
version: "1.4.5"
release-tag: "1.4.5"
version: "1.4.6"
release-tag: "1.4.6"
token: ${{ secrets.WINGET_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
.vscode
.idea
.DS_Store
.env
libsciter-gtk.so
src/ui/inline.rs
extractor
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rustdesk"
version = "1.4.5"
version = "1.4.6"
authors = ["rustdesk <info@rustdesk.com>"]
edition = "2021"
build= "build.rs"
Expand Down
2 changes: 1 addition & 1 deletion appimage/AppImageBuilder-aarch64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ AppDir:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.4.5
version: 1.4.6
exec: usr/share/rustdesk/rustdesk
exec_args: $@
apt:
Expand Down
2 changes: 1 addition & 1 deletion appimage/AppImageBuilder-x86_64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ AppDir:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.4.5
version: 1.4.6
exec: usr/share/rustdesk/rustdesk
exec_args: $@
apt:
Expand Down
40 changes: 40 additions & 0 deletions flutter/lib/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4118,3 +4118,43 @@ String mouseButtonsToPeer(int buttons) {
return '';
}
}

/// Build an avatar widget from an avatar URL or data URI string.
/// Returns [fallback] if avatar is empty or cannot be decoded.
/// [borderRadius] defaults to [size]/2 (circle).
Widget? buildAvatarWidget({
required String avatar,
required double size,
double? borderRadius,
Widget? fallback,
}) {
final trimmed = avatar.trim();
if (trimmed.isEmpty) return fallback;

ImageProvider? imageProvider;
if (trimmed.startsWith('data:image/')) {
final comma = trimmed.indexOf(',');
if (comma > 0) {
try {
imageProvider = MemoryImage(base64Decode(trimmed.substring(comma + 1)));
} catch (_) {}
}
} else if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
imageProvider = NetworkImage(trimmed);
}

if (imageProvider == null) return fallback;

final radius = borderRadius ?? size / 2;
return ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: Image(
image: imageProvider,
width: size,
height: size,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) =>
fallback ?? SizedBox.shrink(),
),
);
}
3 changes: 3 additions & 0 deletions flutter/lib/common/hbbs/hbbs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ enum UserStatus { kDisabled, kNormal, kUnverified }
class UserPayload {
String name = '';
String displayName = '';
String avatar = '';
String email = '';
String note = '';
String? verifier;
Expand All @@ -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'],
Expand All @@ -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
Expand Down
74 changes: 55 additions & 19 deletions flutter/lib/desktop/pages/desktop_setting_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,7 @@ class _GeneralState extends State<_General> {
}

Widget other() {
final showAutoUpdate =
isWindows && bind.mainIsInstalled() && !bind.isCustomClient();
final showAutoUpdate = isWindows && bind.mainIsInstalled();
final children = <Widget>[
if (!isWeb && !bind.isIncomingOnly())
_OptionCheckBox(context, 'Confirm before closing multiple tabs',
Expand Down Expand Up @@ -2027,28 +2026,65 @@ 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() {
// Resolve relative avatar path at display time
final avatar =
bind.mainResolveAvatarUrl(avatar: gFFI.userModel.avatar.value);
return buildAvatarWidget(
avatar: avatar,
size: 44,
);
}
}

class _Checkbox extends StatefulWidget {
Expand Down
47 changes: 30 additions & 17 deletions flutter/lib/desktop/pages/server_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -462,23 +462,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,
Expand Down Expand Up @@ -582,6 +566,35 @@ class _CmHeaderState extends State<_CmHeader>

@override
bool get wantKeepAlive => true;

Widget _buildClientAvatar() {
return buildAvatarWidget(
avatar: client.avatar,
size: 70,
borderRadius: 15,
fallback: _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.isNotEmpty ? client.name[0] : '?',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 55,
),
),
);
}
}

class _PrivilegeBoard extends StatefulWidget {
Expand Down
22 changes: 15 additions & 7 deletions flutter/lib/mobile/pages/server_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -841,13 +841,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,
Expand All @@ -860,6 +854,20 @@ class ClientInfo extends StatelessWidget {
),
]));
}

Widget _buildAvatar(BuildContext context) {
final fallback = CircleAvatar(
backgroundColor: str2color(
client.name,
Theme.of(context).brightness == Brightness.light ? 255 : 150),
child: Text(client.name.isNotEmpty ? client.name[0] : '?'),
);
return buildAvatarWidget(
avatar: client.avatar,
size: 40,
fallback: fallback,
)!;
}
}

void androidChannelInit() {
Expand Down
10 changes: 9 additions & 1 deletion flutter/lib/mobile/pages/settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,15 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
title: Obx(() => Text(gFFI.userModel.userName.value.isEmpty
? translate('Login')
: '${translate('Logout')} (${gFFI.userModel.accountLabelWithHandle})')),
leading: Icon(Icons.person),
leading: Obx(() {
final avatar = bind.mainResolveAvatarUrl(
avatar: gFFI.userModel.avatar.value);
return buildAvatarWidget(
avatar: avatar,
size: 40,
) ??
Icon(Icons.person);
}),
onPressed: (context) {
if (gFFI.userModel.userName.value.isEmpty) {
loginDialog();
Expand Down
Loading