Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ internal fun AccountSettingsDevicesSection(
)
Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
Text(
device.name.ifBlank { device.id },
device.displayName,
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
maxLines = 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ private fun DeviceListCard(
)
Column(verticalArrangement = Arrangement.spacedBy(3.dp), modifier = Modifier.weight(1f)) {
Text(
device.name.ifBlank { device.id },
device.displayName,
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
maxLines = 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ internal fun MobileScreen(onAccountRestored: (Boolean) -> Unit = {}) {
createDevices = readyAccount?.devices.orEmpty().map { device ->
com.openbitfun.mobile.app.ui.remote.CreateDeviceChoice(
id = device.id,
name = device.name,
name = device.displayName,
online = device.online,
selected = device.id == readyAccount?.selectedDeviceId,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ internal fun SidebarRemoteWorkspaceSection(
projectedDevices.take(visibleDeviceCount).forEach { device ->
val active = device.id == activeDeviceId
SidebarDeviceSelectorRow(
deviceName = device.name.ifBlank { device.id },
deviceName = device.displayName,
online = device.online,
selected = active,
loading = active && (remoteState is RemoteSessionUiState.Loading ||
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { MobileDesignGeometry, MobileDesignTypography } from '../../generated/MobileDesignTokens';
import { RemoteI18n } from '../../i18n/RemoteI18n';
import { CloudAccountDevice } from '../../services/CloudAccountClient';
import { accountDeviceDisplayName, CloudAccountDevice } from '../../services/CloudAccountClient';
import { AccountDeviceSelectionPolicy } from '../policy/AccountDeviceSelectionPolicy';
import { DeviceModelSettingsEntryPolicy } from '../policy/DeviceModelSettingsEntryPolicy';
import { DeviceDirectoryEntry, DeviceDirectoryState } from '../state/DeviceDirectoryState';
import {
CARD,
CONTENT_ON_ACTION,
Expand All @@ -20,6 +21,7 @@ import { DefaultAccountAvatar } from './DefaultAccountAvatar';
/** Account identity and device management shared by Settings and account entry. */
@ComponentV2
export struct AccountProfilePanel {
@Param directoryState: DeviceDirectoryState = new DeviceDirectoryState();
@Param accountUsername: string = '';
@Param accountAvatarUrl: string = '';
@Param accountUserId: string = '';
Expand All @@ -42,7 +44,6 @@ export struct AccountProfilePanel {
async (): Promise<CloudAccountDevice[]> => [];
@Event cloudSelectDevice: (device: CloudAccountDevice) => Promise<void> =
async (_device: CloudAccountDevice): Promise<void> => {};
@Local accountDevices: CloudAccountDevice[] = [];
@Local accountDevicesBusy: boolean = false;
@Local accountDevicesError: string = '';
@Local switchingDeviceId: string = '';
Expand Down Expand Up @@ -172,7 +173,7 @@ export struct AccountProfilePanel {
if (!this.isAccountAuthenticated()) {
Text(RemoteI18n.t('remote.settings.deviceLoginRequired'))
.fontSize(MobileDesignTypography.bodySmall.size).lineHeight(MobileDesignTypography.bodySmall.lineHeight).fontColor(MUTED).width('100%')
} else if (this.accountDevicesBusy && this.accountDevices.length === 0) {
} else if (this.accountDevicesBusy && this.directoryState.devices.length === 0) {
Text(RemoteI18n.t('remote.settings.deviceLoading'))
.fontSize(MobileDesignTypography.bodySmall.size).fontColor(MUTED).width('100%')
} else if (this.desktopDevices().length === 0) {
Expand All @@ -189,7 +190,7 @@ export struct AccountProfilePanel {
}
.width('100%')
}, (device: CloudAccountDevice): string =>
`${device.deviceId}:${device.online ? 'online' : 'offline'}:${device.lastSeenAt || 0}:${device.deviceName}` +
`${device.deviceId}:${device.online ? 'online' : 'offline'}:${device.lastSeenAt || 0}:${accountDeviceDisplayName(device)}` +
`:${this.isCurrentControlDevice(device) ? 'driven' : 'idle'}` +
`:${this.supportsDeviceModelDefaults ? 'model-defaults' : 'no-model-defaults'}`)
}
Expand Down Expand Up @@ -219,7 +220,7 @@ export struct AccountProfilePanel {
.width(28)
.height(28)
Column({ space: 2 }) {
Text(device.deviceName || device.deviceId)
Text(accountDeviceDisplayName(device))
.fontSize(MobileDesignTypography.titleSmall.size).fontWeight(FontWeight.Medium).fontColor(INK)
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
Text(this.deviceStatus(device))
Expand Down Expand Up @@ -349,7 +350,7 @@ export struct AccountProfilePanel {
this.accountDevicesBusy = true;
this.accountDevicesError = '';
try {
this.accountDevices = await this.cloudListDevices();
await this.cloudListDevices();
} catch (err) {
this.accountDevicesError = err instanceof Error ? err.message :
RemoteI18n.t('remote.settings.deviceLoadFailed');
Expand All @@ -358,8 +359,8 @@ export struct AccountProfilePanel {
}
}

private desktopDevices(): CloudAccountDevice[] {
return this.accountDevices.filter((device: CloudAccountDevice): boolean =>
private desktopDevices(): DeviceDirectoryEntry[] {
return this.directoryState.devices.filter((device: DeviceDirectoryEntry): boolean =>
device.deviceId !== this.deviceId && device.deviceName !== 'HarmonyOS Phone');
}

Expand Down Expand Up @@ -488,7 +489,7 @@ export struct AccountProfilePanel {

@Monitor('accountUserId')
private handleAccountUserChanged(): void {
if (this.isAccountAuthenticated() && this.accountDevices.length === 0) {
if (this.isAccountAuthenticated() && this.directoryState.devices.length === 0) {
this.refreshAccountDevices();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export struct AppSidebarSurface {

@ComponentV2
export struct AppSettingsSurface {
@Param deviceDirectoryState: DeviceDirectoryState = new DeviceDirectoryState();
@Param shellState: AppShellState = new AppShellState();
@Param remotePageState: RemotePageState = new RemotePageState();
@Param generalPageState: GeneralChatPageState = new GeneralChatPageState();
Expand All @@ -145,6 +146,7 @@ export struct AppSettingsSurface {
})
} else {
SettingsSheet({
directoryState: this.deviceDirectoryState,
sheetState: this.shellState.settingsSheet,
accountUsername: this.remotePageState.accountUsername,
accountAvatarUrl: this.remotePageState.accountAvatarUrl,
Expand Down Expand Up @@ -179,12 +181,14 @@ export struct AppSettingsSurface {

@ComponentV2
export struct AppConnectSurface {
@Param deviceDirectoryState: DeviceDirectoryState = new DeviceDirectoryState();
@Param remotePageState: RemotePageState = new RemotePageState();
@Param deviceId: string = '';
@Param actions: AppRootPresentationActions = emptyAppRootPresentationActions();

build() {
ConnectView({
directoryState: this.deviceDirectoryState,
remoteUrl: this.remotePageState.remoteUrl,
userId: this.remotePageState.userId,
statusText: this.remotePageState.conversation.statusText,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ export struct AppRootPresentation {

@Builder SettingsContent() {
AppSettingsSurface({
deviceDirectoryState: this.deviceDirectoryState,
shellState: this.shellState,
remotePageState: this.remotePageState,
generalPageState: this.generalPageState,
Expand All @@ -645,6 +646,7 @@ export struct AppRootPresentation {

@Builder ConnectContent() {
AppConnectSurface({
deviceDirectoryState: this.deviceDirectoryState,
remotePageState: this.remotePageState,
deviceId: this.deviceId,
actions: this.actions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { MobileDesignTypography } from '../../generated/MobileDesignTokens';
import { RemoteI18n } from '../../i18n/RemoteI18n';
import { CloudAccountDevice } from '../../services/CloudAccountClient';
import { accountDeviceDisplayName, CloudAccountDevice } from '../../services/CloudAccountClient';
import { ACCENT, CARD, STATUS_SUCCESS, INK, LINE, MUTED, PAGE_BG, STATUS_DANGER, SOFT } from './Theme';
import { AccountDeviceSelectionPolicy } from '../policy/AccountDeviceSelectionPolicy';
import { DeviceDirectoryEntry, DeviceDirectoryState } from '../state/DeviceDirectoryState';
import { NavigationBackButton } from './NavigationBackButton';

@ComponentV2
export struct ConnectAccountDevicePage {
@Param directoryState: DeviceDirectoryState = new DeviceDirectoryState();
@Param deviceId: string = '';
@Param controlTargetDeviceId: string = '';
@Param connectionState: string = 'idle';
Expand All @@ -15,7 +17,6 @@ export struct ConnectAccountDevicePage {
async (): Promise<CloudAccountDevice[]> => [];
@Event cloudSelectDevice: (device: CloudAccountDevice) => Promise<void> =
async (_device: CloudAccountDevice): Promise<void> => {};
@Local accountDevices: CloudAccountDevice[] = [];
@Local accountDevicesBusy: boolean = false;
@Local accountDevicesError: string = '';
@Local switchingDeviceId: string = '';
Expand Down Expand Up @@ -77,7 +78,7 @@ export struct ConnectAccountDevicePage {
}
.width('100%').height(38)

if (this.accountDevicesBusy && this.accountDevices.length === 0) {
if (this.accountDevicesBusy && this.directoryState.devices.length === 0) {
Column() {
this.AccountDeviceSkeletonRow()
this.AccountDeviceSkeletonRow()
Expand All @@ -95,7 +96,7 @@ export struct ConnectAccountDevicePage {
ForEach(this.desktopDevices(), (device: CloudAccountDevice) => {
this.AccountConnectDeviceRow(device)
}, (device: CloudAccountDevice): string =>
`${device.deviceId}:${device.online ? 'online' : 'offline'}:${device.lastSeenAt || 0}:${device.deviceName}`)
`${device.deviceId}:${device.online ? 'online' : 'offline'}:${device.lastSeenAt || 0}:${accountDeviceDisplayName(device)}`)
}
.width('100%')
}
Expand Down Expand Up @@ -127,7 +128,7 @@ export struct ConnectAccountDevicePage {
SymbolGlyph($r('sys.symbol.desktop'))
.fontSize(22).fontColor([MUTED]).width(26).height(24).opacity(device.online ? 0.68 : 0.38)
Column({ space: 3 }) {
Text(device.deviceName || device.deviceId)
Text(accountDeviceDisplayName(device))
.fontSize(MobileDesignTypography.titleSmall.size).fontWeight(FontWeight.Medium).fontColor(INK)
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
Text(this.accountDeviceStatus(device))
Expand Down Expand Up @@ -171,7 +172,7 @@ export struct ConnectAccountDevicePage {
this.accountDevicesBusy = true;
this.accountDevicesError = '';
try {
this.accountDevices = await this.cloudListDevices();
await this.cloudListDevices();
} catch (err) {
this.accountDevicesError = err instanceof Error ? err.message :
RemoteI18n.t('remote.settings.deviceLoadFailed');
Expand All @@ -180,8 +181,8 @@ export struct ConnectAccountDevicePage {
}
}

private desktopDevices(): CloudAccountDevice[] {
return this.accountDevices.filter((device: CloudAccountDevice): boolean =>
private desktopDevices(): DeviceDirectoryEntry[] {
return this.directoryState.devices.filter((device: DeviceDirectoryEntry): boolean =>
device.deviceId !== this.deviceId && device.deviceName !== 'HarmonyOS Phone');
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { MobileDesignTypography } from '../../generated/MobileDesignTokens';
import { RemoteI18n } from '../../i18n/RemoteI18n';
import { CloudAccountDevice } from '../../services/CloudAccountClient';
import { DeviceDirectoryState } from '../state/DeviceDirectoryState';
import { ConnectAccountDevicePage } from './ConnectAccountDevicePage';
import { AppActionButton, AppActionStyle } from './AppActionButton';
import { NavigationBackButton } from './NavigationBackButton';
Expand All @@ -21,6 +22,7 @@ import { WindowInsetsBinding } from '../../services/WindowSystemBarService';
*/
@ComponentV2
export struct ConnectView {
@Param directoryState: DeviceDirectoryState = new DeviceDirectoryState();
@Param remoteUrl: string = '';
@Param userId: string = '';
@Param statusText: string = RemoteI18n.t('status.waitingConnection');
Expand Down Expand Up @@ -72,6 +74,7 @@ export struct ConnectView {
@Builder
AccountDeviceSelectionPage() {
ConnectAccountDevicePage({
directoryState: this.directoryState,
deviceId: this.deviceId,
controlTargetDeviceId: this.controlTargetDeviceId,
connectionState: this.connectionState,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { MobileDesignGeometry, MobileDesignTypography } from '../../generated/MobileDesignTokens';
import { RemoteI18n } from '../../i18n/RemoteI18n';
import { SettingsSheetState } from '../state/SettingsSheetState';
import { DeviceDirectoryState } from '../state/DeviceDirectoryState';
import { DeviceModelDefaultsRequestPath } from '../state/DeviceModelDefaultsSheetState';
import { CARD, INK, LINE, MUTED, PAGE_BG } from './Theme';
import { CurrentControlSection } from './CurrentControlSection';
Expand All @@ -17,6 +18,7 @@ import { RemotePermissionSection } from './RemotePermissionSection';

@ComponentV2
export struct SettingsSheet {
@Param directoryState: DeviceDirectoryState = new DeviceDirectoryState();
@Param sheetState: SettingsSheetState = new SettingsSheetState();
@Param accountUsername: string = '';
@Param accountAvatarUrl: string = '';
Expand Down Expand Up @@ -162,6 +164,7 @@ export struct SettingsSheet {
Scroll() {
Column({ space: 0 }) {
AccountProfilePanel({
directoryState: this.directoryState,
accountUsername: this.accountUsername,
accountAvatarUrl: this.accountAvatarUrl,
accountUserId: this.authenticatedUserId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export struct SidebarWorkspaceSection {
tint: SIDEBAR_INK
})
.hitTestBehavior(HitTestMode.None)
Text(entry.deviceName)
Text(entry.displayName)
.fontSize(MobileDesignTypography.bodyMedium.size)
.fontWeight(this.isSelected(entry) ? FontWeight.Bold : FontWeight.Regular)
.fontColor(SIDEBAR_INK)
Expand Down Expand Up @@ -200,7 +200,7 @@ export struct SidebarWorkspaceSection {
.alignItems(VerticalAlign.Center)
.backgroundColor(this.isSelected(entry) ? SIDEBAR_SELECTION : TRANSPARENT)
.borderRadius(10)
.accessibilityText(`${entry.deviceName}, ${this.isActiveDevice(entry.deviceId) ? ConnectionStatusPresenter.label(this.connectionState) :
.accessibilityText(`${entry.displayName}, ${this.isActiveDevice(entry.deviceId) ? ConnectionStatusPresenter.label(this.connectionState) :
RemoteI18n.t(entry.online ? 'sidebar.deviceOnline' : 'sidebar.deviceOffline')}`)
.enabled(this.canUseDevice(entry) || this.canRetryDevice(entry))
.onClick(() => {
Expand Down Expand Up @@ -342,7 +342,7 @@ export struct SidebarWorkspaceSection {
tools: this.workspaceTools,
canBrowse: this.isActiveDevice(this.workspacePickerDevice().deviceId),
onBrowse: this.onWorkspaceTools,
deviceName: this.workspacePickerDevice().deviceName,
deviceName: this.workspacePickerDevice().displayName,
savedConnections: this.workspacePickerDevice().deviceId === this.savedConnectionsTargetId ? this.savedConnections : [],
workspaces: this.workspacePickerDevice().catalogSource.length > 0 ?
this.workspacePickerDevice().recentWorkspaces : this.workspacesForDevice(this.workspacePickerDevice()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,13 @@ function gallery(): MiniAppSummary[] {
return apps;
}

function settingsDevices(): CloudAccountDevice[] {
const devices: CloudAccountDevice[] = [];
function settingsDevices(): DeviceDirectoryEntry[] {
const devices: DeviceDirectoryEntry[] = [];
for (let index = 0; index < SETTINGS_DEVICES; index++) {
const device = index + 1;
devices.push({
deviceId: `account-desktop-${device}`,
deviceName: `Account desktop ${device}`,
online: device % 3 !== 0,
lastSeenAt: device
});
const entry = emptyDeviceDirectoryEntry(`account-desktop-${device}`, `Account desktop ${device}`, device % 3 !== 0);
entry.lastSeenAt = device;
devices.push(entry);
}
return devices;
}
Expand Down Expand Up @@ -351,7 +348,9 @@ export struct ImmersiveBottomPreview {
// be the greyed state. This fixture is the connected desktop's, so it
// advertises the command and the sheet shows the entry it really shows.
this.remotePageState.setHostCapabilities([REMOTE_CAPABILITY_DEVICE_MODEL_DEFAULTS_V1]);
this.previewActions = previewActionsWithDevices(settingsDevices());
const devices = settingsDevices();
this.directoryState.replace(devices);
this.previewActions = previewActionsWithDevices(devices);
return;
}
if (scenarioId.endsWith('-connect')) {
Expand Down Expand Up @@ -469,6 +468,7 @@ export struct ImmersiveBottomPreview {
@Builder
private Settings() {
AppSettingsSurface({
deviceDirectoryState: this.directoryState,
shellState: this.shellState,
remotePageState: this.remotePageState,
actions: this.previewActions
Expand All @@ -478,6 +478,7 @@ export struct ImmersiveBottomPreview {
@Builder
private Connect() {
AppConnectSurface({
deviceDirectoryState: this.directoryState,
remotePageState: this.remotePageState,
actions: this.previewActions
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,32 @@ export class WorkspaceDirectoryState {
}

/**
* One account desktop in the sidebar. Nested @Trace fields are what
* One account desktop shared by the sidebar, settings and device picker. Nested @Trace fields are what
* SidebarDeviceGroup actually watches — replacing a plain object on
* the parent array does not update a reused ForEach child.
*/
@ObservedV2
export class DeviceDirectoryEntry {
@Trace deviceId: string = '';
@Trace deviceName: string = '';
@Trace deviceAlias: string | null | undefined = undefined;
@Trace deviceKind: string | undefined = undefined;
@Trace lastSeenAt: number | undefined = undefined;
@Trace compatible: boolean | undefined = undefined;
@Trace online: boolean = false;
@Trace status: DeviceDirectoryStatus = 'idle';
@Trace errorText: string = '';
@Trace workspaces: RecentWorkspaceEntry[] = [];
@Trace catalogSource: string = '';
@Trace recentWorkspaces: RecentWorkspaceEntry[] = [];
@Trace sessions: RemoteSession[] = [];

get displayName(): string {
return this.deviceAlias ?? (this.deviceName || this.deviceId);
}
}

/** Snapshot of every account desktop the sidebar can observe. */
/** Shared observable directory of account desktops. */
@ObservedV2
export class DeviceDirectoryState {
@Trace refreshing: boolean = false;
Expand Down
Loading
Loading