Skip to content

Commit 28eb81a

Browse files
committed
feat(windows): support device removal from devices page
1 parent 3862cc5 commit 28eb81a

3 files changed

Lines changed: 146 additions & 0 deletions

File tree

clipSync-windows/ClipSync.WPF/Core/SyncEngine.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,31 @@ public async Task RequestDeviceListAsync()
468468
await _webSocketClient.SendAsync(message);
469469
}
470470

471+
public async Task<bool> UnregisterDeviceAsync(string deviceId)
472+
{
473+
if (_httpClient == null)
474+
{
475+
ErrorOccurred?.Invoke("Device service is not ready");
476+
return false;
477+
}
478+
479+
if (string.IsNullOrWhiteSpace(deviceId))
480+
{
481+
ErrorOccurred?.Invoke("Device ID is required");
482+
return false;
483+
}
484+
485+
var result = await _httpClient.DeleteDeviceAsync(deviceId);
486+
if (!result.Success)
487+
{
488+
ErrorOccurred?.Invoke(result.Error ?? "Failed to unregister device");
489+
return false;
490+
}
491+
492+
AppLogger.Info("SyncEngine", $"设备注销成功: device_id={deviceId}");
493+
return true;
494+
}
495+
471496
public async Task<List<Network.ClipboardItem>> GetLocalHistoryAsync(int limit = 50)
472497
{
473498
if (_database == null) return new List<Network.ClipboardItem>();

clipSync-windows/ClipSync.WPF/MainWindow.xaml.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,10 +827,64 @@ private Border CreateDeviceCard(Network.Device device)
827827
Grid.SetColumn(statusBadge, 1);
828828
layout.Children.Add(statusBadge);
829829

830+
var isCurrentDevice = string.Equals(
831+
device.DeviceId,
832+
_settingsManager.Settings.DeviceId,
833+
StringComparison.OrdinalIgnoreCase);
834+
835+
var removeButton = new Button
836+
{
837+
Content = isCurrentDevice ? "Current Device" : "Remove",
838+
Margin = new Thickness(0, 44, 0, 0),
839+
Padding = new Thickness(12, 6, 12, 6),
840+
MinWidth = 110,
841+
HorizontalAlignment = HorizontalAlignment.Right,
842+
VerticalAlignment = VerticalAlignment.Top,
843+
Style = FindResource("SecondaryButtonStyle") as Style,
844+
IsEnabled = !isCurrentDevice
845+
};
846+
removeButton.Click += async (s, e) => await OnUnregisterDeviceAsync(device);
847+
Grid.SetColumn(removeButton, 1);
848+
layout.Children.Add(removeButton);
849+
830850
card.Child = layout;
831851
return card;
832852
}
833853

854+
private async Task OnUnregisterDeviceAsync(Network.Device device)
855+
{
856+
if (string.IsNullOrWhiteSpace(device.DeviceId))
857+
{
858+
MessageBox.Show("设备 ID 不可用,无法移除该设备。", "无法移除设备", MessageBoxButton.OK, MessageBoxImage.Warning);
859+
return;
860+
}
861+
862+
if (string.Equals(device.DeviceId, _settingsManager.Settings.DeviceId, StringComparison.OrdinalIgnoreCase))
863+
{
864+
MessageBox.Show("当前这台设备不能在这里移除,请先在其他设备上操作。", "当前设备", MessageBoxButton.OK, MessageBoxImage.Information);
865+
return;
866+
}
867+
868+
var result = MessageBox.Show(
869+
$"确定要移除设备“{GetDeviceDisplayName(device)}”吗?该设备将需要重新登录后才能继续同步。",
870+
"移除设备",
871+
MessageBoxButton.YesNo,
872+
MessageBoxImage.Warning);
873+
874+
if (result != MessageBoxResult.Yes)
875+
{
876+
return;
877+
}
878+
879+
var success = await _syncEngine.UnregisterDeviceAsync(device.DeviceId);
880+
if (!success)
881+
{
882+
return;
883+
}
884+
885+
await RefreshDeviceListAsync(force: true);
886+
}
887+
834888
private void UpdateHomeSummary()
835889
{
836890
if (_homeView == null)

clipSync-windows/ClipSync.WPF/Network/HttpClient.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ public class AuthResult
1717
public long ExpiresAt { get; set; }
1818
}
1919

20+
public class OperationResult
21+
{
22+
public bool Success { get; set; }
23+
public string? Error { get; set; }
24+
}
25+
2026
public class HttpClient
2127
{
2228
private readonly SettingsManager _settingsManager;
@@ -187,5 +193,66 @@ public async Task<AuthResult> RefreshTokenAsync()
187193
};
188194
}
189195
}
196+
197+
public async Task<OperationResult> DeleteDeviceAsync(string deviceId)
198+
{
199+
var httpUrl = _settingsManager.Settings.HttpUrl;
200+
var url = $"{httpUrl}/api/v1/devices/{deviceId}";
201+
202+
var request = new HttpRequestMessage(HttpMethod.Delete, url);
203+
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(
204+
"Bearer", _settingsManager.Settings.Token);
205+
206+
try
207+
{
208+
AppLogger.Info("HttpClient", $"开始注销设备请求: url={url}, device_id={deviceId}");
209+
var response = await _httpClient.SendAsync(request);
210+
var responseJson = await response.Content.ReadAsStringAsync();
211+
212+
if (response.IsSuccessStatusCode)
213+
{
214+
AppLogger.Info("HttpClient", $"注销设备成功: device_id={deviceId}");
215+
return new OperationResult
216+
{
217+
Success = true
218+
};
219+
}
220+
221+
var error = TryParseError(responseJson) ?? "Device unregister failed";
222+
AppLogger.Warn("HttpClient", $"注销设备失败: status={(int)response.StatusCode}, device_id={deviceId}, error={error}");
223+
return new OperationResult
224+
{
225+
Success = false,
226+
Error = error
227+
};
228+
}
229+
catch (Exception ex)
230+
{
231+
AppLogger.Error("HttpClient", $"注销设备异常: device_id={deviceId}, url={url}", ex);
232+
return new OperationResult
233+
{
234+
Success = false,
235+
Error = $"Connection error: {ex.Message}"
236+
};
237+
}
238+
}
239+
240+
private static string? TryParseError(string responseJson)
241+
{
242+
if (string.IsNullOrWhiteSpace(responseJson))
243+
{
244+
return null;
245+
}
246+
247+
try
248+
{
249+
var error = JObject.Parse(responseJson);
250+
return error.Value<string>("error");
251+
}
252+
catch
253+
{
254+
return null;
255+
}
256+
}
190257
}
191258
}

0 commit comments

Comments
 (0)