From 2d924eb4b4a948a968f03b00a853840f9d1f1643 Mon Sep 17 00:00:00 2001 From: "open-swe[bot]" Date: Sun, 10 Aug 2025 13:15:33 +0000 Subject: [PATCH 01/10] Apply patch [skip ci] --- macos_app/lib/services/database_service.dart | 75 ++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/macos_app/lib/services/database_service.dart b/macos_app/lib/services/database_service.dart index 75b192f..8c4f22e 100644 --- a/macos_app/lib/services/database_service.dart +++ b/macos_app/lib/services/database_service.dart @@ -93,8 +93,83 @@ class DatabaseService { await _client.execute("DELETE FROM url_items"); await _client.execute("DELETE FROM categories"); } + Future getSettings() async { + await initialize(); + final ResultSet rs = await _client.query('SELECT value FROM settings WHERE key = ?', positional: ['settings']); + if (rs.rows.isEmpty) { + return Settings(); + } + try { + final value = rs.rows.first['value'] as String?; + if (value == null) { + return Settings(); + } + final Map json = jsonDecode(value); + return Settings.fromJson(json); + } catch (e) { + return Settings(); + } + } + + Future saveSettings(Settings settings) async { + await initialize(); + final jsonString = jsonEncode(settings.toJson()); + await _client.execute( + 'INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)', + positional: ['settings', jsonString], + ); + } + + Future _createSettingsTable() async { + await initialize(); + await _client.execute(''' + CREATE TABLE IF NOT EXISTS settings ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL + ); + '''); + } + + @override + Future initialize() async { + if (_isInitialized) return; + + final dir = await getApplicationSupportDirectory(); + final path = '${dir.path}/later.db'; + + _client = LibsqlClient(path); + await _client.connect(); + + // Create tables if they don't exist + await _client.execute(''' + CREATE TABLE IF NOT EXISTS categories ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + createdAt TEXT NOT NULL, + updatedAt TEXT NOT NULL + ); + '''); + + await _client.execute(''' + CREATE TABLE IF NOT EXISTS url_items ( + id TEXT PRIMARY KEY, + url TEXT NOT NULL, + title TEXT NOT NULL, + description TEXT, + categoryId TEXT, + createdAt TEXT NOT NULL, + updatedAt TEXT NOT NULL, + FOREIGN KEY (categoryId) REFERENCES categories (id) ON DELETE SET NULL + ); + '''); + + await _createSettingsTable(); + + _isInitialized = true; + } Future close() async { if (_isInitialized) { await _client.close(); _isInitialized = false; + From 918455590f0250cb844d5865352967e4fa271261 Mon Sep 17 00:00:00 2001 From: "open-swe[bot]" Date: Sun, 10 Aug 2025 13:15:43 +0000 Subject: [PATCH 02/10] Apply patch [skip ci] --- macos_app/lib/providers/preferences_repository.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/macos_app/lib/providers/preferences_repository.dart b/macos_app/lib/providers/preferences_repository.dart index f119732..d9ade5b 100644 --- a/macos_app/lib/providers/preferences_repository.dart +++ b/macos_app/lib/providers/preferences_repository.dart @@ -84,8 +84,7 @@ class PreferencesRepository { // Settings Future getSettings() async { try { - // Settings are not stored in the database currently, return default - return Settings(); + return await _databaseService.getSettings(); } catch (e) { debugPrint('Error getting settings: $e'); return Settings(); @@ -94,7 +93,7 @@ class PreferencesRepository { Future saveSettings(Settings settings) async { try { - // Settings are not stored in the database currently, no-op + await _databaseService.saveSettings(settings); } catch (e) { debugPrint('Error saving settings: $e'); } @@ -115,3 +114,4 @@ final preferencesRepositoryProvider = Provider( return PreferencesRepository(databaseService); }, ); + From 0f13d630ce8694c83c6fa4e83cc8a0a751102374 Mon Sep 17 00:00:00 2001 From: "open-swe[bot]" Date: Sun, 10 Aug 2025 13:17:28 +0000 Subject: [PATCH 03/10] Apply patch [skip ci] --- macos_app/lib/services/database_service.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/macos_app/lib/services/database_service.dart b/macos_app/lib/services/database_service.dart index 8c4f22e..e9de0e1 100644 --- a/macos_app/lib/services/database_service.dart +++ b/macos_app/lib/services/database_service.dart @@ -130,7 +130,6 @@ class DatabaseService { '''); } - @override Future initialize() async { if (_isInitialized) return; From 8265f4d74da9b7c36dda4deb77ee507d254585cd Mon Sep 17 00:00:00 2001 From: "open-swe[bot]" Date: Sun, 10 Aug 2025 13:19:41 +0000 Subject: [PATCH 04/10] Apply patch [skip ci] --- macos_app/lib/widgets/about_dialog.dart | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 macos_app/lib/widgets/about_dialog.dart diff --git a/macos_app/lib/widgets/about_dialog.dart b/macos_app/lib/widgets/about_dialog.dart new file mode 100644 index 0000000..e69de29 From 0aadad3315db957ce101460e2f08201bcc2c1f11 Mon Sep 17 00:00:00 2001 From: "open-swe[bot]" Date: Sun, 10 Aug 2025 13:19:50 +0000 Subject: [PATCH 05/10] Apply patch [skip ci] --- macos_app/lib/widgets/about_dialog.dart | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/macos_app/lib/widgets/about_dialog.dart b/macos_app/lib/widgets/about_dialog.dart index e69de29..b7d72af 100644 --- a/macos_app/lib/widgets/about_dialog.dart +++ b/macos_app/lib/widgets/about_dialog.dart @@ -0,0 +1,38 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:macos_ui/macos_ui.dart'; + +/// A dialog that shows information about the Later app. +void showAboutLaterDialog(BuildContext context) { + showMacosAlertDialog( + context: context, + builder: (context) { + return MacosAlertDialog( + appIcon: const MacosIcon( + CupertinoIcons.info_circle, + size: 56, + ), + title: const Text('About Later'), + message: const Text( + 'A simple bookmarking app for macOS.', + textAlign: TextAlign.center, + ), + primaryButton: PushButton( + controlSize: ControlSize.large, + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Close'), + ), + secondaryButton: PushButton( + controlSize: ControlSize.large, + onPressed: () { + // Open the GitHub repository + }, + child: const Text('GitHub'), + ), + ); + }, + ); +} + From 324076af07e438b109f8e2681f0a0915bc104188 Mon Sep 17 00:00:00 2001 From: "open-swe[bot]" Date: Sun, 10 Aug 2025 13:20:15 +0000 Subject: [PATCH 06/10] Apply patch [skip ci] --- macos_app/lib/widgets/about_dialog.dart | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/macos_app/lib/widgets/about_dialog.dart b/macos_app/lib/widgets/about_dialog.dart index b7d72af..dbc151f 100644 --- a/macos_app/lib/widgets/about_dialog.dart +++ b/macos_app/lib/widgets/about_dialog.dart @@ -24,15 +24,9 @@ void showAboutLaterDialog(BuildContext context) { }, child: const Text('Close'), ), - secondaryButton: PushButton( - controlSize: ControlSize.large, - onPressed: () { - // Open the GitHub repository - }, - child: const Text('GitHub'), - ), ); }, ); } + From 1cf9469fc85516b1cdf0dfc7b0da121d34736051 Mon Sep 17 00:00:00 2001 From: "open-swe[bot]" Date: Sun, 10 Aug 2025 13:20:24 +0000 Subject: [PATCH 07/10] Apply patch [skip ci] --- macos_app/lib/utils/menubar.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/macos_app/lib/utils/menubar.dart b/macos_app/lib/utils/menubar.dart index 1114890..dbb6fab 100644 --- a/macos_app/lib/utils/menubar.dart +++ b/macos_app/lib/utils/menubar.dart @@ -9,6 +9,7 @@ import '../pages/settings_page.dart'; import '../pages/import_dialog.dart'; import '../pages/import_urls_dialog.dart'; import '../pages/export_dialog.dart'; +import '../widgets/about_dialog.dart'; import '../providers/providers.dart'; import '../utils/import_export_manager.dart'; @@ -36,7 +37,7 @@ class LaterMenuBar { 'Later', [ _buildMenuItem(context, 'About Later', null, () { - // TODO: Implement about dialog + showAboutLaterDialog(context); }), _buildMenuItem(context, 'Preferences', '⌘,', () { Navigator.of(context).push( @@ -224,3 +225,4 @@ class LaterMenuBar { ); } } + From 532fe0a353c6576769cb3c01a9f2850116e38360 Mon Sep 17 00:00:00 2001 From: "open-swe[bot]" Date: Sun, 10 Aug 2025 13:21:01 +0000 Subject: [PATCH 08/10] Apply patch [skip ci] --- macos_app/lib/utils/menubar.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/macos_app/lib/utils/menubar.dart b/macos_app/lib/utils/menubar.dart index dbb6fab..c441c0e 100644 --- a/macos_app/lib/utils/menubar.dart +++ b/macos_app/lib/utils/menubar.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; import 'package:macos_ui/macos_ui.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:local_notifier/local_notifier.dart'; @@ -46,7 +47,7 @@ class LaterMenuBar { }), const Divider(), _buildMenuItem(context, 'Quit Later', '⌘Q', () { - // TODO: Implement graceful app exit + SystemNavigator.pop(); }), ], ), From 6d20d47b75b73cfb2ece32c864b5728e08953d96 Mon Sep 17 00:00:00 2001 From: "open-swe[bot]" Date: Sun, 10 Aug 2025 13:21:59 +0000 Subject: [PATCH 09/10] Apply patch [skip ci] --- shared/js/popup.js | 198 --------------------------------------------- 1 file changed, 198 deletions(-) diff --git a/shared/js/popup.js b/shared/js/popup.js index 1588160..8b13789 100644 --- a/shared/js/popup.js +++ b/shared/js/popup.js @@ -1,199 +1 @@ -function generateId() { - return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( - /[xy]/g, - function (c) { - const r = (Math.random() * 16) | 0; - const v = c === "x" ? r : (r & 0x3) | 0x8; - return v.toString(16); - }, - ); -} -function showStatus(message, type) { - const statusDiv = document.getElementById("status"); - statusDiv.textContent = message; - statusDiv.className = "status " + type; - - // Clear status after 3 seconds - setTimeout(function () { - statusDiv.className = "status"; - }, 3000); -} - -function initializePopup(browserAPI) { - // DOM elements - const categorySelect = document.getElementById("category"); - const newCategoryForm = document.getElementById("newCategoryForm"); - const newCategoryInput = document.getElementById("newCategory"); - const createCategoryBtn = document.getElementById("createCategory"); - const showNewCategoryLink = document.getElementById("showNewCategory"); - const saveCurrentTabBtn = document.getElementById("saveCurrentTab"); - const saveAllTabsBtn = document.getElementById("saveAllTabs"); - - // Load categories from storage - loadCategories(); - - // Event listeners - showNewCategoryLink.addEventListener("click", function (e) { - e.preventDefault(); - newCategoryForm.classList.toggle("hidden"); - showNewCategoryLink.classList.toggle("hidden"); - newCategoryInput.focus(); - }); - - createCategoryBtn.addEventListener("click", function () { - createCategory(); - }); - - newCategoryInput.addEventListener("keypress", function (e) { - if (e.key === "Enter") { - createCategory(); - } - }); - - saveCurrentTabBtn.addEventListener("click", function () { - saveCurrentTab(); - }); - - saveAllTabsBtn.addEventListener("click", function () { - saveAllTabs(); - }); - - // Functions - function loadCategories() { - browserAPI.storage.sync.get("categories").then(function (data) { - let categories = data.categories || []; - - // If no categories exist, create a default one - if (categories.length === 0) { - categories = [{ id: generateId(), name: "Bookmarks" }]; - browserAPI.storage.sync.set({ categories: categories }); - } - - // Clear and populate the dropdown - categorySelect.innerHTML = ""; - categories.forEach(function (category) { - const option = document.createElement("option"); - option.value = category.id; - option.textContent = category.name; - categorySelect.appendChild(option); - }); - }).catch(function (error) { - console.error("Error loading categories:", error); - }); - } - - function createCategory() { - const categoryName = newCategoryInput.value.trim(); - - if (categoryName) { - browserAPI.storage.sync.get("categories").then(function (data) { - const categories = data.categories || []; - const newCategory = { - id: generateId(), - name: categoryName, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }; - - categories.push(newCategory); - browserAPI.storage.sync.set({ categories: categories }).then(function () { - // Reload categories and reset form - loadCategories(); - newCategoryInput.value = ""; - newCategoryForm.classList.add("hidden"); - showNewCategoryLink.classList.remove("hidden"); - - // Select the new category - setTimeout(function () { - categorySelect.value = newCategory.id; - }, 100); - }).catch(function (error) { - console.error("Error setting categories:", error); - showStatus("Failed to create category", "error"); - }); - }).catch(function (error) { - console.error("Error getting categories:", error); - showStatus("Failed to create category", "error"); - }); - } - } - - function saveCurrentTab() { - browserAPI.tabs.query({ active: true, currentWindow: true }).then(function (tabs) { - if (tabs.length === 0) { - showStatus("No active tab found.", "error"); - return; - } - saveTabsToLater(tabs); - }).catch(function (error) { - console.error("Error querying tabs:", error); - showStatus("Failed to save current tab.", "error"); - }); - } - - function saveAllTabs() { - browserAPI.tabs.query({ currentWindow: true }).then(function (tabs) { - if (tabs.length === 0) { - showStatus("No tabs found in the current window.", "error"); - return; - } - saveTabsToLater(tabs); - }).catch(function (error) { - console.error("Error querying tabs:", error); - showStatus("Failed to save all tabs.", "error"); - }); - } - - function saveTabsToLater(tabs) { - const urlItems = tabs.map((tab) => { - return { - id: generateId(), - url: tab.url, - title: tab.title || tab.url, - description: "", - categoryId: categorySelect.value, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }; - }); - - browserAPI.storage.sync.get("categories").then(function (data) { - const categories = data.categories || []; - const formattedCategories = categories.map((category) => { - return { - id: category.id, - name: category.name, - createdAt: category.createdAt || new Date().toISOString(), - updatedAt: category.updatedAt || new Date().toISOString(), - }; - }); - - const exportData = { - urls: urlItems, - categories: formattedCategories, - version: "1.0.0", - exportedAt: new Date().toISOString(), - }; - - copyToClipboard(exportData, tabs.length, false); - triggerClipboardImport(exportData, tabs.length); - }).catch((error) => { - console.error("Error in saveTabsToLater:", error); - const fallbackExportData = { - urls: urlItems, - categories: [], - version: "1.0.0", - exportedAt: new Date().toISOString(), - }; - copyToClipboard(fallbackExportData, tabs.length); - }); - } - - function triggerClipboardImport(exportData, tabCount) { - const laterUrl = `later:///clipboard-import`; - showStatus(`${tabCount} tab(s) sent to Later app.`, "success"); - setTimeout(() => { - window.location.href = laterUrl; - }, 300); - } From 2411825bc9b6a1c0b241f2b0514e0d75eff631e2 Mon Sep 17 00:00:00 2001 From: "open-swe[bot]" Date: Sun, 10 Aug 2025 13:25:34 +0000 Subject: [PATCH 10/10] Apply patch [skip ci] --- shared/js/popup.js | 1 - 1 file changed, 1 deletion(-) diff --git a/shared/js/popup.js b/shared/js/popup.js index 8b13789..e69de29 100644 --- a/shared/js/popup.js +++ b/shared/js/popup.js @@ -1 +0,0 @@ -