diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a1cfede --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM nginx:alpine + +COPY ./build/web /usr/share/nginx/html + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/devenv.nix b/devenv.nix index a1f58e1..5a070d6 100644 --- a/devenv.nix +++ b/devenv.nix @@ -1,5 +1,4 @@ -{ pkgs, ... }: -{ +{pkgs, ...}: { android = { enable = true; emulator.enable = false; @@ -39,4 +38,15 @@ # Needed by flutter_secure_storage libsecret.dev ]; + + processes.web-server = { + exec = "python -m http.server 8080 -d build/web"; + }; + + scripts = { + web-to-container.exec = '' + flutter build web --release + docker build -t sheetless . + ''; + }; } diff --git a/lib/api.dart b/lib/api.dart index 768f1c6..fc96b2a 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:logging/logging.dart'; import 'package:path_provider/path_provider.dart'; // For cache storage @@ -21,10 +22,7 @@ class ApiClient { final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, - body: jsonEncode({ - 'email': username, - 'password': password, - }), + body: jsonEncode({'email': username, 'password': password}), ); if (response.statusCode == 200) { @@ -59,7 +57,8 @@ class ApiClient { return response; } else { log.warning( - 'GET request failed: ${response.statusCode} ${response.body}'); + 'GET request failed: ${response.statusCode} ${response.body}', + ); } } catch (e) { log.warning('Error during GET request', e); @@ -68,7 +67,9 @@ class ApiClient { } Future post( - String endpoint, Map body) async { + String endpoint, + Map body, + ) async { try { final url = '$baseUrl$endpoint'; final headers = { @@ -86,7 +87,8 @@ class ApiClient { return response; } else { log.info( - 'POST request failed: ${response.statusCode} ${response.body}'); + 'POST request failed: ${response.statusCode} ${response.body}', + ); } } catch (e) { log.info('Error during POST request: $e'); @@ -112,7 +114,8 @@ class ApiClient { return response; } else { log.info( - 'POST Form Data request failed: ${response.statusCode} ${response.body}'); + 'POST Form Data request failed: ${response.statusCode} ${response.body}', + ); } } catch (e) { log.info('Error during POST Form Data request: $e'); @@ -135,7 +138,8 @@ class ApiClient { .toList(); } else { log.warning( - 'Failed to fetch sheets with status: ${response.statusCode}'); + 'Failed to fetch sheets with status: ${response.statusCode}', + ); log.info('Response: ${response.body}'); } } catch (e) { @@ -145,6 +149,25 @@ class ApiClient { return List.empty(); } + Future fetchPdfFileData(String sheetUuid) async { + try { + final response = await get('/sheet/pdf/$sheetUuid', isBinary: true); + + if (response != null && response.statusCode == 200) { + log.info("PDF downloaded"); + return response.bodyBytes; + } else { + log.warning( + "Failed to fetch PDF from API. Status: ${response?.statusCode}", + ); + } + } catch (e) { + log.warning("Error fetching PDF", e); + } + + return null; + } + Future getPdfFileCached(String sheetUuid) async { try { final cacheDir = await getTemporaryDirectory(); @@ -156,16 +179,11 @@ class ApiClient { return cachedFile; } - final response = await get('/sheet/pdf/$sheetUuid', isBinary: true); - - if (response != null && response.statusCode == 200) { - await cachedFile.writeAsBytes(response.bodyBytes); - log.info("PDF downloaded and cached at: $cachedPdfPath"); - return cachedFile; - } else { - log.warning( - "Failed to fetch PDF from API. Status: ${response?.statusCode}"); - } + final pdfFileData = await fetchPdfFileData(sheetUuid); + await cachedFile.writeAsBytes( + pdfFileData!, + ); // TODO: proper error handling + log.info("PDF cached at: $cachedPdfPath"); } catch (e) { log.warning("Error fetching PDF", e); } diff --git a/lib/main.dart b/lib/main.dart index de75764..bf46759 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; +import 'package:pdfrx/pdfrx.dart'; import 'login_page.dart'; @@ -12,6 +13,8 @@ void main() { } }); + pdfrxFlutterInitialize(); // Needed for web + runApp(const MyApp()); } @@ -22,10 +25,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: 'Sheetless', - theme: ThemeData( - useMaterial3: true, - primarySwatch: Colors.blue, - ), + theme: ThemeData(useMaterial3: true, primarySwatch: Colors.blue), home: const LoginPage(), ); } diff --git a/lib/sheet_viewer_page.dart b/lib/sheet_viewer_page.dart index 73b90ba..1e37cf1 100644 --- a/lib/sheet_viewer_page.dart +++ b/lib/sheet_viewer_page.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_drawing_board/flutter_drawing_board.dart'; @@ -43,13 +44,21 @@ class _SheetViewerPageState extends State { } Future loadPdf() async { - var file = await widget.apiClient.getPdfFileCached(widget.sheet.uuid); - if (file == null) { - throw Exception("Failed fetching pdf file"); + if (kIsWeb) { + var data = await widget.apiClient.fetchPdfFileData(widget.sheet.uuid); + if (data == null) { + throw Exception("Failed fetching pdf file"); + } + + document = await PdfDocument.openData(data); + } else { + var file = await widget.apiClient.getPdfFileCached(widget.sheet.uuid); + if (file == null) { + throw Exception("Failed fetching pdf file"); + } + + document = await PdfDocument.openFile(file.path); } - - document = await PdfDocument.openFile(file.path); - return true; } diff --git a/web/index.html b/web/index.html index d50d70c..9540c7b 100644 --- a/web/index.html +++ b/web/index.html @@ -21,7 +21,7 @@ - + @@ -33,16 +33,6 @@ - -