import 'dart:html' as html; import 'dart:convert'; import 'dart:js_util' as js_util; import '../models/tab_data.dart'; // communication with the browser API // list of TabData objects // result LATER // API takes time so Future as a Type // ASYNC WAIT // return empty if fail // try catch no crash // final for security, assign once then locked, no change main data. // API string result // dynamic list "ANY" Type // decode to list of maps so its ready for use // objects // .toList adding to a list of objects that can be called for the data class BrowserApiService { Future> getTabs() async { try { final result = await _callBrowserAPI('getTabs'); if (result != null) { final List data = json.decode(result); return data.map((item) { item['type'] = 'tab'; return TabData.fromJson(item); }).toList(); } } catch (e) { print('Error getting tabs: $e'); } return []; } Future> getBookmarks() async { try { final result = await _callBrowserAPI('getBookmarks'); if (result != null) { final List data = json.decode(result); return data.map((item) { item['type'] = 'bookmark'; return TabData.fromJson(item); }).toList(); } } catch (e) { print('Error getting bookmarks: $e'); } return []; } Future> getHistory() async { try { final result = await _callBrowserAPI('getHistory', [100]); if (result != null) { final List data = json.decode(result); return data.map((item) { item['type'] = 'history'; return TabData.fromJson(item); }).toList(); } } catch (e) { print('Error getting history: $e'); } return []; } // methods for tab_manager_home // Future // void, no return // gets a string // async, await // _callBrowserAPI for data Future switchToTab(String tabId) async { await _callBrowserAPI('switchToTab', [tabId]); } Future openTab(String url) async { await _callBrowserAPI('openTab', [url]); } Future closeTab(String tabId) async { await _callBrowserAPI('closeTab', [tabId]); } Future removeBookmark(String bookmarkId) async { await _callBrowserAPI('removeBookmark', [bookmarkId]); } Future togglePinTab(String tabId, bool pin) async { await _callBrowserAPI('togglePinTab', [tabId, pin]); } // _callBrowserAPI bridging // Future // return string or ? null // method what to call // optional list args for history "100" // a check for browser API // js_util call JavaScript functions // promiseToFuture convert // html.window global scope, javascript BrowserAPI // ? allow args to be null // final result check if args is null or something // call function that fits // Encode convert javascript object to string // And that is why json format is fucking genius... Future _callBrowserAPI(String method, [List? args]) async { try { if (!js_util.hasProperty(html.window, 'BrowserAPI')) { print('BrowserAPI not found - running in development mode'); return null; } final browserAPI = js_util.getProperty(html.window, 'BrowserAPI'); final function = js_util.getProperty(browserAPI, method); final result = args == null ? await js_util.promiseToFuture(js_util.callMethod(function, 'call', [browserAPI])) : await js_util.promiseToFuture(js_util.callMethod(function, 'call', [browserAPI, ...args])); return json.encode(result); } catch (e) { print('Error calling $method: $e'); return null; } } } // Bridge Flutter app and the browser extension. // // Methods fetching from browser, convert to TabData objects. // // SwitchToTab, openTab, closeTab allow control of tabs. // // _callBrowserAPI low level JavaScript communication. // // dart:js_util call JavaScript functions exposed by browser extension through window.BrowserAPI. // // Empty results if the API is unavailable. // // Browser specific code separate from UI logic, app easier to maintain. // // Without extension, prints debug messages instead of crashing.