Compare commits

...

9 Commits

10 changed files with 122 additions and 50 deletions

View File

@@ -11,12 +11,12 @@ android {
ndkVersion = "27.2.12479018" ndkVersion = "27.2.12479018"
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_17
} }
kotlinOptions { kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString() jvmTarget = JavaVersion.VERSION_17.toString()
} }
defaultConfig { defaultConfig {

View File

@@ -1,3 +1,13 @@
subprojects {
afterEvaluate {
val android = project.extensions.findByName("android")
if (android is com.android.build.gradle.BaseExtension) {
// Force the same sdk version everywhere
android.ndkVersion = "27.2.12479018"
}
}
}
allprojects { allprojects {
repositories { repositories {
google() google()
@@ -19,3 +29,4 @@ subprojects {
tasks.register<Delete>("clean") { tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory) delete(rootProject.layout.buildDirectory)
} }

View File

@@ -18,7 +18,7 @@ pluginManagement {
plugins { plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.7.3" apply false id("com.android.application") version "8.9.1" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false id("org.jetbrains.kotlin.android") version "2.1.0" apply false
} }

View File

@@ -11,12 +11,16 @@ class SyncResult {
final bool isOnline; final bool isOnline;
final int changesSynced; final int changesSynced;
final int annotationsSynced; final int annotationsSynced;
final int changesUnsynced;
final int annotationsUnsynced;
SyncResult({ SyncResult({
required this.sheets, required this.sheets,
required this.isOnline, required this.isOnline,
this.changesSynced = 0, required this.changesSynced,
this.annotationsSynced = 0, required this.annotationsSynced,
required this.changesUnsynced,
required this.annotationsUnsynced,
}); });
} }
@@ -80,6 +84,8 @@ class SyncService {
// 3. Upload pending annotations // 3. Upload pending annotations
annotationsSynced = await _uploadPendingAnnotations(); annotationsSynced = await _uploadPendingAnnotations();
final remainingAnnotations = await _storageService
.readPendingAnnotationUploads();
// 4. Apply any remaining local changes (in case some failed to upload) // 4. Apply any remaining local changes (in case some failed to upload)
final changeQueue = await _storageService.readChangeQueue(); final changeQueue = await _storageService.readChangeQueue();
@@ -102,6 +108,8 @@ class SyncService {
isOnline: true, isOnline: true,
changesSynced: changesSynced, changesSynced: changesSynced,
annotationsSynced: annotationsSynced, annotationsSynced: annotationsSynced,
changesUnsynced: changeQueue.length,
annotationsUnsynced: remainingAnnotations.length,
); );
} }
@@ -127,9 +135,16 @@ class SyncService {
} }
} }
final remainingAnnotations = await _storageService
.readPendingAnnotationUploads();
return SyncResult( return SyncResult(
sheets: sheets, sheets: sheets,
isOnline: false, isOnline: false,
changesSynced: 0,
annotationsSynced: 0,
changesUnsynced: changeQueue.length,
annotationsUnsynced: remainingAnnotations.length,
); );
} }

View File

@@ -48,7 +48,9 @@ class _HomePageState extends State<HomePage> with RouteAware {
super.initState(); super.initState();
// Exit fullscreen when entering home page // Exit fullscreen when entering home page
if (FullScreen.isFullScreen) {
FullScreen.setFullScreen(false); FullScreen.setFullScreen(false);
}
// Subscribe to route changes // Subscribe to route changes
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -71,14 +73,19 @@ class _HomePageState extends State<HomePage> with RouteAware {
@override @override
void didPush() { void didPush() {
// Exit fullscreen when entering home page
if (FullScreen.isFullScreen) {
FullScreen.setFullScreen(false); FullScreen.setFullScreen(false);
}
super.didPush(); super.didPush();
} }
@override @override
void didPopNext() { void didPopNext() {
// Exit fullscreen when returning to home page // Exit fullscreen when returning to home page
if (FullScreen.isFullScreen) {
FullScreen.setFullScreen(false); FullScreen.setFullScreen(false);
}
super.didPopNext(); super.didPopNext();
} }
@@ -212,6 +219,7 @@ class _HomePageState extends State<HomePage> with RouteAware {
onLogout: _handleLogout, onLogout: _handleLogout,
appName: _appName, appName: _appName,
appVersion: _appVersion, appVersion: _appVersion,
syncFuture: _syncFuture,
), ),
body: RefreshIndicator(onRefresh: _refreshSheets, child: _buildBody()), body: RefreshIndicator(onRefresh: _refreshSheets, child: _buildBody()),
); );

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sheetless/core/services/sync_service.dart';
/// Callback for shuffle state changes. /// Callback for shuffle state changes.
typedef ShuffleCallback = void Function(bool enabled); typedef ShuffleCallback = void Function(bool enabled);
@@ -12,12 +13,14 @@ class AppDrawer extends StatelessWidget {
final VoidCallback onLogout; final VoidCallback onLogout;
final String? appName; final String? appName;
final String? appVersion; final String? appVersion;
final Future<SyncResult> syncFuture;
const AppDrawer({ const AppDrawer({
super.key, super.key,
required this.isShuffling, required this.isShuffling,
required this.onShuffleChanged, required this.onShuffleChanged,
required this.onLogout, required this.onLogout,
required this.syncFuture,
this.appName, this.appName,
this.appVersion, this.appVersion,
}); });
@@ -32,7 +35,7 @@ class AppDrawer extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
_buildActions(), _buildActions(),
_buildAppInfo(), Column(children: [_buildSyncStatus(), _buildAppInfo()]),
], ],
), ),
), ),
@@ -44,10 +47,7 @@ class AppDrawer extends StatelessWidget {
return Column( return Column(
children: [ children: [
ListTile( ListTile(
leading: Icon( leading: Icon(Icons.shuffle, color: isShuffling ? Colors.blue : null),
Icons.shuffle,
color: isShuffling ? Colors.blue : null,
),
title: const Text('Shuffle'), title: const Text('Shuffle'),
onTap: () => onShuffleChanged(!isShuffling), onTap: () => onShuffleChanged(!isShuffling),
), ),
@@ -60,6 +60,47 @@ class AppDrawer extends StatelessWidget {
); );
} }
Widget _buildSyncStatus() {
return Center(
// padding: const EdgeInsets.all(5.0),
child: FutureBuilder<SyncResult>(
future: syncFuture,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Text(
"Error: ${snapshot.error.toString()}",
style: const TextStyle(color: Colors.red),
textAlign: TextAlign.center,
);
}
if (snapshot.hasData) {
final changes = snapshot.data!.changesUnsynced;
final annotations = snapshot.data!.annotationsUnsynced;
if (changes == 0 && annotations == 0) {
return Text(
"All synced!",
style: const TextStyle(color: Colors.black),
textAlign: TextAlign.center,
);
}
return Text(
"$changes changes and $annotations annotations unsynchronized!",
style: const TextStyle(color: Colors.red),
textAlign: TextAlign.center,
);
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
Widget _buildAppInfo() { Widget _buildAppInfo() {
final versionText = appName != null && appVersion != null final versionText = appName != null && appVersion != null
? '$appName v$appVersion' ? '$appName v$appVersion'
@@ -67,10 +108,7 @@ class AppDrawer extends StatelessWidget {
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Text( child: Text(versionText, style: const TextStyle(color: Colors.grey)),
versionText,
style: const TextStyle(color: Colors.grey),
),
); );
} }
} }

View File

@@ -65,7 +65,9 @@ class _SheetViewerPageState extends State<SheetViewerPage>
_rightDrawingController = DrawingController(maxHistorySteps: 50); _rightDrawingController = DrawingController(maxHistorySteps: 50);
FullScreen.addListener(this); FullScreen.addListener(this);
if (FullScreen.isFullScreen != widget.config.fullscreen) {
FullScreen.setFullScreen(widget.config.fullscreen); FullScreen.setFullScreen(widget.config.fullscreen);
}
_documentLoaded = _loadPdf(); _documentLoaded = _loadPdf();
} }
@@ -161,14 +163,12 @@ class _SheetViewerPageState extends State<SheetViewerPage>
); );
// Upload left page to server // Upload left page to server
if (leftHasContent) {
_syncService.uploadAnnotation( _syncService.uploadAnnotation(
sheetUuid: widget.sheet.uuid, sheetUuid: widget.sheet.uuid,
page: _currentPage, page: _currentPage,
annotationsJson: leftJson, annotationsJson: leftJson,
lastModified: now, lastModified: now,
); );
}
_leftDrawingController.markSaved(); _leftDrawingController.markSaved();
} }
@@ -188,14 +188,12 @@ class _SheetViewerPageState extends State<SheetViewerPage>
); );
// Upload right page to server // Upload right page to server
if (rightHasContent) {
_syncService.uploadAnnotation( _syncService.uploadAnnotation(
sheetUuid: widget.sheet.uuid, sheetUuid: widget.sheet.uuid,
page: _currentPage + 1, page: _currentPage + 1,
annotationsJson: rightJson, annotationsJson: rightJson,
lastModified: now, lastModified: now,
); );
}
_rightDrawingController.markSaved(); _rightDrawingController.markSaved();
} }
@@ -214,7 +212,7 @@ class _SheetViewerPageState extends State<SheetViewerPage>
} }
void _toggleFullscreen() { void _toggleFullscreen() {
FullScreen.setFullScreen(!widget.config.fullscreen); FullScreen.setFullScreen(!FullScreen.isFullScreen);
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -300,8 +298,9 @@ class _SheetViewerPageState extends State<SheetViewerPage>
icon: Icon( icon: Icon(
widget.config.fullscreen ? Icons.fullscreen_exit : Icons.fullscreen, widget.config.fullscreen ? Icons.fullscreen_exit : Icons.fullscreen,
), ),
tooltip: tooltip: widget.config.fullscreen
widget.config.fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen', ? 'Exit Fullscreen'
: 'Enter Fullscreen',
onPressed: _toggleFullscreen, onPressed: _toggleFullscreen,
), ),
IconButton( IconButton(
@@ -313,8 +312,9 @@ class _SheetViewerPageState extends State<SheetViewerPage>
icon: Icon( icon: Icon(
widget.config.twoPageMode ? Icons.filter_1 : Icons.filter_2, widget.config.twoPageMode ? Icons.filter_1 : Icons.filter_2,
), ),
tooltip: tooltip: widget.config.twoPageMode
widget.config.twoPageMode ? 'Single Page Mode' : 'Two Page Mode', ? 'Single Page Mode'
: 'Two Page Mode',
onPressed: _toggleTwoPageMode, onPressed: _toggleTwoPageMode,
), ),
], ],
@@ -346,8 +346,9 @@ class _SheetViewerPageState extends State<SheetViewerPage>
currentPageNumber: _currentPage, currentPageNumber: _currentPage,
config: widget.config, config: widget.config,
leftDrawingController: _leftDrawingController, leftDrawingController: _leftDrawingController,
rightDrawingController: rightDrawingController: widget.config.twoPageMode
widget.config.twoPageMode ? _rightDrawingController : null, ? _rightDrawingController
: null,
drawingEnabled: _isPaintMode, drawingEnabled: _isPaintMode,
); );

View File

@@ -86,7 +86,6 @@ class _EditSheetBottomSheetState extends State<EditSheetBottomSheet> {
const SizedBox(height: 12), const SizedBox(height: 12),
TextFormField( TextFormField(
controller: _composerController, controller: _composerController,
validator: _validateNotEmpty,
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Composer', labelText: 'Composer',
border: OutlineInputBorder(), border: OutlineInputBorder(),

View File

@@ -364,10 +364,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: objective_c name: objective_c
sha256: "983c7fa1501f6dcc0cb7af4e42072e9993cb28d73604d25ebf4dab08165d997e" sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.2.5" version: "9.3.0"
package_info_plus: package_info_plus:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -617,10 +617,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: source_span name: source_span
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.1" version: "1.10.2"
sqflite: sqflite:
dependency: transitive dependency: transitive
description: description:

View File

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 0.1.2 version: 0.2.0
environment: environment:
sdk: ^3.0.0 sdk: ^3.0.0