device: add copyToClipboard and fix regenerate token HTMX button
Phase 3: Complete device page functionality fixes - Add copyToClipboard function to device-management.ts: - Uses navigator.clipboard.writeText() for copying - Shows success/error toast notifications - Already exported in Alpine.store, now properly defined - Fix typo in devices.templ: change 'regerate-token' to 'regenerate-token' - Add HTMX support to RegenerateDeviceToken handler: - Returns HTML with reload script for HTMX requests - Preserves JSON response for API calls The regenerate token button now works via HTMX (hx-put) instead of Alpine.js, matching the pattern used elsewhere in the app.
This commit is contained in:
@@ -554,6 +554,11 @@ func (h *DeviceHandler) RegenerateDeviceToken(c *echo.Context) error {
|
|||||||
syncURLs["bookmarks"] = fmt.Sprintf("%s/api/sync/koreader/bookmarks", baseURL)
|
syncURLs["bookmarks"] = fmt.Sprintf("%s/api/sync/koreader/bookmarks", baseURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle HTMX requests - return HTML with page reload script
|
||||||
|
if c.Request().Header.Get("HX-Request") == "true" {
|
||||||
|
html := fmt.Sprintf(`<div class="text-green-500">Token regenerated! Reloading ... </div><script>window.location.reload();`)
|
||||||
|
return c.HTML(http.StatusOK, html)
|
||||||
|
}
|
||||||
return c.JSON(http.StatusOK, map[string]interface{}{
|
return c.JSON(http.StatusOK, map[string]interface{}{
|
||||||
"message": "Token regenerated successfully",
|
"message": "Token regenerated successfully",
|
||||||
"auth_token": newToken,
|
"auth_token": newToken,
|
||||||
|
|||||||
@@ -148,7 +148,8 @@ templ Devices(user User, devices []handlers.DeviceInfo, pendingRegistrations []P
|
|||||||
}
|
}
|
||||||
<!-- Regenerate Token Button -->
|
<!-- Regenerate Token Button -->
|
||||||
<button
|
<button
|
||||||
@click="regenerateDeviceToken('{ device.ID }', $event)"
|
hx-put={ "/api/devices/" + device.ID.String() + "/regenerate-token" }
|
||||||
|
hx-confirm="⚠️ Old token will immediately stop working. Regenerate anyway?"
|
||||||
class="w-full px-3 py-2 text-xs rounded border hover:opacity-80"
|
class="w-full px-3 py-2 text-xs rounded border hover:opacity-80"
|
||||||
style="border-color: var(--border); color: var(--text-secondary); background-color: var(--bg-primary);"
|
style="border-color: var(--border); color: var(--text-secondary); background-color: var(--bg-primary);"
|
||||||
>
|
>
|
||||||
|
|||||||
+204
-68
@@ -30,9 +30,15 @@ async function handleAddDevice(event: Event): Promise<void> {
|
|||||||
const token = getToken();
|
const token = getToken();
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
|
|
||||||
const deviceNameInput = document.getElementById("device-name") as HTMLInputElement;
|
const deviceNameInput = document.getElementById(
|
||||||
const deviceTypeInput = document.getElementById("device-type") as HTMLSelectElement;
|
"device-name",
|
||||||
const deviceIdentifierInput = document.getElementById("device-identifier") as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
|
const deviceTypeInput = document.getElementById(
|
||||||
|
"device-type",
|
||||||
|
) as HTMLSelectElement;
|
||||||
|
const deviceIdentifierInput = document.getElementById(
|
||||||
|
"device-identifier",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
|
||||||
if (!deviceNameInput || !deviceTypeInput || !deviceIdentifierInput) return;
|
if (!deviceNameInput || !deviceTypeInput || !deviceIdentifierInput) return;
|
||||||
|
|
||||||
@@ -53,7 +59,10 @@ async function handleAddDevice(event: Event): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
showToast("Device registered! Check your device for sync instructions.", "success");
|
showToast(
|
||||||
|
"Device registered! Check your device for sync instructions.",
|
||||||
|
"success",
|
||||||
|
);
|
||||||
hideAddDeviceModal();
|
hideAddDeviceModal();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
} else {
|
} else {
|
||||||
@@ -67,12 +76,24 @@ async function handleAddDevice(event: Event): Promise<void> {
|
|||||||
|
|
||||||
function showDeviceSettings(deviceId: string): void {
|
function showDeviceSettings(deviceId: string): void {
|
||||||
const modal = document.getElementById("device-settings-modal");
|
const modal = document.getElementById("device-settings-modal");
|
||||||
const deviceIdInput = document.getElementById("settings-device-id") as HTMLInputElement;
|
const deviceIdInput = document.getElementById(
|
||||||
const deviceTypeInput = document.getElementById("settings-device-type") as HTMLInputElement;
|
"settings-device-id",
|
||||||
const deviceNameInput = document.getElementById("settings-device-name") as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
const syncEnabledInput = document.getElementById("settings-sync-enabled") as HTMLInputElement;
|
const deviceTypeInput = document.getElementById(
|
||||||
const autoSyncInput = document.getElementById("settings-auto-sync") as HTMLInputElement;
|
"settings-device-type",
|
||||||
const syncFrequencyInput = document.getElementById("settings-sync-frequency") as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
|
const deviceNameInput = document.getElementById(
|
||||||
|
"settings-device-name",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const syncEnabledInput = document.getElementById(
|
||||||
|
"settings-sync-enabled",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const autoSyncInput = document.getElementById(
|
||||||
|
"settings-auto-sync",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const syncFrequencyInput = document.getElementById(
|
||||||
|
"settings-sync-frequency",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
|
||||||
if (!modal || !deviceIdInput) return;
|
if (!modal || !deviceIdInput) return;
|
||||||
|
|
||||||
@@ -88,9 +109,11 @@ function showDeviceSettings(deviceId: string): void {
|
|||||||
.then((device) => {
|
.then((device) => {
|
||||||
if (deviceTypeInput) deviceTypeInput.value = device.device_type || "";
|
if (deviceTypeInput) deviceTypeInput.value = device.device_type || "";
|
||||||
if (deviceNameInput) deviceNameInput.value = device.device_name || "";
|
if (deviceNameInput) deviceNameInput.value = device.device_name || "";
|
||||||
if (syncEnabledInput) syncEnabledInput.checked = device.sync_enabled || false;
|
if (syncEnabledInput)
|
||||||
|
syncEnabledInput.checked = device.sync_enabled || false;
|
||||||
if (autoSyncInput) autoSyncInput.checked = device.auto_sync || false;
|
if (autoSyncInput) autoSyncInput.checked = device.auto_sync || false;
|
||||||
if (syncFrequencyInput) syncFrequencyInput.value = String(device.sync_frequency_minutes || 60);
|
if (syncFrequencyInput)
|
||||||
|
syncFrequencyInput.value = String(device.sync_frequency_minutes || 60);
|
||||||
|
|
||||||
const deviceType = device.device_type;
|
const deviceType = device.device_type;
|
||||||
return fetch("/api/collections", {
|
return fetch("/api/collections", {
|
||||||
@@ -104,17 +127,32 @@ function showDeviceSettings(deviceId: string): void {
|
|||||||
const viewSettings = firstCollection.view_settings || {};
|
const viewSettings = firstCollection.view_settings || {};
|
||||||
const deviceSettings = viewSettings[deviceType] || {};
|
const deviceSettings = viewSettings[deviceType] || {};
|
||||||
|
|
||||||
const viewModeInput = document.getElementById("settings-view-mode") as HTMLInputElement;
|
const viewModeInput = document.getElementById(
|
||||||
const sortOrderInput = document.getElementById("settings-sort-order") as HTMLInputElement;
|
"settings-view-mode",
|
||||||
const itemsPerPageInput = document.getElementById("settings-items-per-page") as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
const showCoversInput = document.getElementById("settings-show-covers") as HTMLInputElement;
|
const sortOrderInput = document.getElementById(
|
||||||
const showProgressInput = document.getElementById("settings-show-progress") as HTMLInputElement;
|
"settings-sort-order",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const itemsPerPageInput = document.getElementById(
|
||||||
|
"settings-items-per-page",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const showCoversInput = document.getElementById(
|
||||||
|
"settings-show-covers",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const showProgressInput = document.getElementById(
|
||||||
|
"settings-show-progress",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
|
||||||
if (viewModeInput) viewModeInput.value = deviceSettings.view_mode || "grid";
|
if (viewModeInput)
|
||||||
if (sortOrderInput) sortOrderInput.value = deviceSettings.sort_order || "name";
|
viewModeInput.value = deviceSettings.view_mode || "grid";
|
||||||
if (itemsPerPageInput) itemsPerPageInput.value = String(deviceSettings.items_per_page || 24);
|
if (sortOrderInput)
|
||||||
if (showCoversInput) showCoversInput.checked = deviceSettings.show_covers !== false;
|
sortOrderInput.value = deviceSettings.sort_order || "name";
|
||||||
if (showProgressInput) showProgressInput.checked = deviceSettings.show_progress || false;
|
if (itemsPerPageInput)
|
||||||
|
itemsPerPageInput.value = String(deviceSettings.items_per_page || 24);
|
||||||
|
if (showCoversInput)
|
||||||
|
showCoversInput.checked = deviceSettings.show_covers !== false;
|
||||||
|
if (showProgressInput)
|
||||||
|
showProgressInput.checked = deviceSettings.show_progress || false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -125,7 +163,9 @@ function showDeviceSettings(deviceId: string): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showShelfMappings(deviceId: string): void {
|
function showShelfMappings(deviceId: string): void {
|
||||||
const mappingsDeviceIdInput = document.getElementById("mappings-device-id") as HTMLInputElement;
|
const mappingsDeviceIdInput = document.getElementById(
|
||||||
|
"mappings-device-id",
|
||||||
|
) as HTMLInputElement;
|
||||||
const modal = document.getElementById("shelf-mappings-modal");
|
const modal = document.getElementById("shelf-mappings-modal");
|
||||||
|
|
||||||
if (mappingsDeviceIdInput) mappingsDeviceIdInput.value = deviceId;
|
if (mappingsDeviceIdInput) mappingsDeviceIdInput.value = deviceId;
|
||||||
@@ -180,29 +220,45 @@ async function loadShelfMappings(deviceId: string): Promise<void> {
|
|||||||
)
|
)
|
||||||
.join("");
|
.join("");
|
||||||
} else {
|
} else {
|
||||||
container.innerHTML = '<p style="color: var(--text-secondary)">No shelf mappings configured. Click "Add Mapping" to create one.</p>';
|
container.innerHTML =
|
||||||
|
'<p style="color: var(--text-secondary)">No shelf mappings configured. Click "Add Mapping" to create one.</p>';
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
container.innerHTML = '<p style="color: var(--error)">Failed to load mappings</p>';
|
container.innerHTML =
|
||||||
|
'<p style="color: var(--error)">Failed to load mappings</p>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAddMappingModal(): void {
|
function showAddMappingModal(): void {
|
||||||
const mappingsDeviceIdInput = document.getElementById("mappings-device-id") as HTMLInputElement;
|
const mappingsDeviceIdInput = document.getElementById(
|
||||||
const mappingIdInput = document.getElementById("mapping-id") as HTMLInputElement;
|
"mappings-device-id",
|
||||||
const mappingCollectionInput = document.getElementById("mapping-collection") as HTMLSelectElement;
|
) as HTMLInputElement;
|
||||||
const mappingShelfNameInput = document.getElementById("mapping-shelf-name") as HTMLInputElement;
|
const mappingIdInput = document.getElementById(
|
||||||
const mappingSyncDirectionInput = document.getElementById("mapping-sync-direction") as HTMLSelectElement;
|
"mapping-id",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const mappingCollectionInput = document.getElementById(
|
||||||
|
"mapping-collection",
|
||||||
|
) as HTMLSelectElement;
|
||||||
|
const mappingShelfNameInput = document.getElementById(
|
||||||
|
"mapping-shelf-name",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const mappingSyncDirectionInput = document.getElementById(
|
||||||
|
"mapping-sync-direction",
|
||||||
|
) as HTMLSelectElement;
|
||||||
const modal = document.getElementById("add-mapping-modal");
|
const modal = document.getElementById("add-mapping-modal");
|
||||||
|
|
||||||
if (mappingsDeviceIdInput) {
|
if (mappingsDeviceIdInput) {
|
||||||
const mappingDeviceIdInput = document.getElementById("mapping-device-id") as HTMLInputElement;
|
const mappingDeviceIdInput = document.getElementById(
|
||||||
if (mappingDeviceIdInput) mappingDeviceIdInput.value = mappingsDeviceIdInput.value;
|
"mapping-device-id",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
if (mappingDeviceIdInput)
|
||||||
|
mappingDeviceIdInput.value = mappingsDeviceIdInput.value;
|
||||||
}
|
}
|
||||||
if (mappingIdInput) mappingIdInput.value = "";
|
if (mappingIdInput) mappingIdInput.value = "";
|
||||||
if (mappingCollectionInput) mappingCollectionInput.value = "";
|
if (mappingCollectionInput) mappingCollectionInput.value = "";
|
||||||
if (mappingShelfNameInput) mappingShelfNameInput.value = "";
|
if (mappingShelfNameInput) mappingShelfNameInput.value = "";
|
||||||
if (mappingSyncDirectionInput) mappingSyncDirectionInput.value = "bidirectional";
|
if (mappingSyncDirectionInput)
|
||||||
|
mappingSyncDirectionInput.value = "bidirectional";
|
||||||
|
|
||||||
loadCollections();
|
loadCollections();
|
||||||
if (modal) modal.classList.remove("hidden");
|
if (modal) modal.classList.remove("hidden");
|
||||||
@@ -216,7 +272,9 @@ function hideAddMappingModal(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadCollections(): Promise<void> {
|
async function loadCollections(): Promise<void> {
|
||||||
const select = document.getElementById("mapping-collection") as HTMLSelectElement;
|
const select = document.getElementById(
|
||||||
|
"mapping-collection",
|
||||||
|
) as HTMLSelectElement;
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
if (!select || !token) return;
|
if (!select || !token) return;
|
||||||
|
|
||||||
@@ -229,24 +287,40 @@ async function loadCollections(): Promise<void> {
|
|||||||
if (result.collections) {
|
if (result.collections) {
|
||||||
select.innerHTML =
|
select.innerHTML =
|
||||||
'<option value="">Select collection...</option>' +
|
'<option value="">Select collection...</option>' +
|
||||||
result.collections.map((col: any) => `<option value="${col.id}">${col.name}</option>`).join("");
|
result.collections
|
||||||
|
.map((col: any) => `<option value="${col.id}">${col.name}</option>`)
|
||||||
|
.join("");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to load collections", error);
|
console.error("Failed to load collections", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function editMapping(mappingId: string, collectionId: string, shelfName: string, syncDirection: string): void {
|
function editMapping(
|
||||||
const mappingIdInput = document.getElementById("mapping-id") as HTMLInputElement;
|
mappingId: string,
|
||||||
const mappingCollectionInput = document.getElementById("mapping-collection") as HTMLSelectElement;
|
collectionId: string,
|
||||||
const mappingShelfNameInput = document.getElementById("mapping-shelf-name") as HTMLInputElement;
|
shelfName: string,
|
||||||
const mappingSyncDirectionInput = document.getElementById("mapping-sync-direction") as HTMLSelectElement;
|
syncDirection: string,
|
||||||
|
): void {
|
||||||
|
const mappingIdInput = document.getElementById(
|
||||||
|
"mapping-id",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const mappingCollectionInput = document.getElementById(
|
||||||
|
"mapping-collection",
|
||||||
|
) as HTMLSelectElement;
|
||||||
|
const mappingShelfNameInput = document.getElementById(
|
||||||
|
"mapping-shelf-name",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const mappingSyncDirectionInput = document.getElementById(
|
||||||
|
"mapping-sync-direction",
|
||||||
|
) as HTMLSelectElement;
|
||||||
const modal = document.getElementById("add-mapping-modal");
|
const modal = document.getElementById("add-mapping-modal");
|
||||||
|
|
||||||
if (mappingIdInput) mappingIdInput.value = mappingId;
|
if (mappingIdInput) mappingIdInput.value = mappingId;
|
||||||
if (mappingCollectionInput) mappingCollectionInput.value = collectionId;
|
if (mappingCollectionInput) mappingCollectionInput.value = collectionId;
|
||||||
if (mappingShelfNameInput) mappingShelfNameInput.value = shelfName;
|
if (mappingShelfNameInput) mappingShelfNameInput.value = shelfName;
|
||||||
if (mappingSyncDirectionInput) mappingSyncDirectionInput.value = syncDirection;
|
if (mappingSyncDirectionInput)
|
||||||
|
mappingSyncDirectionInput.value = syncDirection;
|
||||||
|
|
||||||
if (modal) modal.classList.remove("hidden");
|
if (modal) modal.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
@@ -255,13 +329,30 @@ async function handleSaveMapping(event: Event): Promise<void> {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
const mappingsDeviceIdInput = document.getElementById("mapping-device-id") as HTMLInputElement;
|
const mappingsDeviceIdInput = document.getElementById(
|
||||||
const mappingIdInput = document.getElementById("mapping-id") as HTMLInputElement;
|
"mapping-device-id",
|
||||||
const mappingCollectionInput = document.getElementById("mapping-collection") as HTMLSelectElement;
|
) as HTMLInputElement;
|
||||||
const mappingShelfNameInput = document.getElementById("mapping-shelf-name") as HTMLInputElement;
|
const mappingIdInput = document.getElementById(
|
||||||
const mappingSyncDirectionInput = document.getElementById("mapping-sync-direction") as HTMLSelectElement;
|
"mapping-id",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const mappingCollectionInput = document.getElementById(
|
||||||
|
"mapping-collection",
|
||||||
|
) as HTMLSelectElement;
|
||||||
|
const mappingShelfNameInput = document.getElementById(
|
||||||
|
"mapping-shelf-name",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const mappingSyncDirectionInput = document.getElementById(
|
||||||
|
"mapping-sync-direction",
|
||||||
|
) as HTMLSelectElement;
|
||||||
|
|
||||||
if (!token || !mappingsDeviceIdInput || !mappingCollectionInput || !mappingShelfNameInput || !mappingSyncDirectionInput) return;
|
if (
|
||||||
|
!token ||
|
||||||
|
!mappingsDeviceIdInput ||
|
||||||
|
!mappingCollectionInput ||
|
||||||
|
!mappingShelfNameInput ||
|
||||||
|
!mappingSyncDirectionInput
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
const deviceId = mappingsDeviceIdInput.value;
|
const deviceId = mappingsDeviceIdInput.value;
|
||||||
const mappingId = mappingIdInput.value;
|
const mappingId = mappingIdInput.value;
|
||||||
@@ -305,16 +396,21 @@ async function deleteMapping(mappingId: string): Promise<void> {
|
|||||||
if (!confirm("Are you sure you want to delete this mapping?")) return;
|
if (!confirm("Are you sure you want to delete this mapping?")) return;
|
||||||
|
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
const mappingsDeviceIdInput = document.getElementById("mappings-device-id") as HTMLInputElement;
|
const mappingsDeviceIdInput = document.getElementById(
|
||||||
|
"mappings-device-id",
|
||||||
|
) as HTMLInputElement;
|
||||||
if (!token || !mappingsDeviceIdInput) return;
|
if (!token || !mappingsDeviceIdInput) return;
|
||||||
|
|
||||||
const deviceId = mappingsDeviceIdInput.value;
|
const deviceId = mappingsDeviceIdInput.value;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/devices/${deviceId}/collections/${mappingId}`, {
|
const response = await fetch(
|
||||||
method: "DELETE",
|
`/api/devices/${deviceId}/collections/${mappingId}`,
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
{
|
||||||
});
|
method: "DELETE",
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
showToast("Mapping deleted", "success");
|
showToast("Mapping deleted", "success");
|
||||||
@@ -337,12 +433,24 @@ async function handleSaveDeviceSettings(event: Event): Promise<void> {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
const settingsDeviceIdInput = document.getElementById("settings-device-id") as HTMLInputElement;
|
const settingsDeviceIdInput = document.getElementById(
|
||||||
const settingsDeviceTypeInput = document.getElementById("settings-device-type") as HTMLInputElement;
|
"settings-device-id",
|
||||||
const settingsDeviceNameInput = document.getElementById("settings-device-name") as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
const settingsSyncEnabledInput = document.getElementById("settings-sync-enabled") as HTMLInputElement;
|
const settingsDeviceTypeInput = document.getElementById(
|
||||||
const settingsAutoSyncInput = document.getElementById("settings-auto-sync") as HTMLInputElement;
|
"settings-device-type",
|
||||||
const settingsSyncFrequencyInput = document.getElementById("settings-sync-frequency") as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
|
const settingsDeviceNameInput = document.getElementById(
|
||||||
|
"settings-device-name",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const settingsSyncEnabledInput = document.getElementById(
|
||||||
|
"settings-sync-enabled",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const settingsAutoSyncInput = document.getElementById(
|
||||||
|
"settings-auto-sync",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const settingsSyncFrequencyInput = document.getElementById(
|
||||||
|
"settings-sync-frequency",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
|
||||||
if (!token || !settingsDeviceIdInput || !settingsDeviceNameInput) return;
|
if (!token || !settingsDeviceIdInput || !settingsDeviceNameInput) return;
|
||||||
|
|
||||||
@@ -353,14 +461,27 @@ async function handleSaveDeviceSettings(event: Event): Promise<void> {
|
|||||||
device_name: settingsDeviceNameInput.value,
|
device_name: settingsDeviceNameInput.value,
|
||||||
sync_enabled: settingsSyncEnabledInput?.checked || false,
|
sync_enabled: settingsSyncEnabledInput?.checked || false,
|
||||||
auto_sync: settingsAutoSyncInput?.checked || false,
|
auto_sync: settingsAutoSyncInput?.checked || false,
|
||||||
sync_frequency_minutes: parseInt(settingsSyncFrequencyInput?.value || "60", 10),
|
sync_frequency_minutes: parseInt(
|
||||||
|
settingsSyncFrequencyInput?.value || "60",
|
||||||
|
10,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewModeInput = document.getElementById("settings-view-mode") as HTMLSelectElement;
|
const viewModeInput = document.getElementById(
|
||||||
const sortOrderInput = document.getElementById("settings-sort-order") as HTMLSelectElement;
|
"settings-view-mode",
|
||||||
const itemsPerPageInput = document.getElementById("settings-items-per-page") as HTMLInputElement;
|
) as HTMLSelectElement;
|
||||||
const showCoversInput = document.getElementById("settings-show-covers") as HTMLInputElement;
|
const sortOrderInput = document.getElementById(
|
||||||
const showProgressInput = document.getElementById("settings-show-progress") as HTMLInputElement;
|
"settings-sort-order",
|
||||||
|
) as HTMLSelectElement;
|
||||||
|
const itemsPerPageInput = document.getElementById(
|
||||||
|
"settings-items-per-page",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const showCoversInput = document.getElementById(
|
||||||
|
"settings-show-covers",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const showProgressInput = document.getElementById(
|
||||||
|
"settings-show-progress",
|
||||||
|
) as HTMLInputElement;
|
||||||
|
|
||||||
const viewSettings = {
|
const viewSettings = {
|
||||||
view_mode: viewModeInput?.value || "grid",
|
view_mode: viewModeInput?.value || "grid",
|
||||||
@@ -413,12 +534,29 @@ async function handleSaveDeviceSettings(event: Event): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function copyToClipboard(text: string, description: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
showToast(`${description} copied!`, "success");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to copy to clipboard", error);
|
||||||
|
showToast("Failed to copy to clipboard", "error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleRevokeDevice(): Promise<void> {
|
async function handleRevokeDevice(): Promise<void> {
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
const settingsDeviceIdInput = document.getElementById("settings-device-id") as HTMLInputElement;
|
const settingsDeviceIdInput = document.getElementById(
|
||||||
|
"settings-device-id",
|
||||||
|
) as HTMLInputElement;
|
||||||
if (!token || !settingsDeviceIdInput) return;
|
if (!token || !settingsDeviceIdInput) return;
|
||||||
|
|
||||||
if (!confirm("Are you sure you want to revoke this device? It will no longer be able to sync.")) return;
|
if (
|
||||||
|
!confirm(
|
||||||
|
"Are you sure you want to revoke this device? It will no longer be able to sync.",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
const deviceId = settingsDeviceIdInput.value;
|
const deviceId = settingsDeviceIdInput.value;
|
||||||
|
|
||||||
@@ -547,7 +685,6 @@ export {
|
|||||||
hideShelfMappingsModal,
|
hideShelfMappingsModal,
|
||||||
loadCollections,
|
loadCollections,
|
||||||
loadShelfMappings,
|
loadShelfMappings,
|
||||||
regenerateDeviceToken,
|
|
||||||
rejectDevice,
|
rejectDevice,
|
||||||
setupEventDelegation,
|
setupEventDelegation,
|
||||||
showAddDeviceModal,
|
showAddDeviceModal,
|
||||||
@@ -573,7 +710,6 @@ Alpine.store("devices", {
|
|||||||
hideShelfMappingsModal,
|
hideShelfMappingsModal,
|
||||||
loadCollections,
|
loadCollections,
|
||||||
loadShelfMappings,
|
loadShelfMappings,
|
||||||
regenerateDeviceToken,
|
|
||||||
rejectDevice,
|
rejectDevice,
|
||||||
setupEventDelegation,
|
setupEventDelegation,
|
||||||
showAddDeviceModal,
|
showAddDeviceModal,
|
||||||
|
|||||||
Reference in New Issue
Block a user