From 8ca5ed4142d799ec86a191c7d2bb0a015858d522 Mon Sep 17 00:00:00 2001 From: angelocordero Date: Mon, 5 Jun 2023 02:39:59 +0800 Subject: [PATCH] refactor song editor --- lib/media_center/media_center_providers.dart | 15 +- .../playlist_preview_panel_notifier.dart | 2 +- .../song_editor_lyrics_fields_notifier.dart | 166 ++++-------------- lib/media_center/tabs/songs_tab.dart | 62 ++++++- .../widgets/song_editor_text_field_tile.dart | 64 +++++++ lib/media_center/widgets/song_preview.dart | 2 +- 6 files changed, 162 insertions(+), 149 deletions(-) create mode 100644 lib/media_center/widgets/song_editor_text_field_tile.dart diff --git a/lib/media_center/media_center_providers.dart b/lib/media_center/media_center_providers.dart index 9d09338..ff49272 100644 --- a/lib/media_center/media_center_providers.dart +++ b/lib/media_center/media_center_providers.dart @@ -56,9 +56,9 @@ final selectedSongProvider = StateProvider.autoDispose((ref) { return Song.empty(); } - Song editedSong = ref.read(editedSongProvider); + Song? editedSong = ref.read(editedSongProvider); - if (editedSong != Song.empty()) { + if (editedSong != null) { return editedSong; } @@ -96,12 +96,7 @@ final playlistSelectedPreviewProvider = StateProvider.autoDispose((ref) return null; }); -final isEditingProvider = StateProvider.autoDispose((ref) { - ref.watch(selectedSongProvider); - - return false; -}); - -final editedSongProvider = StateProvider.autoDispose((ref) { - return Song.empty(); +/// Highlights songs after editing +final editedSongProvider = StateProvider.autoDispose((ref) { + return null; }); diff --git a/lib/media_center/notifiers/playlist_preview_panel_notifier.dart b/lib/media_center/notifiers/playlist_preview_panel_notifier.dart index 684d3d4..420a0e4 100644 --- a/lib/media_center/notifiers/playlist_preview_panel_notifier.dart +++ b/lib/media_center/notifiers/playlist_preview_panel_notifier.dart @@ -73,6 +73,6 @@ class PlaylistPreviewPanelNotifier extends StateNotifier { } void _previewSong(Song args) { - state = SongPreview(song: args); + state = SongPreview( args); } } diff --git a/lib/media_center/notifiers/song_editor_lyrics_fields_notifier.dart b/lib/media_center/notifiers/song_editor_lyrics_fields_notifier.dart index 4f7edac..6e9f380 100644 --- a/lib/media_center/notifiers/song_editor_lyrics_fields_notifier.dart +++ b/lib/media_center/notifiers/song_editor_lyrics_fields_notifier.dart @@ -2,53 +2,34 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:freecps/core/file_utils.dart'; -import '../../core/constants.dart'; import '../../core/helper_functions.dart'; import '../../models/song_model.dart'; import '../media_center_providers.dart'; import '../tabs/songs_tab.dart'; +import '../widgets/song_editor_text_field_tile.dart'; +/// Index of lyrics field and the position of the cursor in the field typedef CursorLocation = ({int textFieldIndex, int cursorPosition}); typedef FieldData = ({TextEditingController controller, String label}); -//TODO: MASSIVE MASSIVE REFACTOR - -class SongEditorLyricsFieldsNotifier extends StateNotifier { - SongEditorLyricsFieldsNotifier(this.song, this.ref) : super(Container()) { - init(); +class SongEditorLyricsFieldsNotifier extends StateNotifier> { + SongEditorLyricsFieldsNotifier(this.song, this.ref) : super([]) { + _init(); } - final List<_SongEditorTextFieldTile> _fields = []; - final List _fieldsData = []; - - Song song; - - AutoDisposeStateNotifierProviderRef ref; - CursorLocation? cursorLocation; + final AutoDisposeStateNotifierProviderRef> ref; + Song song; - TextEditingController titleController = TextEditingController(); - TextEditingController artistController = TextEditingController(); + final List _fieldsData = []; void setCursorLocation({required int textFieldIndex, required int cursorPos}) { cursorLocation = (textFieldIndex: textFieldIndex - 1, cursorPosition: cursorPos); } - init() { - for (var entry in song.lyrics.entries) { - for (var element in entry.value) { - TextEditingController controller = TextEditingController.fromValue(TextEditingValue(text: element)); - _fieldsData.add((controller: controller, label: entry.key)); - } - } - - titleController.text = song.title; - artistController.text = song.artist; - - _setState(); - } - - insertSlide() { + // adds a field to the list + insertField() { + // if there are no fields, adds field to begin the list and return if (_fieldsData.isEmpty) { TextEditingController insertController = TextEditingController(); @@ -60,6 +41,10 @@ class SongEditorLyricsFieldsNotifier extends StateNotifier { return; } + // if there are fields, insert a field to the cursor location + // splits the text based on the cursor + // puts text after the cursor into the newly inserted field + if (cursorLocation == null) return; int index = cursorLocation!.textFieldIndex; @@ -79,7 +64,10 @@ class SongEditorLyricsFieldsNotifier extends StateNotifier { _setState(); } - save() { + save({ + required TextEditingController titleController, + required TextEditingController artistController, + }) { if (titleController.text.isEmpty) return; if (_fieldsData.isEmpty) return; @@ -110,121 +98,37 @@ class SongEditorLyricsFieldsNotifier extends StateNotifier { bool isEditing = ref.read(isEditingProvider); ref.read(isEditingProvider.notifier).state = !isEditing; - ref.read(editedSongProvider.notifier).state = song; } - Widget _textFieldList() { - return Expanded( - child: ListView( - children: [ - Row( - children: [ - const Text('Title: '), - Flexible( - flex: 1, - child: Container(), - ), - Flexible( - flex: 2, - child: TextField( - controller: titleController, - ), - ), - ], - ), - Row( - children: [ - const Text('Artist: '), - const Spacer( - flex: 1, - ), - Flexible( - flex: 2, - child: TextField( - controller: artistController, - ), - ), - ], - ), - ..._fields, - ], - ), - ); + + /// initializes the fields + /// takes all labels, ie. Verses, Chorus, etc. and gets all lines of lyrics from each label + /// assigns each line to a corresponding field + _init() { + for (var entry in song.lyrics.entries) { + for (var element in entry.value) { + TextEditingController controller = TextEditingController.fromValue(TextEditingValue(text: element)); + _fieldsData.add((controller: controller, label: entry.key)); + } + } + + _setState(); } void _setState() { - _fields.clear(); + state.clear(); for (int i = 0; i < _fieldsData.length; i++) { FieldData fieldData = _fieldsData[i]; - _fields.add(_SongEditorTextFieldTile( + state.add(SongEditorTextFieldTile( label: fieldData.label, controller: fieldData.controller, index: i + 1, )); } - state = _textFieldList(); - } -} - -class _SongEditorTextFieldTile extends ConsumerWidget { - const _SongEditorTextFieldTile({required this.label, required this.controller, required this.index}); - - final String label; - final TextEditingController controller; - final int index; - - @override - Widget build(BuildContext context, WidgetRef ref) { - final Color color = catpuccinColorsSample[label] ?? Colors.blueGrey; - return Card( - margin: const EdgeInsets.symmetric(horizontal: 100, vertical: 10), - shape: RoundedRectangleBorder( - side: BorderSide( - color: color, - ), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Container( - padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 10), - color: color, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(index.toString()), - Text(label), - ], - ), - ), - Padding( - padding: const EdgeInsets.all(10.0), - child: TextField( - textAlign: TextAlign.center, - controller: controller, - minLines: 5, - maxLines: 10, - onTap: () { - ref.read(songEditorProvider.notifier).setCursorLocation( - textFieldIndex: index, - cursorPos: controller.selection.baseOffset, - ); - }, - onChanged: (input) { - ref.read(songEditorProvider.notifier).setCursorLocation( - textFieldIndex: index, - cursorPos: controller.selection.baseOffset, - ); - }, - ), - ), - ], - ), - ); + state = state.toList(); } } diff --git a/lib/media_center/tabs/songs_tab.dart b/lib/media_center/tabs/songs_tab.dart index 3c0e02c..34ce415 100644 --- a/lib/media_center/tabs/songs_tab.dart +++ b/lib/media_center/tabs/songs_tab.dart @@ -1,6 +1,8 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:freecps/media_center/widgets/song_preview.dart'; import '../../core/constants.dart'; @@ -11,13 +13,20 @@ import '../../models/song_slide_model.dart'; import '../../widgets/song_slide_widget.dart'; import '../media_center_providers.dart'; import '../notifiers/song_editor_lyrics_fields_notifier.dart'; +import '../widgets/song_editor_text_field_tile.dart'; -final songEditorProvider = StateNotifierProvider.autoDispose((ref) { +final songEditorLyricsFieldProvider = StateNotifierProvider.autoDispose>((ref) { Song song = ref.watch(selectedSongProvider); return SongEditorLyricsFieldsNotifier(song, ref); }); +final isEditingProvider = StateProvider.autoDispose((ref) { + ref.watch(selectedSongProvider); + + return false; +}); + class SongsTab extends ConsumerWidget { const SongsTab({ Key? key, @@ -79,7 +88,7 @@ class SongsTab extends ConsumerWidget { const VerticalDivider(), Flexible( flex: 2, - child: ref.watch(isEditingProvider) ? const _SongEditor() : SongPreview(song: selectedSong), + child: ref.watch(isEditingProvider) ? _SongEditor(selectedSong) : SongPreview(selectedSong), ), const VerticalDivider(), Flexible( @@ -189,7 +198,12 @@ class _SongSlidePreview extends StatelessWidget { } class _SongEditor extends ConsumerWidget { - const _SongEditor(); + _SongEditor(this.song); + + final Song song; + + late final TextEditingController titleController = TextEditingController(text: song.title); + late final TextEditingController artistController = TextEditingController(text: song.artist); @override Widget build(BuildContext context, WidgetRef ref) { @@ -200,19 +214,55 @@ class _SongEditor extends ConsumerWidget { children: [ ElevatedButton( onPressed: () { - ref.read(songEditorProvider.notifier).insertSlide(); + ref.read(songEditorLyricsFieldProvider.notifier).insertField(); }, child: const Text('Inser Slide in cursor position'), ), ElevatedButton( onPressed: () { - ref.read(songEditorProvider.notifier).save(); + ref.read(songEditorLyricsFieldProvider.notifier).save( + titleController: titleController, + artistController: artistController, + ); }, child: const Text('Save song'), ), ], ), - ref.watch(songEditorProvider), + Row( + children: [ + const Text('Title: '), + Flexible( + flex: 1, + child: Container(), + ), + Flexible( + flex: 2, + child: TextField( + controller: titleController, + ), + ), + ], + ), + Row( + children: [ + const Text('Artist: '), + const Spacer( + flex: 1, + ), + Flexible( + flex: 2, + child: TextField( + controller: artistController, + ), + ), + ], + ), + Expanded( + child: ListView( + children: [...ref.watch(songEditorLyricsFieldProvider)], + ), + ), ], ); } diff --git a/lib/media_center/widgets/song_editor_text_field_tile.dart b/lib/media_center/widgets/song_editor_text_field_tile.dart new file mode 100644 index 0000000..4c4d62d --- /dev/null +++ b/lib/media_center/widgets/song_editor_text_field_tile.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../core/constants.dart'; +import '../tabs/songs_tab.dart'; + +class SongEditorTextFieldTile extends ConsumerWidget { + const SongEditorTextFieldTile({super.key, required this.label, required this.controller, required this.index}); + + final TextEditingController controller; + final int index; + final String label; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final Color color = catpuccinColorsSample[label] ?? Colors.blueGrey; + return Card( + margin: const EdgeInsets.symmetric(horizontal: 100, vertical: 10), + shape: RoundedRectangleBorder( + side: BorderSide( + color: color, + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 10), + color: color, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(index.toString()), + Text(label), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: TextField( + textAlign: TextAlign.center, + controller: controller, + minLines: 5, + maxLines: 10, + onTap: () { + ref.read(songEditorLyricsFieldProvider.notifier).setCursorLocation( + textFieldIndex: index, + cursorPos: controller.selection.baseOffset, + ); + }, + onChanged: (input) { + ref.read(songEditorLyricsFieldProvider.notifier).setCursorLocation( + textFieldIndex: index, + cursorPos: controller.selection.baseOffset, + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/lib/media_center/widgets/song_preview.dart b/lib/media_center/widgets/song_preview.dart index 1ea5a0b..49d1319 100644 --- a/lib/media_center/widgets/song_preview.dart +++ b/lib/media_center/widgets/song_preview.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import '../../models/song_model.dart'; class SongPreview extends StatelessWidget { - const SongPreview({super.key, required this.song}); + const SongPreview(this.song, {super.key}); final Song song;