Skip to content

Commit

Permalink
v1.12.0 (#363)
Browse files Browse the repository at this point in the history
* [351] Fix build with flutter_secure_storage (#353)

* [303] Add rich text notes class and migration (#354)

* [355] Fix labels pages (#359)

* [360] Fix horizontal lines crash (#362)

* [344] Maximum content preview lines (#361)

* Prepare v1.12.0 (#364)
  • Loading branch information
maelchiotti authored Jan 12, 2025
1 parent 85bd707 commit 3ed31df
Show file tree
Hide file tree
Showing 75 changed files with 2,624 additions and 1,002 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 1.12.0 - 2025-01-12

### Added

- Setting to choose the maximum number of content preview lines to show in the notes tiles
- Italian translation

### Fixed

- Main notes page not displaying all the notes after going back from a labels page
- Crash when using an horizontal line in the the first line of the editor

## 1.11.0 - 2025-01-05

### Added
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ All the supported languages are listed here alphabetically. You can see more det
- French
- German
- Hindi
- Italian
- Polish
- Portuguese
- Russian
Expand Down
4 changes: 4 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,8 @@ flutter {
dependencies {
implementation 'com.android.support:multidex:2.0.1'
androidTestUtil 'androidx.test:orchestrator:1.5.1'
// required by flutter_secure_storage
implementation 'com.google.errorprone:error_prone_annotations:2.36.0'
// required by flutter_secure_storage
implementation 'com.github.spotbugs:spotbugs-annotations:4.8.6'
}
7 changes: 7 additions & 0 deletions fastlane/metadata/android/en-US/changelogs/250.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ADDED
- Setting to choose the maximum number of content preview lines to show in the notes tiles
- Italian translation

FIXED
- Main notes page not displaying all the notes after going back from a labels page
- Crash when using an horizontal line in the the first line of the editor
7 changes: 7 additions & 0 deletions fastlane/metadata/android/fr-FR/changelogs/250.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
AJOUTÉ
- Paramètre permettant de choisir le nombre maximum de lignes de l'aperçu du contenu à afficher dans les tuiles de notes
- Traduction en italien

CORRIGÉ
- La page principale des notes n'affiche pas toutes les notes après un retour depuis une page d'étiquettes
- Crash lors de l'utilisation d'une ligne horizontale dans la première ligne de l'éditeur
2 changes: 1 addition & 1 deletion fastlane/metadata/android/zh-CN/full_description.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<p><b>Material Notes</b> 是基于文本的笔记应用程序,旨在简单。它包含 Material 设计。 它在本地存储笔记,并且没有任何互联网权限,因此您是唯一可以访问笔记的人。</p><p><b>做笔记</b></p><ul><li>撰写文本笔记(标题和内容)</li><li>利用高级格式选项,包括清单</li><li>编辑时撤销和恢复更改</li><li>使用主屏幕上的快建操作快速添加笔记</li></ul><p><b>整理</b></p><ul><li>搜索您的笔记</li><li>按日期或标题、升序或降序对笔记进行排序</li><li>以列表或网格视图显示笔记</li><li>置顶您的笔记</li><li>从回收站中还原已删除的笔记</li></ul><p><b>分享和备份</b></p><ul><li>分享其他应用程序中的文本,将其直接添加到笔记中</li><li>以文本形式分享笔记</li><li>手动或自动将笔记导出为 JSON,然后再重新导入</li><li>将笔记导出为 Markdown</li></ul><p><b>保护</b></p><ul><li>不必担心您的数据如何处理:它无法离开您的设备 因为应用程序没有任何互联网权限</li><li>加密您的 JSON 导出</li></ul><p><b>定制</b></p><ul><li>选择您的语言</li><li>选择您的主题(浅色、深色或黑色)</li><li>选择是否希望主题是动态的(使用背景颜色)</li><li>选择是要启用高级格式、仅启用清单还是保持基本笔记</li></ul>
<p><b>Material Notes</b> 是基于文本的笔记应用程序,旨在简单。它包含 Material 设计。 它在本地存储笔记,并且没有任何互联网权限,因此只有您可以访问笔记。</p><p><b>做笔记</b></p><ul><li>撰写文本笔记(标题和内容)</li><li>利用高级格式选项,包括清单</li><li>编辑时撤销和恢复更改</li><li>使用主屏幕上的快捷操作快速添加笔记</li></ul><p><b>整理</b></p><ul><li>搜索您的笔记</li><li>按日期或标题、升序或降序对笔记进行排序</li><li>以列表或网格视图显示笔记</li><li>置顶您的笔记</li><li>从回收站中还原已删除的笔记</li></ul><p><b>分享和备份</b></p><ul><li>分享其他应用程序中的文本,将其直接添加到笔记中</li><li>以文本形式分享笔记</li><li>手动或自动将笔记导出为 JSON,然后再重新导入</li><li>将笔记导出为 Markdown</li></ul><p><b>保护</b></p><ul><li>不必担心您的数据如何处理:它无法离开您的设备 因为应用程序没有任何互联网权限</li><li>加密您的 JSON 导出</li></ul><p><b>定制</b></p><ul><li>选择您的语言</li><li>选择您的主题(浅色、深色或黑色)</li><li>选择是否希望主题是动态的(使用背景颜色)</li><li>选择是要启用高级格式、仅启用清单还是保持基本笔记</li></ul>
7 changes: 4 additions & 3 deletions lib/common/actions/notes/add.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'select.dart';

import '../../../models/note/note.dart';
import '../../../navigation/navigator_utils.dart';
import '../../../providers/notes/notes_provider.dart';
import '../../../providers/notifiers/notifiers.dart';
import 'select.dart';

/// Adds a note.
///
Expand All @@ -14,11 +15,11 @@ Future<void> addNote(BuildContext context, WidgetRef ref, {String? content}) asy
exitNotesSelectionMode(context, ref);
}

final note = content == null ? Note.empty() : Note.content(content);
final note = content == null ? RichTextNote.empty() : RichTextNote.content(content);

// If some content was provided, immediately save the note without waiting for changes in the editor
if (content != null) {
ref.read(notesProvider.notifier).edit(note);
ref.read(notesProvider(label: currentLabelFilter).notifier).edit(note);
}

currentNoteNotifier.value = note;
Expand Down
5 changes: 3 additions & 2 deletions lib/common/actions/notes/copy.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import 'package:flutter/services.dart';
import '../../constants/constants.dart';

import '../../../models/note/note.dart';
import '../../../utils/snack_bar_utils.dart';
import '../../constants/constants.dart';

/// Copies the content of the [note] to the clipboard.
Future<void> copyNote(Note note) async {
Future<void> copyNote({required Note note}) async {
Clipboard.setData(
ClipboardData(text: note.contentPreview),
);
Expand Down
12 changes: 6 additions & 6 deletions lib/common/actions/notes/delete.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import 'select.dart';
///
/// First, asks for a confirmation if needed.
/// Finally, pops the route if the note was deleted from the editor page.
Future<bool> deleteNote(BuildContext context, WidgetRef ref, Note? note, [bool pop = false]) async {
Future<bool> deleteNote(BuildContext context, WidgetRef ref, {Note? note, bool pop = false}) async {
if (note == null) {
return false;
}
Expand All @@ -35,7 +35,7 @@ Future<bool> deleteNote(BuildContext context, WidgetRef ref, Note? note, [bool p

currentNoteNotifier.value = null;

final succeeded = await ref.read(notesProvider.notifier).delete(note);
final succeeded = await ref.read(notesProvider(label: currentLabelFilter).notifier).delete(note);

if (!succeeded) {
return false;
Expand All @@ -49,7 +49,7 @@ Future<bool> deleteNote(BuildContext context, WidgetRef ref, Note? note, [bool p
/// Returns `true` if the [notes] were deleted, `false` otherwise.
///
/// First, asks for a confirmation if needed.
Future<bool> deleteNotes(BuildContext context, WidgetRef ref, List<Note> notes) async {
Future<bool> deleteNotes(BuildContext context, WidgetRef ref, {required List<Note> notes}) async {
if (!await askForConfirmation(
context,
l.dialog_delete,
Expand All @@ -59,7 +59,7 @@ Future<bool> deleteNotes(BuildContext context, WidgetRef ref, List<Note> notes)
return false;
}

final succeeded = await ref.read(notesProvider.notifier).deleteAll(notes);
final succeeded = await ref.read(notesProvider(label: currentLabelFilter).notifier).deleteAll(notes);

if (context.mounted) {
exitNotesSelectionMode(context, ref);
Expand All @@ -74,7 +74,7 @@ Future<bool> deleteNotes(BuildContext context, WidgetRef ref, List<Note> notes)
///
/// First, asks for a confirmation if needed.
/// Finally, pops the route if the note was deleted from the editor page.
Future<bool> permanentlyDeleteNote(BuildContext context, WidgetRef ref, Note? note, [bool pop = false]) async {
Future<bool> permanentlyDeleteNote(BuildContext context, WidgetRef ref, {Note? note, bool pop = false}) async {
if (note == null) {
return false;
}
Expand Down Expand Up @@ -109,7 +109,7 @@ Future<bool> permanentlyDeleteNote(BuildContext context, WidgetRef ref, Note? no
/// Returns `true` if the [notes] were permanently deleted, `false` otherwise.
///
/// First, asks for a confirmation if needed.
Future<bool> permanentlyDeleteNotes(BuildContext context, WidgetRef ref, List<Note> notes) async {
Future<bool> permanentlyDeleteNotes(BuildContext context, WidgetRef ref, {required List<Note> notes}) async {
if (!await askForConfirmation(
context,
l.dialog_permanently_delete,
Expand Down
8 changes: 4 additions & 4 deletions lib/common/actions/notes/labels.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import '../../constants/constants.dart';
import 'select.dart';

/// Asks the user to select the labels for the [note].
Future<List<Label>?> selectLabels(BuildContext context, WidgetRef ref, Note note) async {
Future<List<Label>?> selectLabels(BuildContext context, WidgetRef ref, {required Note note}) async {
if (ref.read(labelsListProvider).value == null || ref.read(labelsListProvider).value!.isEmpty) {
SnackBarUtils.info(l.snack_bar_no_labels).show();

Expand All @@ -29,7 +29,7 @@ Future<List<Label>?> selectLabels(BuildContext context, WidgetRef ref, Note note
return null;
}

await ref.read(notesProvider.notifier).editLabels(note, selectedLabels);
await ref.read(notesProvider(label: currentLabelFilter).notifier).editLabels(note, selectedLabels);

// Forcefully notify the listeners because only the labels of the note have changed
currentNoteNotifier.value = note;
Expand All @@ -39,7 +39,7 @@ Future<List<Label>?> selectLabels(BuildContext context, WidgetRef ref, Note note
}

/// Asks the user to select the labels to add to the [notes].
Future<List<Label>?> addLabels(BuildContext context, WidgetRef ref, List<Note> notes) async {
Future<List<Label>?> addLabels(BuildContext context, WidgetRef ref, {required List<Note> notes}) async {
if (ref.read(labelsListProvider).value == null || ref.read(labelsListProvider).value!.isEmpty) {
SnackBarUtils.info(l.snack_bar_no_labels).show();

Expand All @@ -56,7 +56,7 @@ Future<List<Label>?> addLabels(BuildContext context, WidgetRef ref, List<Note> n
return null;
}

await ref.read(notesProvider.notifier).addLabels(notes, selectedLabels);
await ref.read(notesProvider(label: currentLabelFilter).notifier).addLabels(notes, selectedLabels);

if (context.mounted) {
exitNotesSelectionMode(context, ref);
Expand Down
12 changes: 7 additions & 5 deletions lib/common/actions/notes/pin.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'select.dart';

import '../../../models/note/note.dart';
import '../../../providers/notes/notes_provider.dart';
import '../../../providers/notifiers/notifiers.dart';
import 'select.dart';

/// Toggles the pined status of the [note].
///
/// Returns `true` if the pined status of the [note] was toggled, `false` otherwise.
Future<bool> togglePinNote(BuildContext context, WidgetRef ref, Note? note) async {
Future<bool> togglePinNote(BuildContext context, WidgetRef ref, {Note? note}) async {
if (note == null) {
return false;
}

await ref.read(notesProvider.notifier).togglePin(note);
await ref.read(notesProvider(label: currentLabelFilter).notifier).togglePin(note);

return false;
}

/// Toggles the pined status of the [notes].
Future<void> togglePinNotes(BuildContext context, WidgetRef ref, List<Note> notes) async {
await ref.read(notesProvider.notifier).togglePinAll(notes);
Future<void> togglePinNotes(BuildContext context, WidgetRef ref, {required List<Note> notes}) async {
await ref.read(notesProvider(label: currentLabelFilter).notifier).togglePinAll(notes);

if (context.mounted) {
exitNotesSelectionMode(context, ref);
Expand Down
4 changes: 2 additions & 2 deletions lib/common/actions/notes/restore.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import 'select.dart';
///
/// First, asks for a confirmation if needed.
/// Finally, pops the route if the note was restored from the editor page.
Future<bool> restoreNote(BuildContext context, WidgetRef ref, Note? note, [bool pop = false]) async {
Future<bool> restoreNote(BuildContext context, WidgetRef ref, {Note? note, bool pop = false}) async {
if (note == null) {
return false;
}
Expand Down Expand Up @@ -47,7 +47,7 @@ Future<bool> restoreNote(BuildContext context, WidgetRef ref, Note? note, [bool
/// Returns `true` if the [notes] were restored, `false` otherwise.
///
/// First, asks for a confirmation if needed.
Future<bool> restoreNotes(BuildContext context, WidgetRef ref, List<Note> notes) async {
Future<bool> restoreNotes(BuildContext context, WidgetRef ref, {required List<Note> notes}) async {
if (!await askForConfirmation(
context,
l.dialog_restore,
Expand Down
14 changes: 10 additions & 4 deletions lib/common/actions/notes/select.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,32 @@ import '../../../providers/notes/notes_provider.dart';
import '../../../providers/notifiers/notifiers.dart';

/// Toggles the select status of the [note].
void toggleSelectNote(WidgetRef ref, Note note) {
void toggleSelectNote(WidgetRef ref, {required Note note}) {
if (note.deleted) {
note.selected ? ref.read(binProvider.notifier).unselect(note) : ref.read(binProvider.notifier).select(note);
} else {
note.selected ? ref.read(notesProvider.notifier).unselect(note) : ref.read(notesProvider.notifier).select(note);
note.selected
? ref.read(notesProvider(label: currentLabelFilter).notifier).unselect(note)
: ref.read(notesProvider(label: currentLabelFilter).notifier).select(note);
}
}

/// Selects all the notes.
///
/// Depending on the current route, selects either the notes from the notes page or those from the bin page.
void selectAllNotes(BuildContext context, WidgetRef ref, {bool notesPage = true}) {
notesPage ? ref.read(notesProvider.notifier).selectAll() : ref.read(binProvider.notifier).selectAll();
notesPage
? ref.read(notesProvider(label: currentLabelFilter).notifier).selectAll()
: ref.read(binProvider.notifier).selectAll();
}

/// Unselects all the notes.
///
/// Depending on the current route, unselects either the notes from the notes page or those from the bin page.
void unselectAllNotes(BuildContext context, WidgetRef ref, {bool notesPage = true}) {
notesPage ? ref.read(notesProvider.notifier).unselectAll() : ref.read(binProvider.notifier).unselectAll();
notesPage
? ref.read(notesProvider(label: currentLabelFilter).notifier).unselectAll()
: ref.read(binProvider.notifier).unselectAll();
}

/// Exits the notes selection mode.
Expand Down
4 changes: 2 additions & 2 deletions lib/common/actions/notes/share.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import '../../../models/note/note.dart';
import '../../constants/constants.dart';

/// Shares the [note] as text (title and content).
Future<void> shareNote(Note note) async {
Future<void> shareNote({required Note note}) async {
await Share.share(note.shareText, subject: note.title);
}

/// Shares the [notes] as text (title and content), separated by dashes.
Future<void> shareNotes(List<Note> notes) async {
Future<void> shareNotes({required List<Note> notes}) async {
final text = notes.map((note) => note.shareText).join('\n\n----------\n\n');

await Share.share(text, subject: l.action_share_subject(notes.length));
Expand Down
22 changes: 11 additions & 11 deletions lib/common/constants/notes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import '../../models/note/note.dart';
import '../../utils/localizations_utils.dart';

/// Note displayed on the very first run of the application to welcome the user.
final welcomeNote = Note(
final welcomeNote = RichTextNote(
deleted: false,
pinned: true,
createdTime: DateTime.now(),
Expand All @@ -14,7 +14,7 @@ final welcomeNote = Note(
/// Notes used when running integration tests.
final integrationTestNotes = List.generate(
100,
(index) => Note(
(index) => RichTextNote(
deleted: false,
pinned: false,
createdTime: DateTime(2000, 01, 01, 12).subtract(Duration(minutes: index)),
Expand All @@ -25,7 +25,7 @@ final integrationTestNotes = List.generate(
)..addAll(
List.generate(
100,
(index) => Note(
(index) => RichTextNote(
deleted: true,
pinned: false,
createdTime: DateTime(2000, 01, 01, 12).subtract(Duration(minutes: index)),
Expand All @@ -38,7 +38,7 @@ final integrationTestNotes = List.generate(

/// Notes used when taking screenshots of the application for the stores.
final screenshotNotes = [
Note(
RichTextNote(
deleted: false,
pinned: true,
createdTime: DateTime(2000, 01, 01, 12),
Expand All @@ -47,7 +47,7 @@ final screenshotNotes = [
content:
'[{"insert":"Simple","attributes":{"b":true}},{"insert":", "},{"insert":"local","attributes":{"i":true}},{"insert":", "},{"insert":"material design","attributes":{"u":true}},{"insert":" notes\\n"}]',
),
Note(
RichTextNote(
deleted: false,
pinned: false,
createdTime: DateTime(2000, 01, 01, 11, 55),
Expand All @@ -56,23 +56,23 @@ final screenshotNotes = [
content:
'[{"insert":"Write text notes"},{"insert":"\\n","attributes":{"block":"cl","checked":true}},{"insert":"Use formatting options and undo/redo"},{"insert":"\\n","attributes":{"block":"cl","checked":true}},{"insert":"Use quick action to add from your homescreen"},{"insert":"\\n","attributes":{"block":"cl","checked":true}}]',
),
Note(
RichTextNote(
deleted: false,
pinned: false,
createdTime: DateTime(2000, 01, 01, 11, 50),
editedTime: DateTime(2000, 01, 01, 11, 50),
title: "Organize",
content: '[{"insert":"Search, sort and display in a list or a grid\\nPin and recover from the bin\\n"}]',
),
Note(
RichTextNote(
deleted: false,
pinned: false,
createdTime: DateTime(2000, 01, 01, 11, 50),
editedTime: DateTime(2000, 01, 01, 11, 50),
title: "Categorize",
content: '[{"insert":"Categorize notes with labels\\nPin, hide and colorize labels\\n"}]',
),
Note(
RichTextNote(
deleted: false,
pinned: false,
createdTime: DateTime(2000, 01, 01, 11, 45),
Expand All @@ -81,7 +81,7 @@ final screenshotNotes = [
content:
'[{"insert":"Create a note from shared text\\nShare notes as text and export as Markdown\\nBackup notes as JSON\\n"}]',
),
Note(
RichTextNote(
deleted: false,
pinned: false,
createdTime: DateTime(2000, 01, 01, 11, 40),
Expand All @@ -90,15 +90,15 @@ final screenshotNotes = [
content:
"[{\"insert\":\"Choose your language\\nChoose your theme (including black and dynamic)\\nHide features you don't need\\n\"}]",
),
Note(
RichTextNote(
deleted: false,
pinned: false,
createdTime: DateTime(2000, 01, 01, 11, 35),
editedTime: DateTime(2000, 01, 01, 11, 35),
title: "Protect",
content: '[{"insert":"Your data never leaves your device\\nEncrypt your exports\\n"}]',
),
Note(
RichTextNote(
deleted: true,
pinned: false,
createdTime: DateTime(2000, 01, 01, 12),
Expand Down
Loading

0 comments on commit 3ed31df

Please sign in to comment.