Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[370] Add plain text notes #356

Merged
merged 15 commits into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class _AppState extends ConsumerState<App> with AfterLayoutMixin<App> {
),
child: MaterialApp(
title: 'Material Notes',
home: NotesPage(),
home: NotesPage(label: null),
navigatorKey: rootNavigatorKey,
builder: (context, child) {
// Change the widget shown when a widget building fails
Expand Down
12 changes: 10 additions & 2 deletions lib/common/actions/notes/add.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@ import 'select.dart';
/// Adds a note.
///
/// A [content] can be specified when the note is created from a sharing intent.
Future<void> addNote(BuildContext context, WidgetRef ref, {String? content}) async {
Future<void> addNote<NoteType>(BuildContext context, WidgetRef ref, {String? content}) async {
if (isNotesSelectionModeNotifier.value) {
exitNotesSelectionMode(context, ref);
}

final note = content == null ? RichTextNote.empty() : RichTextNote.content(content);
final Note note;
switch (NoteType) {
case == PlainTextNote:
note = content == null ? PlainTextNote.empty() : PlainTextNote.content(content);
case == RichTextNote:
note = content == null ? RichTextNote.empty() : RichTextNote.content(content);
default:
throw Exception('Unknown note type when creating a new note: $NoteType');
}

// If some content was provided, immediately save the note without waiting for changes in the editor
if (content != null) {
Expand Down
9 changes: 7 additions & 2 deletions lib/common/constants/constants.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import 'package:flutter/material.dart';
import '../logs/app_logger.dart';
import '../../l10n/app_localizations/app_localizations.g.dart';
import 'package:parchment/codecs.dart';
import 'package:saf_stream/saf_stream.dart';
import 'package:saf_util/saf_util.dart';
import 'package:uuid/uuid.dart';

import '../../l10n/app_localizations/app_localizations.g.dart';
import '../logs/app_logger.dart';

/// An UUID generator.
final uuid = Uuid();

/// Contact email address.
const contactEmail = '[email protected]';
Expand Down
10 changes: 1 addition & 9 deletions lib/common/navigation/app_bars/basic_app_bar.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
import 'package:flutter/material.dart';

/// Basic app bar.
///
/// Contains:
/// - A back button if built with [BasicAppBar.back].
/// - The title of the current route.
class BasicAppBar extends StatelessWidget {
/// Default constructor.
/// A basic app bar with a title.
const BasicAppBar({
super.key,
required this.title,
this.back = false,
});

/// Title to display in the app bar.
final String title;

/// Whether to show the back button.
final bool back;

@override
Widget build(BuildContext context) {
return AppBar(
Expand Down
53 changes: 18 additions & 35 deletions lib/common/navigation/app_bars/editor_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:fleather/fleather.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../../../models/note/notes_types.dart';
import '../../../pages/editor/sheets/about_sheet.dart';
import '../../../providers/notifiers/notifiers.dart';
import '../../actions/notes/copy.dart';
Expand All @@ -16,16 +17,9 @@ import '../../preferences/preference_key.dart';
import '../enums/bin_menu_option.dart';
import '../enums/note_menu_option.dart';

/// Editor's app bar.
///
/// Contains:
/// - A back button.
/// - The title of the editor route.
/// - The undo/redo buttons if enabled by the user.
/// - The checklist button if enabled by the user.
/// - The menu with further actions.
/// Editor app bar.
class EditorAppBar extends ConsumerStatefulWidget {
/// Default constructor.
/// App bar of the editor page allowing to perform actions on the note.
const EditorAppBar({
super.key,
});
Expand All @@ -35,12 +29,10 @@ class EditorAppBar extends ConsumerStatefulWidget {
}

class _BackAppBarState extends ConsumerState<EditorAppBar> {
/// Switches the editor mode between editing and viewing.
void switchMode() {
isFleatherEditorEditMode.value = !isFleatherEditorEditMode.value;
isEditorInEditModeNotifier.value = !isEditorInEditModeNotifier.value;
}

/// Performs the action associated with the selected [menuOption] on the not deleted note.
Future<void> onNoteMenuOptionSelected(NoteMenuOption menuOption) async {
// Manually close the keyboard
FocusManager.instance.primaryFocus?.unfocus();
Expand Down Expand Up @@ -74,7 +66,6 @@ class _BackAppBarState extends ConsumerState<EditorAppBar> {
}
}

/// Performs the action associated with the selected [menuOption] on the deleted note.
Future<void> onBinMenuOptionSelected(BinMenuOption menuOption) async {
// Manually close the keyboard
FocusManager.instance.primaryFocus?.unfocus();
Expand Down Expand Up @@ -102,7 +93,6 @@ class _BackAppBarState extends ConsumerState<EditorAppBar> {
}
}

/// Undoes the latest change in the editor.
void undo() {
final editorController = fleatherControllerNotifier.value;

Expand All @@ -113,7 +103,6 @@ class _BackAppBarState extends ConsumerState<EditorAppBar> {
editorController.undo();
}

/// Redoes the latest change in the editor.
void redo() {
final editorController = fleatherControllerNotifier.value;

Expand All @@ -124,7 +113,6 @@ class _BackAppBarState extends ConsumerState<EditorAppBar> {
editorController.redo();
}

/// Toggles the presence of the checklist in the currently active line of the editor.
void toggleChecklist() {
final editorController = fleatherControllerNotifier.value;

Expand All @@ -144,50 +132,45 @@ class _BackAppBarState extends ConsumerState<EditorAppBar> {
final editorController = fleatherControllerNotifier.value;

final showEditorModeButton = PreferenceKey.editorModeButton.getPreferenceOrDefault();
final showUndoRedoButtons = PreferenceKey.showUndoRedoButtons.getPreferenceOrDefault();
final showChecklistButton = PreferenceKey.showChecklistButton.getPreferenceOrDefault();
final enableLabels = PreferenceKey.enableLabels.getPreferenceOrDefault();

return ValueListenableBuilder(
valueListenable: fleatherFieldHasFocusNotifier,
builder: (context, hasFocus, child) => ValueListenableBuilder(
valueListenable: isFleatherEditorEditMode,
valueListenable: editorHasFocusNotifier,
builder: (context, editorHasFocus, child) => ValueListenableBuilder(
valueListenable: isEditorInEditModeNotifier,
builder: (context, isEditMode, child) => AppBar(
leading: BackButton(),
actions: note == null
? null
: [
if (!note.deleted) ...[
if (showUndoRedoButtons)
if (note.type == NoteType.richText) ...[
ValueListenableBuilder(
valueListenable: fleatherControllerCanUndoNotifier,
builder: (context, canUndo, child) => IconButton(
icon: const Icon(Icons.undo),
tooltip: l.tooltip_undo,
onPressed:
hasFocus && canUndo && editorController != null && editorController.canUndo && isEditMode
? undo
: null,
onPressed: editorHasFocus &&
canUndo &&
editorController != null &&
editorController.canUndo &&
isEditMode
? undo
: null,
),
),
if (showUndoRedoButtons)
ValueListenableBuilder(
valueListenable: fleatherControllerCanRedoNotifier,
builder: (context, canRedo, child) => IconButton(
icon: const Icon(Icons.redo),
tooltip: l.tooltip_redo,
onPressed: hasFocus && canRedo && isEditMode ? redo : null,
onPressed: editorHasFocus && canRedo && isEditMode ? redo : null,
),
),
if (showChecklistButton)
IconButton(
icon: const Icon(Icons.checklist),
tooltip: l.tooltip_toggle_checkbox,
onPressed: hasFocus && isEditMode ? toggleChecklist : null,
),
],
if (showEditorModeButton)
ValueListenableBuilder(
valueListenable: isFleatherEditorEditMode,
valueListenable: isEditorInEditModeNotifier,
builder: (context, isEditMode, child) => IconButton(
icon: Icon(isEditMode ? Icons.visibility : Icons.edit),
tooltip: isEditMode
Expand Down
7 changes: 6 additions & 1 deletion lib/common/navigation/app_bars/notes_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../../../models/label/label.dart';
import '../../../models/note/note.dart';
import '../../../providers/bin/bin_provider.dart';
import '../../../providers/notes/notes_provider.dart';
Expand Down Expand Up @@ -29,16 +30,20 @@ class NotesAppBar extends ConsumerWidget {
/// Default constructor.
const NotesAppBar({
super.key,
this.label,
this.notesPage = true,
});

/// The label used to filter the notes.
final Label? label;

/// Whether the current page is the notes list.
final bool notesPage;

/// Returns the title of the app bar.
String get title {
if (notesPage) {
return currentLabelFilter?.name ?? l.navigation_notes;
return label?.name ?? l.navigation_notes;
} else {
return l.navigation_bin;
}
Expand Down
44 changes: 23 additions & 21 deletions lib/common/navigation/app_bars/notes_selection_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,25 +102,27 @@ class NotesSelectionAppBar extends ConsumerWidget {
}

@override
Widget build(BuildContext context, WidgetRef ref) => notesPage
? ref.watch(notesProvider(label: currentLabelFilter)).when(
data: (notes) => buildAppBar(
context,
ref,
notes.where((note) => note.selected).toList(),
notes.length,
),
error: (exception, stackTrace) => ErrorPlaceholder(exception: exception, stackTrace: stackTrace),
loading: () => const LoadingPlaceholder(),
)
: ref.watch(binProvider).when(
data: (notes) => buildAppBar(
context,
ref,
notes.where((note) => note.selected).toList(),
notes.length,
),
error: (exception, stackTrace) => ErrorPlaceholder(exception: exception, stackTrace: stackTrace),
loading: () => const LoadingPlaceholder(),
);
Widget build(BuildContext context, WidgetRef ref) {
return notesPage
? ref.watch(notesProvider(label: currentLabelFilter)).when(
data: (notes) => buildAppBar(
context,
ref,
notes.where((note) => note.selected).toList(),
notes.length,
),
error: (exception, stackTrace) => ErrorPlaceholder(exception: exception, stackTrace: stackTrace),
loading: () => const LoadingPlaceholder(),
)
: ref.watch(binProvider).when(
data: (notes) => buildAppBar(
context,
ref,
notes.where((note) => note.selected).toList(),
notes.length,
),
error: (exception, stackTrace) => ErrorPlaceholder(exception: exception, stackTrace: stackTrace),
loading: () => const LoadingPlaceholder(),
);
}
}
4 changes: 2 additions & 2 deletions lib/common/navigation/side_navigation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,12 @@ class _SideNavigationState extends ConsumerState<SideNavigation> {
? NavigatorUtils.push(
context,
'${NavigationRoute.label.name}-${label.name}',
NotesPage(),
NotesPage(label: currentLabelFilter),
)
: NavigatorUtils.go(
context,
'${NavigationRoute.label.name}-${label.name}',
NotesPage(),
NotesPage(label: currentLabelFilter),
);
} else if (index == labels.length + 1) {
NavigationRoute.manageLabels.pushOrGo(context, isHomeRoute(route), LabelsPage());
Expand Down
16 changes: 11 additions & 5 deletions lib/common/preferences/preference_key.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,26 @@ import 'preferences_utils.dart';
// ignore_for_file: public_member_api_docs

/// Keys of preferences.
enum PreferenceKey<T> {
enum PreferenceKey<T extends Object> {
// Appearance
locale<String>('en', backup: false),
theme<String>('system'),
dynamicTheming<bool>(true),
blackTheming<bool>(false),
appFont<String>('systemDefault'),
editorFont<String>('systemDefault'),

// Notes types
availableNotesTypes<List<String>>(['plainText', 'richText']),
defaultShortcutNoteType<String>('plainText'),

// Rich text notes
useParagraphsSpacing<bool>(true),

// Notes tiles
showTilesBackground<bool>(false),
showSeparators<bool>(false),
showNoteTypeIcon<bool>(true),
showTitlesOnly<bool>(false),
showTitlesOnlyDisableInSearchView<bool>(true),
maximumContentPreviewLines<int>(3),
Expand All @@ -26,13 +36,9 @@ enum PreferenceKey<T> {
binSwipeLeftAction<String>('restore'),

// Editor
showUndoRedoButtons<bool>(true),
showChecklistButton<bool>(true),
showToolbar<bool>(true),
editorModeButton<bool>(true),
openEditorReadingMode<bool>(false),
focusTitleOnNewNote<bool>(false),
useParagraphsSpacing<bool>(true),

// Labels
enableLabels<bool>(true),
Expand Down
Loading