From 81a8a5dccc72fec3b670119a36d0524668eed9ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Zl=C3=A1mal?= Date: Sat, 30 Dec 2023 22:15:55 +0100 Subject: [PATCH 01/10] Implemented moving selected items to another page. Undo/redo is not handled correctly because old items are deleted and new created. --- lib/pages/editor/editor.dart | 96 ++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/lib/pages/editor/editor.dart b/lib/pages/editor/editor.dart index faca2a3ece..3fd99d1e11 100644 --- a/lib/pages/editor/editor.dart +++ b/lib/pages/editor/editor.dart @@ -585,9 +585,13 @@ class EditorState extends State { } void onDrawUpdate(ScaleUpdateDetails details) { + const distOffset=50.0; // when focal point moves at least this distance from the current page + // then move selection to new page + int pageOffset=0; // offset of selection to another page. Nonzero value indicates moving selection to another page + final page = coreInfo.pages[dragPageIndex!]; - final position = page.renderBox!.globalToLocal(details.focalPoint); - final offset = position - previousPosition; + Offset position = page.renderBox!.globalToLocal(details.focalPoint); + Offset offset = position - previousPosition; if (currentTool is Pen) { (currentTool as Pen).onDragUpdate(position, currentPressure); page.redrawStrokes(); @@ -601,13 +605,46 @@ class EditorState extends State { } else if (currentTool is Select) { Select select = currentTool as Select; if (select.doneSelecting) { + //log.info('Update selection position'); + double rectBottom=page.size.height; + double rectTop=0; + double cursorPosition=position.dy; + //log.info('page rect $rectTop to $rectBottom. Position is $cursorPosition'); + if (cursorPosition>rectBottom+distOffset){ + // selection should be moved to next page + //log.info('Selection should be moved to next page >'); + if (coreInfo.pages.length>select.selectResult.pageIndex+1){ + //log.info('Selection can be moved'); + offset=Offset(offset.dx,offset.dy-(rectBottom+distOffset)); // recalculate offset so entities moved to new page will be at correct position + pageOffset=1; + } + } + else{ + if (cursorPosition<(rectTop-distOffset)){ + // selection should be moved to previous page + //log.info('Selection should be moved to previous page <'); + if (select.selectResult.pageIndex>0){ + //log.info('Selection can be moved'); + offset=Offset(offset.dx,offset.dy+(rectBottom+distOffset)); + pageOffset=-1; + } + } + } for (Stroke stroke in select.selectResult.strokes) { stroke.shift(offset); } for (EditorImage image in select.selectResult.images) { image.dstRect = image.dstRect.shift(offset); } + // shift also selection path select.selectResult.path = select.selectResult.path.shift(offset); + if (pageOffset!=0) { + // before page redraw we need to move selected entities to another page + // this page will be redrawn later + //log.info('Moving selected item to new page and deleting them from original page'); + selectionOffsetPage(pageOffset); // move entities to new page and remove them from current one + } + } else { select.onDragUpdate(position); } @@ -616,8 +653,61 @@ class EditorState extends State { (currentTool as LaserPointer).onDragUpdate(position); page.redrawStrokes(); } + + if (pageOffset!=0){ + // now handle new page, cursor position and another things + // change index of drag page + dragPageIndex = onWhichPageIsFocalPoint(details.focalPoint); // update page and create paint rectangle + if (dragPageIndex==null){ + return; // page does not exist + } + final pageNew = coreInfo.pages[dragPageIndex!]; + // recalculate position according new drag page + position = pageNew.renderBox!.globalToLocal(details.focalPoint); + double rectBottom=pageNew.size.height; + //log.info('New page rect $rectTop to $rectBottom. Position on new page is $cursorPosition'); + offset=Offset(offset.dx,offset.dy-pageOffset*(rectBottom+distOffset)); // put offset to original page + setState(() {}); // force update of builder so movement of selection to another page is taken into account + pageNew.redrawStrokes(); // and finally redraw new page + } + previousPosition = position; - moveOffset += offset; + moveOffset += offset; // this value is keeped due to Undo/redo + } + + void selectionOffsetPage(int pageOffset){ + // selected items should be moved to another page + Select select = currentTool as Select; + // test if pages exist + final int oldPage=select.selectResult.pageIndex; + final int newPage=select.selectResult.pageIndex+pageOffset; + if (oldPage<0 || oldPage>coreInfo.pages.length-1){ + return; + } + if (newPage<0 || newPage>coreInfo.pages.length-1){ + return; + } + final pageOld = coreInfo.pages[oldPage]; + final pageNew = coreInfo.pages[newPage]; + final strokes = select.selectResult.strokes; + final images = select.selectResult.images; + + // remove from original page + for (Stroke stroke in strokes) { + pageOld.strokes.remove(stroke); + } + for (EditorImage image in images) { + pageOld.images.remove(image); + } + // add to new page + for (Stroke stroke in strokes) { + pageNew.strokes.add(stroke); + } + for (EditorImage image in images) { + pageNew.images.add(image); + } + // move selection to new page + select.selectResult.pageIndex+=pageOffset; } void onDrawEnd(ScaleEndDetails details) { From 41f605f843b2bb0cf59830e12a944d0016082835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Zl=C3=A1mal?= Date: Sun, 31 Dec 2023 08:47:32 +0100 Subject: [PATCH 02/10] Implemented moving selection across pages. Undo/redo works. In Select added pageIndexStart holding original page of selection. In EditorHistory added pageIndexStart - important only for move Solving issue #932 --- lib/data/editor/editor_history.dart | 6 ++ lib/data/tools/select.dart | 6 ++ lib/pages/editor/editor.dart | 97 ++++++++++++++++++++++------- 3 files changed, 88 insertions(+), 21 deletions(-) diff --git a/lib/data/editor/editor_history.dart b/lib/data/editor/editor_history.dart index f67cb5a922..3d39d87bde 100644 --- a/lib/data/editor/editor_history.dart +++ b/lib/data/editor/editor_history.dart @@ -122,8 +122,11 @@ class EditorHistoryItem { this.page, this.quillChange, this.colorChange, + this.pageIndexStart, // original page of selection before it was moved to another page }) : assert(type != EditorHistoryItemType.move || offset != null, 'Offset must be provided for move'), + assert(type != EditorHistoryItemType.move || pageIndexStart != null, + 'PageIndexStart must be provided for move'), assert(type != EditorHistoryItemType.deletePage || page != null, 'Page must be provided for deletePage'), assert(type != EditorHistoryItemType.insertPage || page != null, @@ -141,6 +144,7 @@ class EditorHistoryItem { final EditorHistoryItemType type; final int pageIndex; + final int? pageIndexStart; // original page of selected items before moved to another one final List strokes; final List images; final Rect? offset; @@ -151,6 +155,7 @@ class EditorHistoryItem { EditorHistoryItem copyWith({ EditorHistoryItemType? type, int? pageIndex, + int? pageIndexStart, List? strokes, List? images, Rect? offset, @@ -161,6 +166,7 @@ class EditorHistoryItem { return EditorHistoryItem( type: type ?? this.type, pageIndex: pageIndex ?? this.pageIndex, + pageIndexStart: pageIndexStart ?? this.pageIndexStart, strokes: strokes ?? this.strokes, images: images ?? this.images, offset: offset ?? this.offset, diff --git a/lib/data/tools/select.dart b/lib/data/tools/select.dart index 3117770746..6ca44822bc 100644 --- a/lib/data/tools/select.dart +++ b/lib/data/tools/select.dart @@ -18,6 +18,7 @@ class Select extends Tool { strokes: const [], images: const [], path: Path(), + pageIndexStart: -1, // page index when selection was created ); bool doneSelecting = false; @@ -57,6 +58,7 @@ class Select extends Tool { strokes: [], images: [], path: Path(), + pageIndexStart: pageIndex, // page index when selection was created ); selectResult.path.moveTo(position.dx, position.dy); onDragUpdate(position); @@ -127,12 +129,14 @@ class SelectResult { final List strokes; final List images; Path path; + int pageIndexStart; SelectResult({ required this.pageIndex, required this.strokes, required this.images, required this.path, + required this.pageIndexStart, }); bool get isEmpty { @@ -144,12 +148,14 @@ class SelectResult { List? strokes, List? images, Path? path, + int? pageIndexStart, }) { return SelectResult( pageIndex: pageIndex ?? this.pageIndex, strokes: strokes ?? this.strokes, images: images ?? this.images, path: path ?? this.path, + pageIndexStart: pageIndexStart ?? this.pageIndexStart, ); } } diff --git a/lib/pages/editor/editor.dart b/lib/pages/editor/editor.dart index 3fd99d1e11..5dba3294d9 100644 --- a/lib/pages/editor/editor.dart +++ b/lib/pages/editor/editor.dart @@ -411,11 +411,17 @@ class EditorState extends State { } case EditorHistoryItemType.move: + // all movement of selection was on one page for (Stroke stroke in item.strokes) { stroke.shift(Offset( -item.offset!.left, -item.offset!.top, )); + if (item.pageIndex!=item.pageIndexStart) { + // stroke must be on original page when selection movements started + int pageIndexStart = item.pageIndexStart ?? -1; + moveStrokeToPage(stroke, item.pageIndex, pageIndexStart); + } } Select select = Select.currentSelect; if (select.doneSelecting) { @@ -423,6 +429,10 @@ class EditorState extends State { -item.offset!.left, -item.offset!.top, )); + if (item.pageIndex!=item.pageIndexStart) { + int pageIndexStart = item.pageIndexStart ?? -1; + select.selectResult.pageIndex=pageIndexStart; + } } for (EditorImage image in item.images) { image.dstRect = Rect.fromLTRB( @@ -431,6 +441,11 @@ class EditorState extends State { image.dstRect.right - item.offset!.right, image.dstRect.bottom - item.offset!.bottom, ); + if (item.pageIndex!=item.pageIndexStart) { + // image must be on original page when selection movements started + int pageIndexStart = item.pageIndexStart ?? 0; + moveImageToPage(image, item.pageIndex, pageIndexStart); + } } case EditorHistoryItemType.quillChange: @@ -469,6 +484,8 @@ class EditorState extends State { undo(item.copyWith(type: EditorHistoryItemType.deletePage)); case EditorHistoryItemType.move: undo(item.copyWith( + pageIndex: item.pageIndexStart, // exchange page and Start page + pageIndexStart: item.pageIndex, offset: Rect.fromLTRB( -item.offset!.left, -item.offset!.top, @@ -638,7 +655,9 @@ class EditorState extends State { } // shift also selection path select.selectResult.path = select.selectResult.path.shift(offset); + if (pageOffset!=0) { + offset = position - previousPosition; // and put real offset back // before page redraw we need to move selected entities to another page // this page will be redrawn later //log.info('Moving selected item to new page and deleting them from original page'); @@ -666,8 +685,9 @@ class EditorState extends State { position = pageNew.renderBox!.globalToLocal(details.focalPoint); double rectBottom=pageNew.size.height; //log.info('New page rect $rectTop to $rectBottom. Position on new page is $cursorPosition'); - offset=Offset(offset.dx,offset.dy-pageOffset*(rectBottom+distOffset)); // put offset to original page - setState(() {}); // force update of builder so movement of selection to another page is taken into account + // recalculate the offset as if the selection were always moving to one page. Important for undo/redo + offset=Offset(offset.dx,offset.dy-pageOffset*(rectBottom+distOffset)); + // setState(() {}); // force update of builder so movement of selection to another page is taken into account pageNew.redrawStrokes(); // and finally redraw new page } @@ -675,6 +695,47 @@ class EditorState extends State { moveOffset += offset; // this value is keeped due to Undo/redo } + bool moveStrokeToPage(Stroke stroke,int pageIndexOrig,int pageIndexDest){ + // move stroke from page pageIndexOrig to pageIndexDest + // setState must be called before use to track changes + if (pageIndexOrig==pageIndexDest || pageIndexOrig==-1 || pageIndexDest==-1){ + return false; // no page change + } + if (pageIndexOrig<0 || pageIndexOrig>coreInfo.pages.length-1){ + return false; + } + if (pageIndexDest<0 || pageIndexDest>coreInfo.pages.length-1){ + return false; + } + final pageOrig = coreInfo.pages[pageIndexOrig]; + final pageDest = coreInfo.pages[pageIndexDest]; + // remove from original page + pageOrig.strokes.remove(stroke); + // add to new page + pageDest.strokes.add(stroke); + return true; + } + bool moveImageToPage(EditorImage image,int pageIndexOrig,int pageIndexDest){ + // move image from page pageIndexOrig to pageIndexDest + // setState must be called before use to track changes + if (pageIndexOrig==pageIndexDest || pageIndexOrig==-1 || pageIndexDest==-1){ + return false; // no page change + } + if (pageIndexOrig<0 || pageIndexOrig>coreInfo.pages.length-1){ + return false; + } + if (pageIndexDest<0 || pageIndexDest>coreInfo.pages.length-1){ + return false; + } + final pageOrig = coreInfo.pages[pageIndexOrig]; + final pageDest = coreInfo.pages[pageIndexDest]; + // remove from original page + pageOrig.images.remove(image); + // add to new page + pageDest.images.add(image); + return true; + } + void selectionOffsetPage(int pageOffset){ // selected items should be moved to another page Select select = currentTool as Select; @@ -687,27 +748,20 @@ class EditorState extends State { if (newPage<0 || newPage>coreInfo.pages.length-1){ return; } - final pageOld = coreInfo.pages[oldPage]; - final pageNew = coreInfo.pages[newPage]; final strokes = select.selectResult.strokes; final images = select.selectResult.images; - // remove from original page - for (Stroke stroke in strokes) { - pageOld.strokes.remove(stroke); - } - for (EditorImage image in images) { - pageOld.images.remove(image); - } - // add to new page - for (Stroke stroke in strokes) { - pageNew.strokes.add(stroke); - } - for (EditorImage image in images) { - pageNew.images.add(image); - } - // move selection to new page - select.selectResult.pageIndex+=pageOffset; + setState(() { + // remove from original page + for (Stroke stroke in strokes) { + moveStrokeToPage(stroke, oldPage, newPage); + } + for (EditorImage image in images) { + moveImageToPage(image, oldPage, newPage); + } + // move selection to new page + select.selectResult.pageIndex += pageOffset; + }); } void onDrawEnd(ScaleEndDetails details) { @@ -745,7 +799,8 @@ class EditorState extends State { if (select.doneSelecting) { history.recordChange(EditorHistoryItem( type: EditorHistoryItemType.move, - pageIndex: dragPageIndex!, + pageIndexStart: select.selectResult.pageIndexStart, // use page index of page where entities were aat start of movement + pageIndex: select.selectResult.pageIndex, // use page index of page where entities are now strokes: select.selectResult.strokes, images: select.selectResult.images, offset: Rect.fromLTRB( From d5b71424958151b99367512376ef8e763ccaa90f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Zl=C3=A1mal?= Date: Mon, 1 Jan 2024 10:57:26 +0100 Subject: [PATCH 03/10] Fixed recording history when image itself is moved. pageStartIndex must be set. --- lib/pages/editor/editor.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/editor/editor.dart b/lib/pages/editor/editor.dart index 5dba3294d9..4f3259fc4d 100644 --- a/lib/pages/editor/editor.dart +++ b/lib/pages/editor/editor.dart @@ -862,6 +862,7 @@ class EditorState extends State { history.recordChange(EditorHistoryItem( type: EditorHistoryItemType.move, pageIndex: image.pageIndex, + pageIndexStart: image.pageIndex, strokes: [], images: [image], offset: offset, From 71795947fc96c2592f9bafc07d1d8fb78f93bc0a Mon Sep 17 00:00:00 2001 From: Adil Hanney Date: Tue, 27 Feb 2024 11:13:29 +0000 Subject: [PATCH 04/10] chore: dart format --- lib/data/editor/editor_history.dart | 3 +- lib/data/tools/select.dart | 4 +- lib/pages/editor/editor.dart | 123 ++++++++++++++++------------ 3 files changed, 73 insertions(+), 57 deletions(-) diff --git a/lib/data/editor/editor_history.dart b/lib/data/editor/editor_history.dart index 3d39d87bde..cb438de2bd 100644 --- a/lib/data/editor/editor_history.dart +++ b/lib/data/editor/editor_history.dart @@ -144,7 +144,8 @@ class EditorHistoryItem { final EditorHistoryItemType type; final int pageIndex; - final int? pageIndexStart; // original page of selected items before moved to another one + final int? + pageIndexStart; // original page of selected items before moved to another one final List strokes; final List images; final Rect? offset; diff --git a/lib/data/tools/select.dart b/lib/data/tools/select.dart index 6ca44822bc..48e11729c1 100644 --- a/lib/data/tools/select.dart +++ b/lib/data/tools/select.dart @@ -18,7 +18,7 @@ class Select extends Tool { strokes: const [], images: const [], path: Path(), - pageIndexStart: -1, // page index when selection was created + pageIndexStart: -1, // page index when selection was created ); bool doneSelecting = false; @@ -58,7 +58,7 @@ class Select extends Tool { strokes: [], images: [], path: Path(), - pageIndexStart: pageIndex, // page index when selection was created + pageIndexStart: pageIndex, // page index when selection was created ); selectResult.path.moveTo(position.dx, position.dy); onDragUpdate(position); diff --git a/lib/pages/editor/editor.dart b/lib/pages/editor/editor.dart index 4f3259fc4d..0298a0205c 100644 --- a/lib/pages/editor/editor.dart +++ b/lib/pages/editor/editor.dart @@ -417,7 +417,7 @@ class EditorState extends State { -item.offset!.left, -item.offset!.top, )); - if (item.pageIndex!=item.pageIndexStart) { + if (item.pageIndex != item.pageIndexStart) { // stroke must be on original page when selection movements started int pageIndexStart = item.pageIndexStart ?? -1; moveStrokeToPage(stroke, item.pageIndex, pageIndexStart); @@ -429,9 +429,9 @@ class EditorState extends State { -item.offset!.left, -item.offset!.top, )); - if (item.pageIndex!=item.pageIndexStart) { + if (item.pageIndex != item.pageIndexStart) { int pageIndexStart = item.pageIndexStart ?? -1; - select.selectResult.pageIndex=pageIndexStart; + select.selectResult.pageIndex = pageIndexStart; } } for (EditorImage image in item.images) { @@ -441,7 +441,7 @@ class EditorState extends State { image.dstRect.right - item.offset!.right, image.dstRect.bottom - item.offset!.bottom, ); - if (item.pageIndex!=item.pageIndexStart) { + if (item.pageIndex != item.pageIndexStart) { // image must be on original page when selection movements started int pageIndexStart = item.pageIndexStart ?? 0; moveImageToPage(image, item.pageIndex, pageIndexStart); @@ -484,14 +484,14 @@ class EditorState extends State { undo(item.copyWith(type: EditorHistoryItemType.deletePage)); case EditorHistoryItemType.move: undo(item.copyWith( - pageIndex: item.pageIndexStart, // exchange page and Start page + pageIndex: item.pageIndexStart, // exchange page and Start page pageIndexStart: item.pageIndex, offset: Rect.fromLTRB( - -item.offset!.left, - -item.offset!.top, - -item.offset!.right, - -item.offset!.bottom, - ))); + -item.offset!.left, + -item.offset!.top, + -item.offset!.right, + -item.offset!.bottom, + ))); case EditorHistoryItemType.quillChange: undo(item.copyWith(type: EditorHistoryItemType.quillUndoneChange)); case EditorHistoryItemType.quillUndoneChange: // this will never happen @@ -602,9 +602,11 @@ class EditorState extends State { } void onDrawUpdate(ScaleUpdateDetails details) { - const distOffset=50.0; // when focal point moves at least this distance from the current page - // then move selection to new page - int pageOffset=0; // offset of selection to another page. Nonzero value indicates moving selection to another page + const distOffset = + 50.0; // when focal point moves at least this distance from the current page + // then move selection to new page + int pageOffset = + 0; // offset of selection to another page. Nonzero value indicates moving selection to another page final page = coreInfo.pages[dragPageIndex!]; Offset position = page.renderBox!.globalToLocal(details.focalPoint); @@ -623,27 +625,30 @@ class EditorState extends State { Select select = currentTool as Select; if (select.doneSelecting) { //log.info('Update selection position'); - double rectBottom=page.size.height; - double rectTop=0; - double cursorPosition=position.dy; + double rectBottom = page.size.height; + double rectTop = 0; + double cursorPosition = position.dy; //log.info('page rect $rectTop to $rectBottom. Position is $cursorPosition'); - if (cursorPosition>rectBottom+distOffset){ + if (cursorPosition > rectBottom + distOffset) { // selection should be moved to next page //log.info('Selection should be moved to next page >'); - if (coreInfo.pages.length>select.selectResult.pageIndex+1){ + if (coreInfo.pages.length > select.selectResult.pageIndex + 1) { //log.info('Selection can be moved'); - offset=Offset(offset.dx,offset.dy-(rectBottom+distOffset)); // recalculate offset so entities moved to new page will be at correct position - pageOffset=1; + offset = Offset( + offset.dx, + offset.dy - + (rectBottom + + distOffset)); // recalculate offset so entities moved to new page will be at correct position + pageOffset = 1; } - } - else{ - if (cursorPosition<(rectTop-distOffset)){ + } else { + if (cursorPosition < (rectTop - distOffset)) { // selection should be moved to previous page //log.info('Selection should be moved to previous page <'); - if (select.selectResult.pageIndex>0){ + if (select.selectResult.pageIndex > 0) { //log.info('Selection can be moved'); - offset=Offset(offset.dx,offset.dy+(rectBottom+distOffset)); - pageOffset=-1; + offset = Offset(offset.dx, offset.dy + (rectBottom + distOffset)); + pageOffset = -1; } } } @@ -656,14 +661,14 @@ class EditorState extends State { // shift also selection path select.selectResult.path = select.selectResult.path.shift(offset); - if (pageOffset!=0) { - offset = position - previousPosition; // and put real offset back + if (pageOffset != 0) { + offset = position - previousPosition; // and put real offset back // before page redraw we need to move selected entities to another page // this page will be redrawn later //log.info('Moving selected item to new page and deleting them from original page'); - selectionOffsetPage(pageOffset); // move entities to new page and remove them from current one + selectionOffsetPage( + pageOffset); // move entities to new page and remove them from current one } - } else { select.onDragUpdate(position); } @@ -673,38 +678,42 @@ class EditorState extends State { page.redrawStrokes(); } - if (pageOffset!=0){ + if (pageOffset != 0) { // now handle new page, cursor position and another things // change index of drag page - dragPageIndex = onWhichPageIsFocalPoint(details.focalPoint); // update page and create paint rectangle - if (dragPageIndex==null){ + dragPageIndex = onWhichPageIsFocalPoint( + details.focalPoint); // update page and create paint rectangle + if (dragPageIndex == null) { return; // page does not exist } final pageNew = coreInfo.pages[dragPageIndex!]; // recalculate position according new drag page position = pageNew.renderBox!.globalToLocal(details.focalPoint); - double rectBottom=pageNew.size.height; + double rectBottom = pageNew.size.height; //log.info('New page rect $rectTop to $rectBottom. Position on new page is $cursorPosition'); // recalculate the offset as if the selection were always moving to one page. Important for undo/redo - offset=Offset(offset.dx,offset.dy-pageOffset*(rectBottom+distOffset)); - // setState(() {}); // force update of builder so movement of selection to another page is taken into account - pageNew.redrawStrokes(); // and finally redraw new page + offset = + Offset(offset.dx, offset.dy - pageOffset * (rectBottom + distOffset)); + // setState(() {}); // force update of builder so movement of selection to another page is taken into account + pageNew.redrawStrokes(); // and finally redraw new page } previousPosition = position; moveOffset += offset; // this value is keeped due to Undo/redo } - bool moveStrokeToPage(Stroke stroke,int pageIndexOrig,int pageIndexDest){ + bool moveStrokeToPage(Stroke stroke, int pageIndexOrig, int pageIndexDest) { // move stroke from page pageIndexOrig to pageIndexDest // setState must be called before use to track changes - if (pageIndexOrig==pageIndexDest || pageIndexOrig==-1 || pageIndexDest==-1){ - return false; // no page change + if (pageIndexOrig == pageIndexDest || + pageIndexOrig == -1 || + pageIndexDest == -1) { + return false; // no page change } - if (pageIndexOrig<0 || pageIndexOrig>coreInfo.pages.length-1){ + if (pageIndexOrig < 0 || pageIndexOrig > coreInfo.pages.length - 1) { return false; } - if (pageIndexDest<0 || pageIndexDest>coreInfo.pages.length-1){ + if (pageIndexDest < 0 || pageIndexDest > coreInfo.pages.length - 1) { return false; } final pageOrig = coreInfo.pages[pageIndexOrig]; @@ -715,16 +724,20 @@ class EditorState extends State { pageDest.strokes.add(stroke); return true; } - bool moveImageToPage(EditorImage image,int pageIndexOrig,int pageIndexDest){ + + bool moveImageToPage( + EditorImage image, int pageIndexOrig, int pageIndexDest) { // move image from page pageIndexOrig to pageIndexDest // setState must be called before use to track changes - if (pageIndexOrig==pageIndexDest || pageIndexOrig==-1 || pageIndexDest==-1){ - return false; // no page change + if (pageIndexOrig == pageIndexDest || + pageIndexOrig == -1 || + pageIndexDest == -1) { + return false; // no page change } - if (pageIndexOrig<0 || pageIndexOrig>coreInfo.pages.length-1){ + if (pageIndexOrig < 0 || pageIndexOrig > coreInfo.pages.length - 1) { return false; } - if (pageIndexDest<0 || pageIndexDest>coreInfo.pages.length-1){ + if (pageIndexDest < 0 || pageIndexDest > coreInfo.pages.length - 1) { return false; } final pageOrig = coreInfo.pages[pageIndexOrig]; @@ -736,16 +749,16 @@ class EditorState extends State { return true; } - void selectionOffsetPage(int pageOffset){ + void selectionOffsetPage(int pageOffset) { // selected items should be moved to another page Select select = currentTool as Select; // test if pages exist - final int oldPage=select.selectResult.pageIndex; - final int newPage=select.selectResult.pageIndex+pageOffset; - if (oldPage<0 || oldPage>coreInfo.pages.length-1){ + final int oldPage = select.selectResult.pageIndex; + final int newPage = select.selectResult.pageIndex + pageOffset; + if (oldPage < 0 || oldPage > coreInfo.pages.length - 1) { return; } - if (newPage<0 || newPage>coreInfo.pages.length-1){ + if (newPage < 0 || newPage > coreInfo.pages.length - 1) { return; } final strokes = select.selectResult.strokes; @@ -799,8 +812,10 @@ class EditorState extends State { if (select.doneSelecting) { history.recordChange(EditorHistoryItem( type: EditorHistoryItemType.move, - pageIndexStart: select.selectResult.pageIndexStart, // use page index of page where entities were aat start of movement - pageIndex: select.selectResult.pageIndex, // use page index of page where entities are now + pageIndexStart: select.selectResult + .pageIndexStart, // use page index of page where entities were aat start of movement + pageIndex: select.selectResult + .pageIndex, // use page index of page where entities are now strokes: select.selectResult.strokes, images: select.selectResult.images, offset: Rect.fromLTRB( From 47ce7e4bec968d223d3d634fb00e240bbd0436d6 Mon Sep 17 00:00:00 2001 From: Adil Hanney Date: Tue, 27 Feb 2024 11:17:40 +0000 Subject: [PATCH 05/10] chore: use dartdoc for pageIndexStart --- lib/data/editor/editor_history.dart | 14 ++++++++++---- lib/data/tools/select.dart | 8 +++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/data/editor/editor_history.dart b/lib/data/editor/editor_history.dart index cb438de2bd..04a746520e 100644 --- a/lib/data/editor/editor_history.dart +++ b/lib/data/editor/editor_history.dart @@ -116,17 +116,17 @@ class EditorHistoryItem { EditorHistoryItem({ required this.type, required this.pageIndex, + this.pageIndexStart, required this.strokes, required this.images, this.offset, this.page, this.quillChange, this.colorChange, - this.pageIndexStart, // original page of selection before it was moved to another page }) : assert(type != EditorHistoryItemType.move || offset != null, 'Offset must be provided for move'), assert(type != EditorHistoryItemType.move || pageIndexStart != null, - 'PageIndexStart must be provided for move'), + 'pageIndexStart must be provided for move'), assert(type != EditorHistoryItemType.deletePage || page != null, 'Page must be provided for deletePage'), assert(type != EditorHistoryItemType.insertPage || page != null, @@ -144,8 +144,14 @@ class EditorHistoryItem { final EditorHistoryItemType type; final int pageIndex; - final int? - pageIndexStart; // original page of selected items before moved to another one + + /// Original page of selected items before being moved to another one. + /// + /// This can be the same as [pageIndex] + /// if the items were moved within the same page. + /// + /// See also: [SelectResult.pageIndexStart] + final int? pageIndexStart; final List strokes; final List images; final Rect? offset; diff --git a/lib/data/tools/select.dart b/lib/data/tools/select.dart index 48e11729c1..5984b2a6e5 100644 --- a/lib/data/tools/select.dart +++ b/lib/data/tools/select.dart @@ -18,7 +18,7 @@ class Select extends Tool { strokes: const [], images: const [], path: Path(), - pageIndexStart: -1, // page index when selection was created + pageIndexStart: -1, ); bool doneSelecting = false; @@ -58,7 +58,7 @@ class Select extends Tool { strokes: [], images: [], path: Path(), - pageIndexStart: pageIndex, // page index when selection was created + pageIndexStart: pageIndex, ); selectResult.path.moveTo(position.dx, position.dy); onDragUpdate(position); @@ -129,7 +129,9 @@ class SelectResult { final List strokes; final List images; Path path; - int pageIndexStart; + + /// The page index when the items were selected. + final int pageIndexStart; SelectResult({ required this.pageIndex, From 7e35a88da4b056050a78652abd40eb674b7385a5 Mon Sep 17 00:00:00 2001 From: Adil Hanney Date: Tue, 27 Feb 2024 11:33:00 +0000 Subject: [PATCH 06/10] ref: minor refactoring --- lib/pages/editor/editor.dart | 54 +++++++++++++++++------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/lib/pages/editor/editor.dart b/lib/pages/editor/editor.dart index 0298a0205c..2f6aab0c77 100644 --- a/lib/pages/editor/editor.dart +++ b/lib/pages/editor/editor.dart @@ -411,16 +411,13 @@ class EditorState extends State { } case EditorHistoryItemType.move: - // all movement of selection was on one page for (Stroke stroke in item.strokes) { stroke.shift(Offset( -item.offset!.left, -item.offset!.top, )); if (item.pageIndex != item.pageIndexStart) { - // stroke must be on original page when selection movements started - int pageIndexStart = item.pageIndexStart ?? -1; - moveStrokeToPage(stroke, item.pageIndex, pageIndexStart); + moveStrokeToPage(stroke, item.pageIndex, item.pageIndexStart!); } } Select select = Select.currentSelect; @@ -430,8 +427,8 @@ class EditorState extends State { -item.offset!.top, )); if (item.pageIndex != item.pageIndexStart) { - int pageIndexStart = item.pageIndexStart ?? -1; - select.selectResult.pageIndex = pageIndexStart; + // move selection area to the original page + select.selectResult.pageIndex = item.pageIndexStart!; } } for (EditorImage image in item.images) { @@ -442,9 +439,7 @@ class EditorState extends State { image.dstRect.bottom - item.offset!.bottom, ); if (item.pageIndex != item.pageIndexStart) { - // image must be on original page when selection movements started - int pageIndexStart = item.pageIndexStart ?? 0; - moveImageToPage(image, item.pageIndex, pageIndexStart); + moveImageToPage(image, item.pageIndex, item.pageIndexStart!); } } @@ -484,14 +479,16 @@ class EditorState extends State { undo(item.copyWith(type: EditorHistoryItemType.deletePage)); case EditorHistoryItemType.move: undo(item.copyWith( - pageIndex: item.pageIndexStart, // exchange page and Start page - pageIndexStart: item.pageIndex, - offset: Rect.fromLTRB( - -item.offset!.left, - -item.offset!.top, - -item.offset!.right, - -item.offset!.bottom, - ))); + // swap pageIndex and pageIndexStart + pageIndex: item.pageIndexStart, + pageIndexStart: item.pageIndex, + offset: Rect.fromLTRB( + -item.offset!.left, + -item.offset!.top, + -item.offset!.right, + -item.offset!.bottom, + ), + )); case EditorHistoryItemType.quillChange: undo(item.copyWith(type: EditorHistoryItemType.quillUndoneChange)); case EditorHistoryItemType.quillUndoneChange: // this will never happen @@ -562,6 +559,10 @@ class EditorState extends State { } } + /// When the selection is dragged this far past the top/bottom of the page, + /// the selection will be moved to the previous/next page. + static const changePageThreshold = 50.0; + void onDrawStart(ScaleStartDetails details) { final page = coreInfo.pages[dragPageIndex!]; final position = page.renderBox!.globalToLocal(details.focalPoint); @@ -602,12 +603,6 @@ class EditorState extends State { } void onDrawUpdate(ScaleUpdateDetails details) { - const distOffset = - 50.0; // when focal point moves at least this distance from the current page - // then move selection to new page - int pageOffset = - 0; // offset of selection to another page. Nonzero value indicates moving selection to another page - final page = coreInfo.pages[dragPageIndex!]; Offset position = page.renderBox!.globalToLocal(details.focalPoint); Offset offset = position - previousPosition; @@ -629,7 +624,7 @@ class EditorState extends State { double rectTop = 0; double cursorPosition = position.dy; //log.info('page rect $rectTop to $rectBottom. Position is $cursorPosition'); - if (cursorPosition > rectBottom + distOffset) { + if (cursorPosition > rectBottom + changePageThreshold) { // selection should be moved to next page //log.info('Selection should be moved to next page >'); if (coreInfo.pages.length > select.selectResult.pageIndex + 1) { @@ -638,16 +633,17 @@ class EditorState extends State { offset.dx, offset.dy - (rectBottom + - distOffset)); // recalculate offset so entities moved to new page will be at correct position + changePageThreshold)); // recalculate offset so entities moved to new page will be at correct position pageOffset = 1; } } else { - if (cursorPosition < (rectTop - distOffset)) { + if (cursorPosition < (rectTop - changePageThreshold)) { // selection should be moved to previous page //log.info('Selection should be moved to previous page <'); if (select.selectResult.pageIndex > 0) { //log.info('Selection can be moved'); - offset = Offset(offset.dx, offset.dy + (rectBottom + distOffset)); + offset = Offset( + offset.dx, offset.dy + (rectBottom + changePageThreshold)); pageOffset = -1; } } @@ -692,8 +688,8 @@ class EditorState extends State { double rectBottom = pageNew.size.height; //log.info('New page rect $rectTop to $rectBottom. Position on new page is $cursorPosition'); // recalculate the offset as if the selection were always moving to one page. Important for undo/redo - offset = - Offset(offset.dx, offset.dy - pageOffset * (rectBottom + distOffset)); + offset = Offset(offset.dx, + offset.dy - pageOffset * (rectBottom + changePageThreshold)); // setState(() {}); // force update of builder so movement of selection to another page is taken into account pageNew.redrawStrokes(); // and finally redraw new page } From 321a86eee7d06e5fe34098e7563c5983c418a5ca Mon Sep 17 00:00:00 2001 From: Adil Hanney Date: Tue, 27 Feb 2024 11:45:56 +0000 Subject: [PATCH 07/10] ref: clean up formatting --- lib/pages/editor/editor.dart | 42 ++++++++++++++---------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/lib/pages/editor/editor.dart b/lib/pages/editor/editor.dart index 2f6aab0c77..c091152af5 100644 --- a/lib/pages/editor/editor.dart +++ b/lib/pages/editor/editor.dart @@ -619,42 +619,33 @@ class EditorState extends State { } else if (currentTool is Select) { Select select = currentTool as Select; if (select.doneSelecting) { - //log.info('Update selection position'); - double rectBottom = page.size.height; - double rectTop = 0; - double cursorPosition = position.dy; - //log.info('page rect $rectTop to $rectBottom. Position is $cursorPosition'); - if (cursorPosition > rectBottom + changePageThreshold) { - // selection should be moved to next page - //log.info('Selection should be moved to next page >'); + int pageOffset = 0; // between -1 and 1 + if (position.dy > page.size.height + changePageThreshold) { + // Selection is dragged past the bottom of the original page if (coreInfo.pages.length > select.selectResult.pageIndex + 1) { - //log.info('Selection can be moved'); offset = Offset( - offset.dx, - offset.dy - - (rectBottom + - changePageThreshold)); // recalculate offset so entities moved to new page will be at correct position + offset.dx, + offset.dy - (page.size.height + changePageThreshold), + ); pageOffset = 1; } - } else { - if (cursorPosition < (rectTop - changePageThreshold)) { - // selection should be moved to previous page - //log.info('Selection should be moved to previous page <'); - if (select.selectResult.pageIndex > 0) { - //log.info('Selection can be moved'); - offset = Offset( - offset.dx, offset.dy + (rectBottom + changePageThreshold)); - pageOffset = -1; - } + } else if (position.dy < -changePageThreshold) { + // Selection is dragged past the top of the original page + if (select.selectResult.pageIndex > 0) { + offset = Offset( + offset.dx, + offset.dy + (page.size.height + changePageThreshold), + ); + pageOffset = -1; } } + for (Stroke stroke in select.selectResult.strokes) { stroke.shift(offset); } for (EditorImage image in select.selectResult.images) { image.dstRect = image.dstRect.shift(offset); } - // shift also selection path select.selectResult.path = select.selectResult.path.shift(offset); if (pageOffset != 0) { @@ -685,11 +676,10 @@ class EditorState extends State { final pageNew = coreInfo.pages[dragPageIndex!]; // recalculate position according new drag page position = pageNew.renderBox!.globalToLocal(details.focalPoint); - double rectBottom = pageNew.size.height; //log.info('New page rect $rectTop to $rectBottom. Position on new page is $cursorPosition'); // recalculate the offset as if the selection were always moving to one page. Important for undo/redo offset = Offset(offset.dx, - offset.dy - pageOffset * (rectBottom + changePageThreshold)); + offset.dy - pageOffset * (page.size.height + changePageThreshold)); // setState(() {}); // force update of builder so movement of selection to another page is taken into account pageNew.redrawStrokes(); // and finally redraw new page } From ae711b3a5426d3977254e53686af5640ec9af7d9 Mon Sep 17 00:00:00 2001 From: Adil Hanney Date: Tue, 27 Feb 2024 11:46:54 +0000 Subject: [PATCH 08/10] ref: avoid duplicate if statement --- lib/pages/editor/editor.dart | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/pages/editor/editor.dart b/lib/pages/editor/editor.dart index c091152af5..3afc74145a 100644 --- a/lib/pages/editor/editor.dart +++ b/lib/pages/editor/editor.dart @@ -655,6 +655,25 @@ class EditorState extends State { //log.info('Moving selected item to new page and deleting them from original page'); selectionOffsetPage( pageOffset); // move entities to new page and remove them from current one + + // now handle new page, cursor position and another things + // change index of drag page + dragPageIndex = onWhichPageIsFocalPoint( + details.focalPoint); // update page and create paint rectangle + if (dragPageIndex == null) { + return; // page does not exist + } + final pageNew = coreInfo.pages[dragPageIndex!]; + // recalculate position according new drag page + position = pageNew.renderBox!.globalToLocal(details.focalPoint); + //log.info('New page rect $rectTop to $rectBottom. Position on new page is $cursorPosition'); + // recalculate the offset as if the selection were always moving to one page. Important for undo/redo + offset = Offset( + offset.dx, + offset.dy - + pageOffset * (page.size.height + changePageThreshold)); + // setState(() {}); // force update of builder so movement of selection to another page is taken into account + pageNew.redrawStrokes(); // and finally redraw new page } } else { select.onDragUpdate(position); @@ -665,25 +684,6 @@ class EditorState extends State { page.redrawStrokes(); } - if (pageOffset != 0) { - // now handle new page, cursor position and another things - // change index of drag page - dragPageIndex = onWhichPageIsFocalPoint( - details.focalPoint); // update page and create paint rectangle - if (dragPageIndex == null) { - return; // page does not exist - } - final pageNew = coreInfo.pages[dragPageIndex!]; - // recalculate position according new drag page - position = pageNew.renderBox!.globalToLocal(details.focalPoint); - //log.info('New page rect $rectTop to $rectBottom. Position on new page is $cursorPosition'); - // recalculate the offset as if the selection were always moving to one page. Important for undo/redo - offset = Offset(offset.dx, - offset.dy - pageOffset * (page.size.height + changePageThreshold)); - // setState(() {}); // force update of builder so movement of selection to another page is taken into account - pageNew.redrawStrokes(); // and finally redraw new page - } - previousPosition = position; moveOffset += offset; // this value is keeped due to Undo/redo } From 3350fc44b79a1a6b2c4e3cde02ffb89d96f237e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Zl=C3=A1mal?= Date: Mon, 1 Apr 2024 14:14:59 +0200 Subject: [PATCH 09/10] pageIndexStart of selection is set to current page immediatley after selection movement is stored to history. This fixes problem when selection is moved across pages, and then moved on page (in this case pageIndexStart was set according to the first movement of selection) --- lib/data/tools/select.dart | 2 +- lib/pages/editor/editor.dart | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/data/tools/select.dart b/lib/data/tools/select.dart index 5984b2a6e5..c3c8700beb 100644 --- a/lib/data/tools/select.dart +++ b/lib/data/tools/select.dart @@ -131,7 +131,7 @@ class SelectResult { Path path; /// The page index when the items were selected. - final int pageIndexStart; + int pageIndexStart; SelectResult({ required this.pageIndex, diff --git a/lib/pages/editor/editor.dart b/lib/pages/editor/editor.dart index df46de3607..7111af7bda 100644 --- a/lib/pages/editor/editor.dart +++ b/lib/pages/editor/editor.dart @@ -604,6 +604,10 @@ class EditorState extends State { } void onDrawUpdate(ScaleUpdateDetails details) { + if (dragPageIndex==null){ + // cursor is somewhere between pages, do not respond until its page will be recognized + return; + } final page = coreInfo.pages[dragPageIndex!]; Offset position = page.renderBox!.globalToLocal(details.focalPoint); Offset offset = position - previousPosition; @@ -765,6 +769,10 @@ class EditorState extends State { } void onDrawEnd(ScaleEndDetails details) { + if (dragPageIndex==null){ + // page cannot be determined from cursor position + return; + } final page = coreInfo.pages[dragPageIndex!]; bool shouldSave = true; setState(() { @@ -820,6 +828,7 @@ class EditorState extends State { moveOffset.dy, ), )); + select.selectResult.pageIndexStart=select.selectResult.pageIndex; // set starting page index to current page } else { shouldSave = false; select.onDragEnd(page.strokes, page.images); From ce101ee425a0434ebab36292a12d0dff742c3120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Zl=C3=A1mal?= Date: Wed, 19 Jun 2024 12:42:50 +0200 Subject: [PATCH 10/10] fix: Merged main to MoveSelectionToAnotherPage branch and fixed conflict in editor.dart --- .github/workflows/android-play-store.yml | 6 +- .github/workflows/android.yml | 136 +- .github/workflows/check-flutter-submodule.yml | 1 + .github/workflows/ios.yml | 45 +- .github/workflows/linux.yml | 47 +- .github/workflows/macos.yml | 37 +- .github/workflows/onyxsdk_pen_tests.yml | 4 +- .github/workflows/tests.yml | 4 +- .github/workflows/update-landing-page.yml | 6 +- .github/workflows/windows.yml | 31 +- README-cs.md | 6 +- README-de.md | 6 +- README-zh-CN.md | 6 +- README-zh-TW.md | 6 +- README.md | 6 +- .../app/FlutterMultiDexApplication.java | 25 - assets/audio/white-noise-8117.md | 1 + assets/audio/white-noise-8117.ogg | Bin 0 -> 10121 bytes assets/images/nextcloud-logo.svg | 1 + .../undraw_mobile_encryption_re_yw3o.svg | 1 + assets/images/undraw_my_files_swob.svg | 1 + devtools_options.yaml | 3 + flatpak/com.adilhanney.saber.metainfo.xml | 59 +- installers/desktop_inno_script.iss | 2 +- lib/components/canvas/_stroke.dart | 20 +- .../canvas/canvas_gesture_detector.dart | 51 +- lib/components/canvas/canvas_image.dart | 7 +- .../canvas/canvas_image_dialog.dart | 2 +- .../canvas/hud/canvas_gesture_lock_btn.dart | 4 +- lib/components/canvas/hud/canvas_hud.dart | 5 +- .../canvas/hud/canvas_zoom_indicator.dart | 4 +- .../canvas/image/pdf_editor_image.dart | 2 +- lib/components/canvas/inner_canvas.dart | 1 - lib/components/canvas/interactive_canvas.dart | 194 +- lib/components/home/delete_folder_button.dart | 2 +- lib/components/home/move_note_button.dart | 2 +- lib/components/home/new_folder_dialog.dart | 2 +- lib/components/home/preview_card.dart | 2 +- lib/components/home/rename_folder_button.dart | 2 +- lib/components/home/rename_note_button.dart | 2 +- lib/components/home/syncing_button.dart | 2 +- lib/components/home/uploading_indicator.dart | 2 +- lib/components/misc/faq.dart | 62 +- lib/components/nextcloud/done_login_step.dart | 133 + lib/components/nextcloud/enc_login_step.dart | 159 + lib/components/nextcloud/login_group.dart | 354 -- lib/components/nextcloud/nc_login_step.dart | 314 ++ .../settings/nextcloud_profile.dart | 102 +- lib/components/settings/settings_color.dart | 10 +- .../settings/settings_directory_selector.dart | 184 + .../settings/settings_selection.dart | 4 +- lib/components/settings/update_manager.dart | 39 +- .../theming/dynamic_material_app.dart | 12 +- lib/components/toolbar/color_bar.dart | 6 +- .../toolbar/editor_bottom_sheet.dart | 21 + lib/components/toolbar/size_picker.dart | 2 +- lib/components/toolbar/toolbar.dart | 22 +- lib/data/editor/editor_core_info.dart | 1 + lib/data/editor/pencil_sound.dart | 116 + lib/data/extensions/string_extensions.dart | 2 + lib/data/file_manager/file_manager.dart | 53 +- lib/data/nextcloud/errors.dart | 10 + lib/data/nextcloud/file_syncer.dart | 76 +- lib/data/nextcloud/login_flow.dart | 86 + lib/data/nextcloud/nc_http_overrides.dart | 4 + .../nextcloud/nextcloud_client_extension.dart | 52 +- lib/data/nextcloud/readable_bytes.dart | 26 + lib/data/prefs.dart | 88 +- lib/data/routes.dart | 1 - lib/data/saber_version.dart | 58 + lib/data/tools/shape_pen.dart | 5 +- lib/data/version.dart | 6 +- lib/i18n/_missing_translations.yaml | 673 +++- lib/i18n/community/strings_ar.i18n.yaml | 88 +- lib/i18n/community/strings_cs.i18n.yaml | 89 +- lib/i18n/community/strings_de.i18n.yaml | 190 +- lib/i18n/community/strings_es.i18n.yaml | 90 +- lib/i18n/community/strings_fa.i18n.yaml | 82 +- lib/i18n/community/strings_fr.i18n.yaml | 90 +- lib/i18n/community/strings_he.i18n.yaml | 90 +- lib/i18n/community/strings_hu.i18n.yaml | 88 +- lib/i18n/community/strings_it.i18n.yaml | 89 +- lib/i18n/community/strings_ja.i18n.yaml | 88 +- lib/i18n/community/strings_pt_BR.i18n.yaml | 90 +- lib/i18n/community/strings_ru.i18n.yaml | 93 +- lib/i18n/community/strings_tr.i18n.yaml | 88 +- .../community/strings_zh_Hans_CN.i18n.yaml | 94 +- .../community/strings_zh_Hant_TW.i18n.yaml | 93 +- lib/i18n/strings.g.dart | 3181 ++++++++++------- lib/i18n/strings.i18n.yaml | 89 +- lib/main_common.dart | 49 +- lib/main_simulator.dart | 12 + lib/pages/editor/editor.dart | 77 +- lib/pages/home/browse.dart | 2 +- lib/pages/home/recent_notes.dart | 2 +- lib/pages/home/settings.dart | 35 +- lib/pages/user/login.dart | 202 +- lib/pages/user/profile_page.dart | 102 - linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 4 + macos/Podfile | 2 +- macos/Runner.xcodeproj/project.pbxproj | 3 + metadata/ar/changelogs/21040.txt | 3 + metadata/ar/changelogs/210403.txt | 1 + metadata/ar/changelogs/22000.txt | 2 + metadata/ar/changelogs/220003.txt | 1 + metadata/ar/changelogs/23000.txt | 3 + metadata/ar/changelogs/230003.txt | 1 + metadata/ar/changelogs/23010.txt | 3 + metadata/ar/changelogs/230103.txt | 1 + metadata/ar/changelogs/23020.txt | 4 + metadata/ar/changelogs/230203.txt | 1 + metadata/ar/changelogs/23030.txt | 3 + metadata/ar/changelogs/230303.txt | 1 + metadata/cs/changelogs/21040.txt | 3 + metadata/cs/changelogs/210403.txt | 1 + metadata/cs/changelogs/22000.txt | 2 + metadata/cs/changelogs/220003.txt | 1 + metadata/cs/changelogs/23000.txt | 3 + metadata/cs/changelogs/230003.txt | 1 + metadata/cs/changelogs/23010.txt | 3 + metadata/cs/changelogs/230103.txt | 1 + metadata/cs/changelogs/23020.txt | 4 + metadata/cs/changelogs/230203.txt | 1 + metadata/cs/changelogs/23030.txt | 3 + metadata/cs/changelogs/230303.txt | 1 + metadata/de/changelogs/21040.txt | 3 + metadata/de/changelogs/210403.txt | 1 + metadata/de/changelogs/22000.txt | 2 + metadata/de/changelogs/220003.txt | 1 + metadata/de/changelogs/23000.txt | 3 + metadata/de/changelogs/230003.txt | 1 + metadata/de/changelogs/23010.txt | 3 + metadata/de/changelogs/230103.txt | 1 + metadata/de/changelogs/23020.txt | 4 + metadata/de/changelogs/230203.txt | 1 + metadata/de/changelogs/23030.txt | 3 + metadata/de/changelogs/230303.txt | 1 + metadata/en-US/changelogs/21040.txt | 3 + metadata/en-US/changelogs/210403.txt | 1 + metadata/en-US/changelogs/22000.txt | 2 + metadata/en-US/changelogs/220003.txt | 1 + metadata/en-US/changelogs/23000.txt | 3 + metadata/en-US/changelogs/230003.txt | 1 + metadata/en-US/changelogs/23010.txt | 3 + metadata/en-US/changelogs/230103.txt | 1 + metadata/en-US/changelogs/23020.txt | 4 + metadata/en-US/changelogs/230203.txt | 1 + metadata/en-US/changelogs/23030.txt | 3 + metadata/en-US/changelogs/230303.txt | 1 + metadata/es/changelogs/21040.txt | 3 + metadata/es/changelogs/210403.txt | 1 + metadata/es/changelogs/22000.txt | 2 + metadata/es/changelogs/220003.txt | 1 + metadata/es/changelogs/23000.txt | 3 + metadata/es/changelogs/230003.txt | 1 + metadata/es/changelogs/23010.txt | 3 + metadata/es/changelogs/230103.txt | 1 + metadata/es/changelogs/23020.txt | 4 + metadata/es/changelogs/230203.txt | 1 + metadata/es/changelogs/23030.txt | 3 + metadata/es/changelogs/230303.txt | 1 + metadata/fa/changelogs/21040.txt | 3 + metadata/fa/changelogs/210403.txt | 1 + metadata/fa/changelogs/22000.txt | 2 + metadata/fa/changelogs/220003.txt | 1 + metadata/fa/changelogs/23000.txt | 3 + metadata/fa/changelogs/230003.txt | 1 + metadata/fa/changelogs/23010.txt | 3 + metadata/fa/changelogs/230103.txt | 1 + metadata/fa/changelogs/23020.txt | 4 + metadata/fa/changelogs/230203.txt | 1 + metadata/fa/changelogs/23030.txt | 3 + metadata/fa/changelogs/230303.txt | 1 + metadata/fr/changelogs/21040.txt | 3 + metadata/fr/changelogs/210403.txt | 1 + metadata/fr/changelogs/22000.txt | 2 + metadata/fr/changelogs/220003.txt | 1 + metadata/fr/changelogs/23000.txt | 3 + metadata/fr/changelogs/230003.txt | 1 + metadata/fr/changelogs/23010.txt | 3 + metadata/fr/changelogs/230103.txt | 1 + metadata/fr/changelogs/23020.txt | 4 + metadata/fr/changelogs/230203.txt | 1 + metadata/fr/changelogs/23030.txt | 3 + metadata/fr/changelogs/230303.txt | 1 + metadata/he/changelogs/21040.txt | 3 + metadata/he/changelogs/210403.txt | 1 + metadata/he/changelogs/22000.txt | 2 + metadata/he/changelogs/220003.txt | 1 + metadata/he/changelogs/23000.txt | 3 + metadata/he/changelogs/230003.txt | 1 + metadata/he/changelogs/23010.txt | 3 + metadata/he/changelogs/230103.txt | 1 + metadata/he/changelogs/23020.txt | 4 + metadata/he/changelogs/230203.txt | 1 + metadata/he/changelogs/23030.txt | 3 + metadata/he/changelogs/230303.txt | 1 + metadata/hu/changelogs/21040.txt | 3 + metadata/hu/changelogs/210403.txt | 1 + metadata/hu/changelogs/22000.txt | 2 + metadata/hu/changelogs/220003.txt | 1 + metadata/hu/changelogs/23000.txt | 3 + metadata/hu/changelogs/230003.txt | 1 + metadata/hu/changelogs/23010.txt | 3 + metadata/hu/changelogs/230103.txt | 1 + metadata/hu/changelogs/23020.txt | 4 + metadata/hu/changelogs/230203.txt | 1 + metadata/hu/changelogs/23030.txt | 3 + metadata/hu/changelogs/230303.txt | 1 + metadata/it/changelogs/21040.txt | 3 + metadata/it/changelogs/210403.txt | 1 + metadata/it/changelogs/22000.txt | 2 + metadata/it/changelogs/220003.txt | 1 + metadata/it/changelogs/23000.txt | 3 + metadata/it/changelogs/230003.txt | 1 + metadata/it/changelogs/23010.txt | 3 + metadata/it/changelogs/230103.txt | 1 + metadata/it/changelogs/23020.txt | 4 + metadata/it/changelogs/230203.txt | 1 + metadata/it/changelogs/23030.txt | 3 + metadata/it/changelogs/230303.txt | 1 + metadata/ja/changelogs/21040.txt | 3 + metadata/ja/changelogs/210403.txt | 1 + metadata/ja/changelogs/22000.txt | 2 + metadata/ja/changelogs/220003.txt | 1 + metadata/ja/changelogs/23000.txt | 3 + metadata/ja/changelogs/230003.txt | 1 + metadata/ja/changelogs/23010.txt | 3 + metadata/ja/changelogs/230103.txt | 1 + metadata/ja/changelogs/23020.txt | 4 + metadata/ja/changelogs/230203.txt | 1 + metadata/ja/changelogs/23030.txt | 3 + metadata/ja/changelogs/230303.txt | 1 + metadata/pt-BR/changelogs/21040.txt | 3 + metadata/pt-BR/changelogs/210403.txt | 1 + metadata/pt-BR/changelogs/22000.txt | 2 + metadata/pt-BR/changelogs/220003.txt | 1 + metadata/pt-BR/changelogs/23000.txt | 3 + metadata/pt-BR/changelogs/230003.txt | 1 + metadata/pt-BR/changelogs/23010.txt | 3 + metadata/pt-BR/changelogs/230103.txt | 1 + metadata/pt-BR/changelogs/23020.txt | 4 + metadata/pt-BR/changelogs/230203.txt | 1 + metadata/pt-BR/changelogs/23030.txt | 3 + metadata/pt-BR/changelogs/230303.txt | 1 + metadata/ru/changelogs/21040.txt | 3 + metadata/ru/changelogs/210403.txt | 1 + metadata/ru/changelogs/22000.txt | 2 + metadata/ru/changelogs/220003.txt | 1 + metadata/ru/changelogs/23000.txt | 3 + metadata/ru/changelogs/230003.txt | 1 + metadata/ru/changelogs/23010.txt | 3 + metadata/ru/changelogs/230103.txt | 1 + metadata/ru/changelogs/23020.txt | 4 + metadata/ru/changelogs/230203.txt | 1 + metadata/ru/changelogs/23030.txt | 3 + metadata/ru/changelogs/230303.txt | 1 + metadata/tr/changelogs/21040.txt | 3 + metadata/tr/changelogs/210403.txt | 1 + metadata/tr/changelogs/22000.txt | 2 + metadata/tr/changelogs/220003.txt | 1 + metadata/tr/changelogs/23000.txt | 3 + metadata/tr/changelogs/230003.txt | 1 + metadata/tr/changelogs/23010.txt | 3 + metadata/tr/changelogs/230103.txt | 1 + metadata/tr/changelogs/23020.txt | 4 + metadata/tr/changelogs/230203.txt | 1 + metadata/tr/changelogs/23030.txt | 3 + metadata/tr/changelogs/230303.txt | 1 + metadata/zh-Hans-CN/changelogs/21040.txt | 3 + metadata/zh-Hans-CN/changelogs/210403.txt | 1 + metadata/zh-Hans-CN/changelogs/22000.txt | 2 + metadata/zh-Hans-CN/changelogs/220003.txt | 1 + metadata/zh-Hans-CN/changelogs/23000.txt | 3 + metadata/zh-Hans-CN/changelogs/230003.txt | 1 + metadata/zh-Hans-CN/changelogs/23010.txt | 3 + metadata/zh-Hans-CN/changelogs/230103.txt | 1 + metadata/zh-Hans-CN/changelogs/23020.txt | 4 + metadata/zh-Hans-CN/changelogs/230203.txt | 1 + metadata/zh-Hans-CN/changelogs/23030.txt | 3 + metadata/zh-Hans-CN/changelogs/230303.txt | 1 + metadata/zh-Hant-TW/changelogs/21040.txt | 3 + metadata/zh-Hant-TW/changelogs/210403.txt | 1 + metadata/zh-Hant-TW/changelogs/22000.txt | 2 + metadata/zh-Hant-TW/changelogs/220003.txt | 1 + metadata/zh-Hant-TW/changelogs/23000.txt | 3 + metadata/zh-Hant-TW/changelogs/230003.txt | 1 + metadata/zh-Hant-TW/changelogs/23010.txt | 3 + metadata/zh-Hant-TW/changelogs/230103.txt | 1 + metadata/zh-Hant-TW/changelogs/23020.txt | 4 + metadata/zh-Hant-TW/changelogs/230203.txt | 1 + metadata/zh-Hant-TW/changelogs/23030.txt | 3 + metadata/zh-Hant-TW/changelogs/230303.txt | 1 + patches/alt_package_name.sh | 5 - patches/remove_proprietary_dependencies.sh | 2 +- pubspec.lock | 406 ++- pubspec.yaml | 34 +- scripts/apply_version.dart | 261 ++ scripts/apply_version.sh | 179 - scripts/process_i18n_pr.sh | 16 + scripts/src/fix_spelling.dart | 10 + scripts/translate_changelogs.dart | 29 +- scripts/translate_missing_translations.dart | 75 +- snap/snapcraft.yaml | 5 +- submodules/flutter | 2 +- test/editor_undo_redo_test.dart | 3 + .../v18_pencil.sbn2.dark_isolatedDiff.png | Bin 0 -> 19353 bytes .../v18_pencil.sbn2.dark_maskedDiff.png | Bin 0 -> 345710 bytes .../v18_pencil.sbn2.dark_masterImage.png | Bin 0 -> 350889 bytes .../v18_pencil.sbn2.dark_testImage.png | Bin 0 -> 344511 bytes .../v18_pencil.sbn2.light_isolatedDiff.png | Bin 0 -> 25327 bytes .../v18_pencil.sbn2.light_maskedDiff.png | Bin 0 -> 353430 bytes .../v18_pencil.sbn2.light_masterImage.png | Bin 0 -> 350322 bytes .../v18_pencil.sbn2.light_testImage.png | Bin 0 -> 342835 bytes .../v19_pens.sbn2.dark_isolatedDiff.png | Bin 0 -> 9408 bytes .../v19_pens.sbn2.dark_maskedDiff.png | Bin 0 -> 125530 bytes .../v19_pens.sbn2.dark_masterImage.png | Bin 0 -> 132988 bytes .../failures/v19_pens.sbn2.dark_testImage.png | Bin 0 -> 129045 bytes .../v19_pens.sbn2.light_isolatedDiff.png | Bin 0 -> 23459 bytes .../v19_pens.sbn2.light_maskedDiff.png | Bin 0 -> 139516 bytes .../v19_pens.sbn2.light_masterImage.png | Bin 0 -> 132607 bytes .../v19_pens.sbn2.light_testImage.png | Bin 0 -> 128526 bytes ..._stress_test_179.sbn.dark_isolatedDiff.png | Bin 0 -> 26116 bytes ...v6_stress_test_179.sbn.dark_maskedDiff.png | Bin 0 -> 850797 bytes ...6_stress_test_179.sbn.dark_masterImage.png | Bin 0 -> 852701 bytes .../v6_stress_test_179.sbn.dark_testImage.png | Bin 0 -> 843521 bytes ...stress_test_179.sbn.light_isolatedDiff.png | Bin 0 -> 28661 bytes ...6_stress_test_179.sbn.light_maskedDiff.png | Bin 0 -> 815122 bytes ..._stress_test_179.sbn.light_masterImage.png | Bin 0 -> 816091 bytes ...v6_stress_test_179.sbn.light_testImage.png | Bin 0 -> 806558 bytes .../v9_quill.sbn.dark_isolatedDiff.png | Bin 0 -> 49204 bytes .../failures/v9_quill.sbn.dark_maskedDiff.png | Bin 0 -> 53936 bytes .../v9_quill.sbn.dark_masterImage.png | Bin 0 -> 61339 bytes test/failures/v9_quill.sbn.dark_testImage.png | Bin 0 -> 44219 bytes .../v9_quill.sbn.light_isolatedDiff.png | Bin 0 -> 53806 bytes .../v9_quill.sbn.light_maskedDiff.png | Bin 0 -> 65359 bytes .../v9_quill.sbn.light_masterImage.png | Bin 0 -> 61184 bytes .../failures/v9_quill.sbn.light_testImage.png | Bin 0 -> 44763 bytes test/fm_write_stream_test.dart | 6 + test/login_validation_test.dart | 99 - test/nc_deletion_test.dart | 4 +- test/sbn_examples/v18_pencil.sbn2.dark.png | Bin 350889 -> 350606 bytes test/sbn_examples/v18_pencil.sbn2.light.png | Bin 350322 -> 349781 bytes test/sbn_examples/v18_pencil.sbn2.pdf.png | Bin 87428 -> 87083 bytes test/sbn_examples/v19_pens.sbn2.dark.png | Bin 132988 -> 132943 bytes test/sbn_examples/v19_pens.sbn2.light.png | Bin 132607 -> 132543 bytes test/sbn_examples/v19_pens.sbn2.pdf.png | Bin 43918 -> 43897 bytes test/sbn_examples/v19_separate_assets.sba | Bin 0 -> 238120 bytes .../v6_stress_test_179.sbn.dark.png | Bin 852701 -> 852540 bytes .../v6_stress_test_179.sbn.light.png | Bin 816091 -> 815548 bytes .../v6_stress_test_179.sbn.pdf.png | Bin 203271 -> 203211 bytes test/sbn_examples/v9_quill.sbn.dark.png | Bin 61339 -> 60451 bytes test/sbn_examples/v9_quill.sbn.light.png | Bin 61184 -> 60418 bytes test/sbn_examples/v9_quill.sbn.pdf.png | Bin 17080 -> 16713 bytes test/utils/test_mock_channel_handlers.dart | 23 + test/version_test.dart | 32 +- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + windows/runner/Runner.rc | 4 +- 361 files changed, 6565 insertions(+), 3652 deletions(-) delete mode 100644 android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java create mode 100644 assets/audio/white-noise-8117.md create mode 100644 assets/audio/white-noise-8117.ogg create mode 100644 assets/images/nextcloud-logo.svg create mode 100644 assets/images/undraw_mobile_encryption_re_yw3o.svg create mode 100644 assets/images/undraw_my_files_swob.svg create mode 100644 devtools_options.yaml create mode 100644 lib/components/nextcloud/done_login_step.dart create mode 100644 lib/components/nextcloud/enc_login_step.dart delete mode 100644 lib/components/nextcloud/login_group.dart create mode 100644 lib/components/nextcloud/nc_login_step.dart create mode 100644 lib/components/settings/settings_directory_selector.dart create mode 100644 lib/data/editor/pencil_sound.dart create mode 100644 lib/data/nextcloud/errors.dart create mode 100644 lib/data/nextcloud/login_flow.dart create mode 100644 lib/data/nextcloud/readable_bytes.dart create mode 100644 lib/data/saber_version.dart create mode 100644 lib/main_simulator.dart delete mode 100644 lib/pages/user/profile_page.dart create mode 100644 metadata/ar/changelogs/21040.txt create mode 100644 metadata/ar/changelogs/210403.txt create mode 100644 metadata/ar/changelogs/22000.txt create mode 100644 metadata/ar/changelogs/220003.txt create mode 100644 metadata/ar/changelogs/23000.txt create mode 100644 metadata/ar/changelogs/230003.txt create mode 100644 metadata/ar/changelogs/23010.txt create mode 100644 metadata/ar/changelogs/230103.txt create mode 100644 metadata/ar/changelogs/23020.txt create mode 100644 metadata/ar/changelogs/230203.txt create mode 100644 metadata/ar/changelogs/23030.txt create mode 100644 metadata/ar/changelogs/230303.txt create mode 100644 metadata/cs/changelogs/21040.txt create mode 100644 metadata/cs/changelogs/210403.txt create mode 100644 metadata/cs/changelogs/22000.txt create mode 100644 metadata/cs/changelogs/220003.txt create mode 100644 metadata/cs/changelogs/23000.txt create mode 100644 metadata/cs/changelogs/230003.txt create mode 100644 metadata/cs/changelogs/23010.txt create mode 100644 metadata/cs/changelogs/230103.txt create mode 100644 metadata/cs/changelogs/23020.txt create mode 100644 metadata/cs/changelogs/230203.txt create mode 100644 metadata/cs/changelogs/23030.txt create mode 100644 metadata/cs/changelogs/230303.txt create mode 100644 metadata/de/changelogs/21040.txt create mode 100644 metadata/de/changelogs/210403.txt create mode 100644 metadata/de/changelogs/22000.txt create mode 100644 metadata/de/changelogs/220003.txt create mode 100644 metadata/de/changelogs/23000.txt create mode 100644 metadata/de/changelogs/230003.txt create mode 100644 metadata/de/changelogs/23010.txt create mode 100644 metadata/de/changelogs/230103.txt create mode 100644 metadata/de/changelogs/23020.txt create mode 100644 metadata/de/changelogs/230203.txt create mode 100644 metadata/de/changelogs/23030.txt create mode 100644 metadata/de/changelogs/230303.txt create mode 100644 metadata/en-US/changelogs/21040.txt create mode 100644 metadata/en-US/changelogs/210403.txt create mode 100644 metadata/en-US/changelogs/22000.txt create mode 100644 metadata/en-US/changelogs/220003.txt create mode 100644 metadata/en-US/changelogs/23000.txt create mode 100644 metadata/en-US/changelogs/230003.txt create mode 100644 metadata/en-US/changelogs/23010.txt create mode 100644 metadata/en-US/changelogs/230103.txt create mode 100644 metadata/en-US/changelogs/23020.txt create mode 100644 metadata/en-US/changelogs/230203.txt create mode 100644 metadata/en-US/changelogs/23030.txt create mode 100644 metadata/en-US/changelogs/230303.txt create mode 100644 metadata/es/changelogs/21040.txt create mode 100644 metadata/es/changelogs/210403.txt create mode 100644 metadata/es/changelogs/22000.txt create mode 100644 metadata/es/changelogs/220003.txt create mode 100644 metadata/es/changelogs/23000.txt create mode 100644 metadata/es/changelogs/230003.txt create mode 100644 metadata/es/changelogs/23010.txt create mode 100644 metadata/es/changelogs/230103.txt create mode 100644 metadata/es/changelogs/23020.txt create mode 100644 metadata/es/changelogs/230203.txt create mode 100644 metadata/es/changelogs/23030.txt create mode 100644 metadata/es/changelogs/230303.txt create mode 100644 metadata/fa/changelogs/21040.txt create mode 100644 metadata/fa/changelogs/210403.txt create mode 100644 metadata/fa/changelogs/22000.txt create mode 100644 metadata/fa/changelogs/220003.txt create mode 100644 metadata/fa/changelogs/23000.txt create mode 100644 metadata/fa/changelogs/230003.txt create mode 100644 metadata/fa/changelogs/23010.txt create mode 100644 metadata/fa/changelogs/230103.txt create mode 100644 metadata/fa/changelogs/23020.txt create mode 100644 metadata/fa/changelogs/230203.txt create mode 100644 metadata/fa/changelogs/23030.txt create mode 100644 metadata/fa/changelogs/230303.txt create mode 100644 metadata/fr/changelogs/21040.txt create mode 100644 metadata/fr/changelogs/210403.txt create mode 100644 metadata/fr/changelogs/22000.txt create mode 100644 metadata/fr/changelogs/220003.txt create mode 100644 metadata/fr/changelogs/23000.txt create mode 100644 metadata/fr/changelogs/230003.txt create mode 100644 metadata/fr/changelogs/23010.txt create mode 100644 metadata/fr/changelogs/230103.txt create mode 100644 metadata/fr/changelogs/23020.txt create mode 100644 metadata/fr/changelogs/230203.txt create mode 100644 metadata/fr/changelogs/23030.txt create mode 100644 metadata/fr/changelogs/230303.txt create mode 100644 metadata/he/changelogs/21040.txt create mode 100644 metadata/he/changelogs/210403.txt create mode 100644 metadata/he/changelogs/22000.txt create mode 100644 metadata/he/changelogs/220003.txt create mode 100644 metadata/he/changelogs/23000.txt create mode 100644 metadata/he/changelogs/230003.txt create mode 100644 metadata/he/changelogs/23010.txt create mode 100644 metadata/he/changelogs/230103.txt create mode 100644 metadata/he/changelogs/23020.txt create mode 100644 metadata/he/changelogs/230203.txt create mode 100644 metadata/he/changelogs/23030.txt create mode 100644 metadata/he/changelogs/230303.txt create mode 100644 metadata/hu/changelogs/21040.txt create mode 100644 metadata/hu/changelogs/210403.txt create mode 100644 metadata/hu/changelogs/22000.txt create mode 100644 metadata/hu/changelogs/220003.txt create mode 100644 metadata/hu/changelogs/23000.txt create mode 100644 metadata/hu/changelogs/230003.txt create mode 100644 metadata/hu/changelogs/23010.txt create mode 100644 metadata/hu/changelogs/230103.txt create mode 100644 metadata/hu/changelogs/23020.txt create mode 100644 metadata/hu/changelogs/230203.txt create mode 100644 metadata/hu/changelogs/23030.txt create mode 100644 metadata/hu/changelogs/230303.txt create mode 100644 metadata/it/changelogs/21040.txt create mode 100644 metadata/it/changelogs/210403.txt create mode 100644 metadata/it/changelogs/22000.txt create mode 100644 metadata/it/changelogs/220003.txt create mode 100644 metadata/it/changelogs/23000.txt create mode 100644 metadata/it/changelogs/230003.txt create mode 100644 metadata/it/changelogs/23010.txt create mode 100644 metadata/it/changelogs/230103.txt create mode 100644 metadata/it/changelogs/23020.txt create mode 100644 metadata/it/changelogs/230203.txt create mode 100644 metadata/it/changelogs/23030.txt create mode 100644 metadata/it/changelogs/230303.txt create mode 100644 metadata/ja/changelogs/21040.txt create mode 100644 metadata/ja/changelogs/210403.txt create mode 100644 metadata/ja/changelogs/22000.txt create mode 100644 metadata/ja/changelogs/220003.txt create mode 100644 metadata/ja/changelogs/23000.txt create mode 100644 metadata/ja/changelogs/230003.txt create mode 100644 metadata/ja/changelogs/23010.txt create mode 100644 metadata/ja/changelogs/230103.txt create mode 100644 metadata/ja/changelogs/23020.txt create mode 100644 metadata/ja/changelogs/230203.txt create mode 100644 metadata/ja/changelogs/23030.txt create mode 100644 metadata/ja/changelogs/230303.txt create mode 100644 metadata/pt-BR/changelogs/21040.txt create mode 100644 metadata/pt-BR/changelogs/210403.txt create mode 100644 metadata/pt-BR/changelogs/22000.txt create mode 100644 metadata/pt-BR/changelogs/220003.txt create mode 100644 metadata/pt-BR/changelogs/23000.txt create mode 100644 metadata/pt-BR/changelogs/230003.txt create mode 100644 metadata/pt-BR/changelogs/23010.txt create mode 100644 metadata/pt-BR/changelogs/230103.txt create mode 100644 metadata/pt-BR/changelogs/23020.txt create mode 100644 metadata/pt-BR/changelogs/230203.txt create mode 100644 metadata/pt-BR/changelogs/23030.txt create mode 100644 metadata/pt-BR/changelogs/230303.txt create mode 100644 metadata/ru/changelogs/21040.txt create mode 100644 metadata/ru/changelogs/210403.txt create mode 100644 metadata/ru/changelogs/22000.txt create mode 100644 metadata/ru/changelogs/220003.txt create mode 100644 metadata/ru/changelogs/23000.txt create mode 100644 metadata/ru/changelogs/230003.txt create mode 100644 metadata/ru/changelogs/23010.txt create mode 100644 metadata/ru/changelogs/230103.txt create mode 100644 metadata/ru/changelogs/23020.txt create mode 100644 metadata/ru/changelogs/230203.txt create mode 100644 metadata/ru/changelogs/23030.txt create mode 100644 metadata/ru/changelogs/230303.txt create mode 100644 metadata/tr/changelogs/21040.txt create mode 100644 metadata/tr/changelogs/210403.txt create mode 100644 metadata/tr/changelogs/22000.txt create mode 100644 metadata/tr/changelogs/220003.txt create mode 100644 metadata/tr/changelogs/23000.txt create mode 100644 metadata/tr/changelogs/230003.txt create mode 100644 metadata/tr/changelogs/23010.txt create mode 100644 metadata/tr/changelogs/230103.txt create mode 100644 metadata/tr/changelogs/23020.txt create mode 100644 metadata/tr/changelogs/230203.txt create mode 100644 metadata/tr/changelogs/23030.txt create mode 100644 metadata/tr/changelogs/230303.txt create mode 100644 metadata/zh-Hans-CN/changelogs/21040.txt create mode 100644 metadata/zh-Hans-CN/changelogs/210403.txt create mode 100644 metadata/zh-Hans-CN/changelogs/22000.txt create mode 100644 metadata/zh-Hans-CN/changelogs/220003.txt create mode 100644 metadata/zh-Hans-CN/changelogs/23000.txt create mode 100644 metadata/zh-Hans-CN/changelogs/230003.txt create mode 100644 metadata/zh-Hans-CN/changelogs/23010.txt create mode 100644 metadata/zh-Hans-CN/changelogs/230103.txt create mode 100644 metadata/zh-Hans-CN/changelogs/23020.txt create mode 100644 metadata/zh-Hans-CN/changelogs/230203.txt create mode 100644 metadata/zh-Hans-CN/changelogs/23030.txt create mode 100644 metadata/zh-Hans-CN/changelogs/230303.txt create mode 100644 metadata/zh-Hant-TW/changelogs/21040.txt create mode 100644 metadata/zh-Hant-TW/changelogs/210403.txt create mode 100644 metadata/zh-Hant-TW/changelogs/22000.txt create mode 100644 metadata/zh-Hant-TW/changelogs/220003.txt create mode 100644 metadata/zh-Hant-TW/changelogs/23000.txt create mode 100644 metadata/zh-Hant-TW/changelogs/230003.txt create mode 100644 metadata/zh-Hant-TW/changelogs/23010.txt create mode 100644 metadata/zh-Hant-TW/changelogs/230103.txt create mode 100644 metadata/zh-Hant-TW/changelogs/23020.txt create mode 100644 metadata/zh-Hant-TW/changelogs/230203.txt create mode 100644 metadata/zh-Hant-TW/changelogs/23030.txt create mode 100644 metadata/zh-Hant-TW/changelogs/230303.txt delete mode 100755 patches/alt_package_name.sh create mode 100644 scripts/apply_version.dart delete mode 100755 scripts/apply_version.sh create mode 100644 scripts/process_i18n_pr.sh create mode 100644 scripts/src/fix_spelling.dart create mode 100644 test/failures/v18_pencil.sbn2.dark_isolatedDiff.png create mode 100644 test/failures/v18_pencil.sbn2.dark_maskedDiff.png create mode 100644 test/failures/v18_pencil.sbn2.dark_masterImage.png create mode 100644 test/failures/v18_pencil.sbn2.dark_testImage.png create mode 100644 test/failures/v18_pencil.sbn2.light_isolatedDiff.png create mode 100644 test/failures/v18_pencil.sbn2.light_maskedDiff.png create mode 100644 test/failures/v18_pencil.sbn2.light_masterImage.png create mode 100644 test/failures/v18_pencil.sbn2.light_testImage.png create mode 100644 test/failures/v19_pens.sbn2.dark_isolatedDiff.png create mode 100644 test/failures/v19_pens.sbn2.dark_maskedDiff.png create mode 100644 test/failures/v19_pens.sbn2.dark_masterImage.png create mode 100644 test/failures/v19_pens.sbn2.dark_testImage.png create mode 100644 test/failures/v19_pens.sbn2.light_isolatedDiff.png create mode 100644 test/failures/v19_pens.sbn2.light_maskedDiff.png create mode 100644 test/failures/v19_pens.sbn2.light_masterImage.png create mode 100644 test/failures/v19_pens.sbn2.light_testImage.png create mode 100644 test/failures/v6_stress_test_179.sbn.dark_isolatedDiff.png create mode 100644 test/failures/v6_stress_test_179.sbn.dark_maskedDiff.png create mode 100644 test/failures/v6_stress_test_179.sbn.dark_masterImage.png create mode 100644 test/failures/v6_stress_test_179.sbn.dark_testImage.png create mode 100644 test/failures/v6_stress_test_179.sbn.light_isolatedDiff.png create mode 100644 test/failures/v6_stress_test_179.sbn.light_maskedDiff.png create mode 100644 test/failures/v6_stress_test_179.sbn.light_masterImage.png create mode 100644 test/failures/v6_stress_test_179.sbn.light_testImage.png create mode 100644 test/failures/v9_quill.sbn.dark_isolatedDiff.png create mode 100644 test/failures/v9_quill.sbn.dark_maskedDiff.png create mode 100644 test/failures/v9_quill.sbn.dark_masterImage.png create mode 100644 test/failures/v9_quill.sbn.dark_testImage.png create mode 100644 test/failures/v9_quill.sbn.light_isolatedDiff.png create mode 100644 test/failures/v9_quill.sbn.light_maskedDiff.png create mode 100644 test/failures/v9_quill.sbn.light_masterImage.png create mode 100644 test/failures/v9_quill.sbn.light_testImage.png delete mode 100644 test/login_validation_test.dart create mode 100644 test/sbn_examples/v19_separate_assets.sba diff --git a/.github/workflows/android-play-store.yml b/.github/workflows/android-play-store.yml index ff3ed0062d..58d3df290a 100644 --- a/.github/workflows/android-play-store.yml +++ b/.github/workflows/android-play-store.yml @@ -6,12 +6,16 @@ on: push: tags: - '**' + branches: + - '**' + paths: + - .github/workflows/android-play-store.yml jobs: build-aab: name: Build AAB for the Play Store runs-on: ubuntu-latest - timeout-minutes: 20 + timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index e93ca9ff7f..c119b2d3ee 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -6,37 +6,18 @@ on: push: tags: - '**' + branches: + - '**' + paths: + - .github/workflows/android.yml jobs: - get-version: - name: Get version - runs-on: ubuntu-latest - timeout-minutes: 1 - outputs: - buildName: ${{ steps.get_version.outputs.buildName }} - buildNumber: ${{ steps.get_version.outputs.buildNumber }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Get version - id: get_version - run: | - # get buildName from lib/data/version.dart - buildName=$(grep -oP "(?<=buildName = ').*(?=')" lib/data/version.dart) - echo "buildName=$buildName" >> $GITHUB_OUTPUT - - # get buildNumber from lib/data/version.dart - buildNumber=$(grep -oP '(?<=buildNumber = ).*(?=;)' lib/data/version.dart) - echo "buildNumber=$buildNumber" >> $GITHUB_OUTPUT - build-apk: name: Build APK runs-on: ubuntu-latest - needs: get-version permissions: contents: write - timeout-minutes: 20 + timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v4 @@ -106,7 +87,8 @@ jobs: - name: Rename signed apk id: rename_apk run: | - apkName="Saber_v${{ needs.get-version.outputs.buildName }}.apk" + buildName=$(grep -oP "(?<=buildName = ').*(?=')" lib/data/version.dart) + apkName="Saber_v${buildName}.apk" mv ${{ steps.sign_app.outputs.signedFile }} output/$apkName echo "apkName=$apkName" >> $GITHUB_OUTPUT @@ -126,10 +108,9 @@ jobs: build-foss-apk: name: Build FOSS APK runs-on: ubuntu-latest - needs: get-version permissions: contents: write - timeout-minutes: 20 + timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v4 @@ -203,7 +184,8 @@ jobs: - name: Rename signed apk id: rename_apk run: | - apkName="Saber_FOSS_v${{ needs.get-version.outputs.buildName }}.apk" + buildName=$(grep -oP "(?<=buildName = ').*(?=')" lib/data/version.dart) + apkName="Saber_FOSS_v${buildName}.apk" mv ${{ steps.sign_app.outputs.signedFile }} output/$apkName echo "apkName=$apkName" >> $GITHUB_OUTPUT @@ -219,101 +201,3 @@ jobs: with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: output/${{ steps.rename_apk.outputs.apkName }} - - build-foss-alt-apk: - name: Build FOSS APK with alternate package name - runs-on: ubuntu-latest - needs: get-version - timeout-minutes: 20 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Remove unneeded files - run: | - rm -rf appimage - rm -rf assets_raw - rm -rf flatpak - rm -rf installers - rm -rf integration_test - rm -rf ios - rm -rf linux - rm -rf macos - rm -rf metadata - rm -rf packages/onyxsdk_pen - rm -rf snap - rm -rf submodules - rm -rf test - rm -rf windows - - - name: Setup cache - uses: actions/cache@v4 - with: - path: | - build - key: ${{ runner.OS }}-saberfoss-${{ hashFiles('**/pubspec.lock') }}-${{ hashFiles('**/*.dart') }} - restore-keys: | - ${{ runner.OS }}-saberfoss- - - - name: Remove proprietary dependencies - run: ./patches/remove_proprietary_dependencies.sh - - - name: Patch package name - run: ./patches/alt_package_name.sh - - - name: Setup Flutter - uses: subosito/flutter-action@v2 - with: - channel: stable - cache: true - cache-key: 'flutter-:os:-:channel:-:version:-:arch:' - - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: 'microsoft' - java-version: '17' - - - run: flutter pub get - - - name: Build apk - run: | - flutter build apk \ - --dart-define=DIRTY=$DIRTY - env: - DIRTY: ${{ !startsWith(github.ref, 'refs/tags/') }} - - - name: Move unsigned apk - run: | - mkdir -p output - mv build/app/outputs/flutter-apk/app-release.apk output/ - - - name: Sign apk - uses: ilharp/sign-android-release@v1 - id: sign_app - with: - releaseDir: output - signingKey: ${{ secrets.SIGNING_KEY }} - keyAlias: ${{ secrets.ALIAS }} - keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} - keyPassword: ${{ secrets.KEY_PASSWORD }} - - - name: Rename signed apk - id: rename_apk - run: | - apkName="Saber_FOSS_alt_v${{ needs.get-version.outputs.buildName }}.apk" - mv ${{ steps.sign_app.outputs.signedFile }} output/$apkName - echo "apkName=$apkName" >> $GITHUB_OUTPUT - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: Saber-Android-FOSS-Alt - path: output/${{ steps.rename_apk.outputs.apkName }} - - - name: Upload to GitHub release - uses: svenstaro/upload-release-action@v2 - if: false - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: output/${{ steps.rename_apk.outputs.apkName }} diff --git a/.github/workflows/check-flutter-submodule.yml b/.github/workflows/check-flutter-submodule.yml index 5e7d25d418..2f0f045223 100644 --- a/.github/workflows/check-flutter-submodule.yml +++ b/.github/workflows/check-flutter-submodule.yml @@ -4,6 +4,7 @@ on: push: paths: - .github/workflows/check-flutter-submodule.yml + - submodules/flutter schedule: # every 2 days at 5:58 am (random time to avoid spikes in GitHub Actions usage) - cron: '58 5 */2 * *' diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 0f3e4f2bb6..58583c3705 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -9,35 +9,16 @@ on: push: tags: - '**' + branches: + - '**' + paths: + - .github/workflows/ios.yml jobs: - get-version: - name: Get version - runs-on: ubuntu-latest - outputs: - buildName: ${{ steps.get_version.outputs.buildName }} - buildNumber: ${{ steps.get_version.outputs.buildNumber }} - timeout-minutes: 1 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Get version - id: get_version - run: | - # get buildName from lib/data/version.dart - buildName=$(grep -oP "(?<=buildName = ').*(?=')" lib/data/version.dart) - echo "buildName=$buildName" >> $GITHUB_OUTPUT - - # get buildNumber from lib/data/version.dart - buildNumber=$(grep -oP '(?<=buildNumber = ).*(?=;)' lib/data/version.dart) - echo "buildNumber=$buildNumber" >> $GITHUB_OUTPUT - build-ios: name: Build for iOS runs-on: macos-latest - needs: get-version - timeout-minutes: 30 + timeout-minutes: 60 env: APP_STORE_CONNECT_ISSUER_ID: ${{secrets.APP_STORE_CONNECT_ISSUER_ID}} APP_STORE_CONNECT_KEY_IDENTIFIER: ${{secrets.APP_STORE_CONNECT_KEY_IDENTIFIER}} @@ -81,7 +62,9 @@ jobs: architecture: x64 - name: Install Codemagic CLI Tools - run: pip3 install codemagic-cli-tools + run: | + brew install pipx + pipx install codemagic-cli-tools - name: Setup keychain run: | @@ -101,7 +84,6 @@ jobs: - run: find . -name "Podfile" -execdir pod install \; - name: Build for iOS - id: build run: | flutter build ipa \ --dart-define=FLAVOR="App Store" \ @@ -110,7 +92,12 @@ jobs: --dart-define=DIRTY="false" \ --export-options-plist=$HOME/export_options.plist - export ipaPath="Saber_v${{ needs.get-version.outputs.buildName }}.ipa" + - name: Move IPA + id: move + run: | + brew install grep + buildName=$(ggrep -oP "(?<=buildName = ').*(?=')" lib/data/version.dart) + export ipaPath="Saber_v${buildName}.ipa" mv $(find $(pwd) -name "*.ipa") $ipaPath echo "ipaPath=$ipaPath" >> $GITHUB_OUTPUT @@ -122,8 +109,8 @@ jobs: uses: actions/upload-artifact@v4 with: name: Saber-iOS-Archive - path: ${{ steps.build.outputs.ipaPath }} + path: ${{ steps.move.outputs.ipaPath }} - name: Publish to App Store if: ${{ startsWith(github.ref, 'refs/tags/') }} - run: app-store-connect publish --path ${{ steps.build.outputs.ipaPath }} + run: app-store-connect publish --path ${{ steps.move.outputs.ipaPath }} diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 76ddcba2fd..bf63f20375 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -6,34 +6,15 @@ on: push: tags: - '**' + branches: + - '**' + paths: + - .github/workflows/linux.yml jobs: - get-version: - name: Get version - runs-on: ubuntu-latest - outputs: - buildName: ${{ steps.get_version.outputs.buildName }} - buildNumber: ${{ steps.get_version.outputs.buildNumber }} - timeout-minutes: 1 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Get version - id: get_version - run: | - # get buildName from lib/data/version.dart - buildName=$(grep -oP "(?<=buildName = ').*(?=')" lib/data/version.dart) - echo "buildName=$buildName" >> $GITHUB_OUTPUT - - # get buildNumber from lib/data/version.dart - buildNumber=$(grep -oP '(?<=buildNumber = ).*(?=;)' lib/data/version.dart) - echo "buildNumber=$buildNumber" >> $GITHUB_OUTPUT - build-flutter-app-x86_64: name: Build Flutter app (x86_64) runs-on: ubuntu-latest - needs: get-version permissions: contents: write timeout-minutes: 20 @@ -56,7 +37,7 @@ jobs: - name: Install apt dependencies uses: awalsh128/cache-apt-pkgs-action@latest with: - packages: libgtk-3-dev libx11-dev pkg-config cmake ninja-build libblkid-dev libsecret-1-dev libjsoncpp-dev ghostscript + packages: libgtk-3-dev libx11-dev pkg-config cmake ninja-build libblkid-dev libsecret-1-dev libjsoncpp-dev ghostscript libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libunwind-dev version: 1.0 execute_install_scripts: true @@ -88,7 +69,8 @@ jobs: - name: Compress Flutter build id: compress run: | - archiveName="Saber_v${{ needs.get-version.outputs.buildName }}_Linux_x86_64.tar.gz" + buildName=$(grep -oP "(?<=buildName = ').*(?=')" lib/data/version.dart) + archiveName="Saber_v${buildName}_Linux_x86_64.tar.gz" echo "archiveName=$archiveName" >> $GITHUB_OUTPUT mkdir -p AppDir @@ -121,11 +103,10 @@ jobs: build-flutter-app-arm64: name: Build Flutter app (arm64) runs-on: ubuntu-latest - needs: get-version permissions: contents: write packages: write - timeout-minutes: 60 + timeout-minutes: 120 steps: - name: Checkout code uses: actions/checkout@v4 @@ -141,7 +122,6 @@ jobs: - name: Build Linux (arm64) uses: uraimo/run-on-arch-action@v2 - timeout-minutes: 120 with: arch: aarch64 distro: ubuntu_latest @@ -167,7 +147,8 @@ jobs: sudo \ bash curl file git unzip xz-utils zip \ clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev \ - libsecret-1-dev libjsoncpp-dev + libsecret-1-dev libjsoncpp-dev \ + libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libunwind-dev echo Creating nonroot user useradd -m -d /home/nonroot -s /bin/bash nonroot @@ -245,7 +226,8 @@ jobs: - name: Compress Flutter build id: compress run: | - archiveName="Saber_v${{ needs.get-version.outputs.buildName }}_Linux_arm64.tar.gz" + buildName=$(grep -oP "(?<=buildName = ').*(?=')" lib/data/version.dart) + archiveName="Saber_v${buildName}_Linux_arm64.tar.gz" echo "archiveName=$archiveName" >> $GITHUB_OUTPUT sudo chown -R $USER:$USER . @@ -275,7 +257,7 @@ jobs: build-appimage: name: Build AppImage - needs: [build-flutter-app-x86_64, get-version] + needs: build-flutter-app-x86_64 runs-on: ubuntu-latest permissions: contents: write @@ -326,7 +308,8 @@ jobs: working-directory: appimage run: | # replace line with "version: latest" with "version: $buildName" - sed -i "s/version: latest/version: ${{ needs.get-version.outputs.buildName }}/" AppImageBuilder.yml + buildName=$(grep -oP "(?<=buildName = ').*(?=')" ../lib/data/version.dart) + sed -i "s/version: latest/version: ${buildName}/" AppImageBuilder.yml - name: Build AppImage working-directory: appimage diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 06a6535231..c4e1d774c0 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -9,40 +9,21 @@ on: push: tags: - '**' + branches: + - '**' + paths: + - .github/workflows/macos.yml jobs: - get-version: - name: Get version - runs-on: ubuntu-latest - outputs: - buildName: ${{ steps.get_version.outputs.buildName }} - buildNumber: ${{ steps.get_version.outputs.buildNumber }} - timeout-minutes: 1 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Get version - id: get_version - run: | - # get buildName from lib/data/version.dart - buildName=$(grep -oP "(?<=buildName = ').*(?=')" lib/data/version.dart) - echo "buildName=$buildName" >> $GITHUB_OUTPUT - - # get buildNumber from lib/data/version.dart - buildNumber=$(grep -oP '(?<=buildNumber = ).*(?=;)' lib/data/version.dart) - echo "buildNumber=$buildNumber" >> $GITHUB_OUTPUT - build-macos: name: Build for macOS runs-on: macos-latest - needs: get-version env: APP_STORE_CONNECT_ISSUER_ID: ${{secrets.APP_STORE_CONNECT_ISSUER_ID}} APP_STORE_CONNECT_KEY_IDENTIFIER: ${{secrets.APP_STORE_CONNECT_KEY_IDENTIFIER}} APP_STORE_CONNECT_PRIVATE_KEY: ${{secrets.APP_STORE_CONNECT_PRIVATE_KEY}} APP_STORE_CERTIFICATE_KEY: ${{secrets.APP_STORE_CERTIFICATE_KEY}} - timeout-minutes: 30 + timeout-minutes: 60 steps: - name: Checkout code uses: actions/checkout@v4 @@ -81,7 +62,7 @@ jobs: architecture: x64 - name: Install Codemagic CLI Tools - run: pip3 install codemagic-cli-tools + run: pipx install codemagic-cli-tools - name: Setup keychain run: | @@ -93,7 +74,7 @@ jobs: --certificate-key=@env:APP_STORE_CERTIFICATE_KEY \ --create - app-store-connect list-certificates \ + app-store-connect certificates list \ --type MAC_INSTALLER_DISTRIBUTION \ --certificate-key=@env:APP_STORE_CERTIFICATE_KEY \ --save @@ -143,7 +124,9 @@ jobs: - name: Compress macOS build id: compress run: | - zipName="Saber_v${{ needs.get-version.outputs.buildName }}.app.zip" + brew install grep + buildName=$(ggrep -oP "(?<=buildName = ').*(?=')" lib/data/version.dart) + zipName="Saber_v${buildName}.app.zip" echo "zipName=$zipName" >> $GITHUB_OUTPUT pushd build/macos/Build/Products/Release diff --git a/.github/workflows/onyxsdk_pen_tests.yml b/.github/workflows/onyxsdk_pen_tests.yml index 61985b785d..2b5c813613 100644 --- a/.github/workflows/onyxsdk_pen_tests.yml +++ b/.github/workflows/onyxsdk_pen_tests.yml @@ -5,9 +5,11 @@ on: pull_request: paths: - 'packages/onyxsdk_pen/**' + - .github/workflows/onyxsdk_pen_tests.yml push: paths: - 'packages/onyxsdk_pen/**' + - .github/workflows/onyxsdk_pen_tests.yml jobs: run-onyx-tests: @@ -21,7 +23,7 @@ jobs: - name: Install apt dependencies uses: awalsh128/cache-apt-pkgs-action@latest with: - packages: libgtk-3-dev libx11-dev pkg-config cmake ninja-build libblkid-dev libsecret-1-dev libjsoncpp-dev ghostscript + packages: libgtk-3-dev libx11-dev pkg-config cmake ninja-build libblkid-dev libsecret-1-dev libjsoncpp-dev ghostscript libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libunwind-dev version: 1.0 execute_install_scripts: true diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ffcfdf7d76..2b7c93e444 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,7 +11,7 @@ jobs: run-tests: name: Run Flutter tests runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 10 steps: - name: Checkout code uses: actions/checkout@v4 @@ -19,7 +19,7 @@ jobs: - name: Install apt dependencies uses: awalsh128/cache-apt-pkgs-action@latest with: - packages: libgtk-3-dev libx11-dev pkg-config cmake ninja-build libblkid-dev libsecret-1-dev libjsoncpp-dev ghostscript + packages: libgtk-3-dev libx11-dev pkg-config cmake ninja-build libblkid-dev libsecret-1-dev libjsoncpp-dev ghostscript libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libunwind-dev version: 1.0 execute_install_scripts: true diff --git a/.github/workflows/update-landing-page.yml b/.github/workflows/update-landing-page.yml index 2ba38d3c7b..1a3deefa36 100644 --- a/.github/workflows/update-landing-page.yml +++ b/.github/workflows/update-landing-page.yml @@ -6,11 +6,15 @@ on: push: tags: - '**' + branches: + - '**' + paths: + - .github/workflows/update-landing-page.yml jobs: dispatch: runs-on: ubuntu-latest - timeout-minutes: 1 + timeout-minutes: 5 steps: - name: Repository Dispatch uses: peter-evans/repository-dispatch@v3 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index fcff1a3fbf..6b45e0716f 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -6,34 +6,15 @@ on: push: tags: - '**' + branches: + - '**' + paths: + - .github/workflows/windows.yml jobs: - get-version: - name: Get version - runs-on: ubuntu-latest - outputs: - buildName: ${{ steps.get_version.outputs.buildName }} - buildNumber: ${{ steps.get_version.outputs.buildNumber }} - timeout-minutes: 1 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Get version - id: get_version - run: | - # get buildName from lib/data/version.dart - buildName=$(grep -oP "(?<=buildName = ').*(?=')" lib/data/version.dart) - echo "buildName=$buildName" >> $GITHUB_OUTPUT - - # get buildNumber from lib/data/version.dart - buildNumber=$(grep -oP '(?<=buildNumber = ).*(?=;)' lib/data/version.dart) - echo "buildNumber=$buildNumber" >> $GITHUB_OUTPUT - build-windows: name: Build for Windows runs-on: windows-latest - needs: get-version permissions: contents: write timeout-minutes: 20 @@ -95,7 +76,9 @@ jobs: id: rename shell: bash run: | - installerName="SaberInstaller_v${{ needs.get-version.outputs.buildName }}.exe" + export LC_ALL=en_US.utf8 + buildName=$(grep -oP "(?<=buildName = ').*(?=')" lib/data/version.dart) + installerName="SaberInstaller_v${buildName}.exe" echo "installerName=$installerName" >> $GITHUB_OUTPUT mv installers/SaberInstaller.exe installers/$installerName diff --git a/README-cs.md b/README-cs.md index d59fe23b74..8e50ec294c 100644 --- a/README-cs.md +++ b/README-cs.md @@ -142,7 +142,7 @@ se dá shrnout jako: #### Linux -`sudo apt install libsecret-1-dev libjsoncpp-dev` +`sudo apt install libsecret-1-dev libjsoncpp-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev` `flutter build linux` Toto je dostatečné pro použití na vlastním počítači, ale pokud chcete své sestavení distribuovat, @@ -336,8 +336,8 @@ Pokud máte rádi Saber, zvažte prosím jeho podporu pomocí [google_play]: https://play.google.com/store/apps/details?id=com.adilhanney.saber [snap]: https://snapcraft.io/saber [app_store]: https://apps.apple.com/cz/app/saber/id1671523739 -[download_windows]: https://github.com/saber-notes/saber/releases/download/v0.21.3/SaberInstaller_v0.21.3.exe -[download_appimage]: https://github.com/saber-notes/saber/releases/download/v0.21.3/Saber-0.21.3-x86_64.AppImage +[download_windows]: https://github.com/saber-notes/saber/releases/download/v0.23.3/SaberInstaller_v0.23.3.exe +[download_appimage]: https://github.com/saber-notes/saber/releases/download/v0.23.3/Saber-0.23.3-x86_64.AppImage [nextcloud]: https://nc.saber.adil.hanney.org/ diff --git a/README-de.md b/README-de.md index b2232d9add..3756faa3d5 100644 --- a/README-de.md +++ b/README-de.md @@ -138,7 +138,7 @@ Das Setup für das [super_clipboard](https://pub.dev/packages/super_clipboard)-P #### Linux -`sudo apt install libsecret-1-dev libjsoncpp-dev` +`sudo apt install libsecret-1-dev libjsoncpp-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev` `flutter build linux` Dies ist ausreichend um es auf dem eigenen Computer zu benutzen, aber falls du deinen Build anderweitig zur Verfügung stellen möchtest, solltest du ein _Predictable Environment_ verwenden: Forke dieses Repo und nutze stattdessen die Github-Aktion [Build for Linux](https://github.com/saber-notes/saber/actions/workflows/linux.yml). @@ -322,8 +322,8 @@ Falls dir Saber gefällt, bitte denke darüber nach das Projekt wie folgt zu unt [google_play]: https://play.google.com/store/apps/details?id=com.adilhanney.saber [snap]: https://snapcraft.io/saber [app_store]: https://apps.apple.com/us/app/saber/id1671523739 -[download_windows]: https://github.com/saber-notes/saber/releases/download/v0.21.3/SaberInstaller_v0.21.3.exe -[download_appimage]: https://github.com/saber-notes/saber/releases/download/v0.21.3/Saber-0.21.3-x86_64.AppImage +[download_windows]: https://github.com/saber-notes/saber/releases/download/v0.23.3/SaberInstaller_v0.23.3.exe +[download_appimage]: https://github.com/saber-notes/saber/releases/download/v0.23.3/Saber-0.23.3-x86_64.AppImage [nextcloud]: https://nc.saber.adil.hanney.org/ diff --git a/README-zh-CN.md b/README-zh-CN.md index 8cfa42a58b..0d35f6c295 100644 --- a/README-zh-CN.md +++ b/README-zh-CN.md @@ -140,7 +140,7 @@ flutter pub get #### Linux -`sudo apt install libsecret-1-dev libjsoncpp-dev` +`sudo apt install libsecret-1-dev libjsoncpp-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev` `flutter build linux` 这对于在您自己的计算机上使用已经足够好了。但是如果您想重新分发您的构建,你需要使用一个可预测的环境:fork 这个仓库并使用 GitHub Action @@ -327,8 +327,8 @@ Windows 安装器使用 [Inno Setup](https://jrsoftware.org/isinfo.php) 创建 [google_play]: https://play.google.com/store/apps/details?id=com.adilhanney.saber [snap]: https://snapcraft.io/saber [app_store]: https://apps.apple.com/us/app/saber/id1671523739 -[download_windows]: https://github.com/saber-notes/saber/releases/download/v0.21.3/SaberInstaller_v0.21.3.exe -[download_appimage]: https://github.com/saber-notes/saber/releases/download/v0.21.3/Saber-0.21.3-x86_64.AppImage +[download_windows]: https://github.com/saber-notes/saber/releases/download/v0.23.3/SaberInstaller_v0.23.3.exe +[download_appimage]: https://github.com/saber-notes/saber/releases/download/v0.23.3/Saber-0.23.3-x86_64.AppImage [nextcloud]: https://nc.saber.adil.hanney.org/ diff --git a/README-zh-TW.md b/README-zh-TW.md index 40f5a76ea3..100336b165 100644 --- a/README-zh-TW.md +++ b/README-zh-TW.md @@ -145,7 +145,7 @@ flutter pub get #### Linux -`sudo apt install libsecret-1-dev libjsoncpp-dev` +`sudo apt install libsecret-1-dev libjsoncpp-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev` `flutter build linux` 這足以在您自己的電腦上使用,但如果您想重新分發您的構建, @@ -320,8 +320,8 @@ Windows 安裝程式是使用 [Inno Setup](https://jrsoftware.org/isinfo.php) [google_play]: https://play.google.com/store/apps/details?id=com.adilhanney.saber [snap]: https://snapcraft.io/saber [app_store]: https://apps.apple.com/us/app/saber/id1671523739 -[download_windows]: https://github.com/saber-notes/saber/releases/download/v0.21.3/SaberInstaller_v0.21.3.exe -[download_appimage]: https://github.com/saber-notes/saber/releases/download/v0.21.3/Saber-0.21.3-x86_64.AppImage +[download_windows]: https://github.com/saber-notes/saber/releases/download/v0.23.3/SaberInstaller_v0.23.3.exe +[download_appimage]: https://github.com/saber-notes/saber/releases/download/v0.23.3/Saber-0.23.3-x86_64.AppImage [nextcloud]: https://nc.saber.adil.hanney.org/ [privacy]: https://github.com/saber-notes/saber/blob/main/privacy_policy.md [license]: https://github.com/saber-notes/saber/blob/main/LICENSE.md diff --git a/README.md b/README.md index 454298c902..633a867059 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ package can be summarised as: #### Linux -`sudo apt install libsecret-1-dev libjsoncpp-dev` +`sudo apt install libsecret-1-dev libjsoncpp-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev` `flutter build linux` This is good enough for using on your own computer, but if you want to redistribute your build, @@ -342,8 +342,8 @@ If you like Saber, please consider supporting it by: [google_play]: https://play.google.com/store/apps/details?id=com.adilhanney.saber [snap]: https://snapcraft.io/saber [app_store]: https://apps.apple.com/us/app/saber/id1671523739 -[download_windows]: https://github.com/saber-notes/saber/releases/download/v0.21.3/SaberInstaller_v0.21.3.exe -[download_appimage]: https://github.com/saber-notes/saber/releases/download/v0.21.3/Saber-0.21.3-x86_64.AppImage +[download_windows]: https://github.com/saber-notes/saber/releases/download/v0.23.3/SaberInstaller_v0.23.3.exe +[download_appimage]: https://github.com/saber-notes/saber/releases/download/v0.23.3/Saber-0.23.3-x86_64.AppImage [nextcloud]: https://nc.saber.adil.hanney.org/ diff --git a/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java b/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java deleted file mode 100644 index 752fc185d4..0000000000 --- a/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java +++ /dev/null @@ -1,25 +0,0 @@ -// Generated file. -// -// If you wish to remove Flutter's multidex support, delete this entire file. -// -// Modifications to this file should be done in a copy under a different name -// as this file may be regenerated. - -package io.flutter.app; - -import android.app.Application; -import android.content.Context; -import androidx.annotation.CallSuper; -import androidx.multidex.MultiDex; - -/** - * Extension of {@link android.app.Application}, adding multidex support. - */ -public class FlutterMultiDexApplication extends Application { - @Override - @CallSuper - protected void attachBaseContext(Context base) { - super.attachBaseContext(base); - MultiDex.install(this); - } -} diff --git a/assets/audio/white-noise-8117.md b/assets/audio/white-noise-8117.md new file mode 100644 index 0000000000..e47218e926 --- /dev/null +++ b/assets/audio/white-noise-8117.md @@ -0,0 +1 @@ +Sound Effect by Daniel Roberts from Pixabay \ No newline at end of file diff --git a/assets/audio/white-noise-8117.ogg b/assets/audio/white-noise-8117.ogg new file mode 100644 index 0000000000000000000000000000000000000000..9310204a029d4cf234fa616c9203d57edff73281 GIT binary patch literal 10121 zcmai4bzBtP*B_9QmQYYckPeaVl1}Mn>4pViSyDiyyE`QoSV9^BMJ1(0gr$*OUgx*tSZ)7veT$`aedmA5^_^HC#U2m2B2l>dZyC7p56U&JvGX%09xX3N1~+FL z!@t-Jsto+R{Jgv(yn+nuy7qQ(TW5Di21R!tXD<(TsJ**4$MqAiaR0%0ASZ7KJ_HM( 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 z \ No newline at end of file diff --git a/assets/images/undraw_mobile_encryption_re_yw3o.svg b/assets/images/undraw_mobile_encryption_re_yw3o.svg new file mode 100644 index 0000000000..09bcb02eab --- /dev/null +++ b/assets/images/undraw_mobile_encryption_re_yw3o.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/undraw_my_files_swob.svg b/assets/images/undraw_my_files_swob.svg new file mode 100644 index 0000000000..203516ef67 --- /dev/null +++ b/assets/images/undraw_my_files_swob.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000000..fa0b357c4f --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/flatpak/com.adilhanney.saber.metainfo.xml b/flatpak/com.adilhanney.saber.metainfo.xml index fcaed2d4de..44acc906d7 100644 --- a/flatpak/com.adilhanney.saber.metainfo.xml +++ b/flatpak/com.adilhanney.saber.metainfo.xml @@ -6,7 +6,7 @@ com.adilhanney.saber - Saber: Handwritten Notes + Saber The notes app built for handwriting https://github.com/saber-notes/saber @@ -14,6 +14,11 @@ Adil Hanney + + #fffeac + #494900 + + MIT GPL-3.0-only @@ -198,6 +203,58 @@ + + +
    +
  • Fixed an "almost logged in" message being shown after logging in
  • +
  • Improved some icons
  • +
  • Disabled scrolling with the arrow keys when text editing
  • +
+
+
+ + +
    +
  • Increased PDF render quality
  • +
  • Typed text now properly aligns with the page lines if enabled
  • +
  • Updated the color picker dialogs
  • +
  • Misc improvements from the new Flutter 3.22
  • +
+
+
+ + +
    +
  • If you like to edit notes on a tablet and simultaneously view them on your computer, there's a new toggle in the editor menu to enable automatic refreshing.
  • +
  • Fixed straightened pencil lines fading out at the ends
  • +
  • Small improvement in app loading times
  • +
+
+
+ + +
    +
  • Redesigned the login process
  • +
  • Stopped the autosave triggering while in the middle of a stroke
  • +
  • Fixed the pencil sound effect setting not being picked up immediately
  • +
+
+
+ + +
    +
  • A simulated pencil sound effect has been added.
  • +
  • The Nextcloud syncing issue has been fixed.
  • +
+
+
+ + +
    +
  • The zoom is now snapped to 1.0x when close
  • +
+
+