Skip to content

Commit

Permalink
reworked state management
Browse files Browse the repository at this point in the history
  • Loading branch information
albemala committed Oct 4, 2023
1 parent aea2d56 commit 1063b6d
Show file tree
Hide file tree
Showing 43 changed files with 1,156 additions and 629 deletions.
4 changes: 2 additions & 2 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ include: package:very_good_analysis/analysis_options.yaml

linter:
rules:
sort_pub_dependencies: false
file_names: false
public_member_api_docs: false
flutter_style_todos: false
sort_constructors_first: false
lines_longer_than_80_chars: false
sort_unnamed_constructors_first: false
always_put_required_named_parameters_first: false
14 changes: 0 additions & 14 deletions lib/blocs/app-info-bloc.dart

This file was deleted.

55 changes: 0 additions & 55 deletions lib/blocs/filtered-glyphs-bloc.dart

This file was deleted.

17 changes: 0 additions & 17 deletions lib/blocs/glyph-details-bloc.dart

This file was deleted.

31 changes: 0 additions & 31 deletions lib/blocs/theme-bloc.dart

This file was deleted.

51 changes: 0 additions & 51 deletions lib/blocs/urls-bloc.dart

This file was deleted.

91 changes: 91 additions & 0 deletions lib/conductors/filtered-glyphs-conductor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import 'package:app/conductors/glyphs-conductor.dart';
import 'package:app/functions/glyphs.dart';
import 'package:app/models/glyph.dart';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_state_management/flutter_state_management.dart';

class FilteredGlyphsConductor extends Conductor {
factory FilteredGlyphsConductor.fromContext(BuildContext context) {
return FilteredGlyphsConductor(
context.getConductor<GlyphsConductor>(),
);
}

final GlyphsConductor _glyphsConductor;

final searchFocusNode = FocusNode();

final searchQueryController = TextEditingController();

final filteredGlyphs = ValueNotifier<Map<String, List<Glyph>>>({});

FilteredGlyphsConductor(this._glyphsConductor) {
_init();
}

void _init() {
_glyphsConductor.glyphs.addListener(_updateFilteredGlyphs);
searchFocusNode.addListener(onSearchFocusChanged);
searchQueryController.addListener(onSearchChanged);
}

@override
void dispose() {
_glyphsConductor.glyphs.removeListener(_updateFilteredGlyphs);
searchFocusNode.removeListener(onSearchFocusChanged);
searchQueryController.removeListener(onSearchChanged);

searchFocusNode.dispose();
searchQueryController.dispose();
filteredGlyphs.dispose();
}

void onSearchFocusChanged() {
// When the search field gets focused, select the existing text
if (searchFocusNode.hasFocus) {
searchQueryController.value = searchQueryController.value.copyWith(
selection: TextSelection(
baseOffset: 0,
extentOffset: searchQueryController.text.length,
),
composing: TextRange.empty,
);
}
}

void setSearchQuery(String value) {
searchQueryController.value = TextEditingValue(
text: value,
selection: searchQueryController.selection,
);
}

void onSearchChanged() {
EasyDebounce.debounce(
'search-debounce',
const Duration(milliseconds: 300),
_updateFilteredGlyphs,
);
}

void _updateFilteredGlyphs() {
if (searchQueryController.text.isEmpty) {
filteredGlyphs.value = glyphsByGroup(_glyphsConductor.glyphs.value);
} else {
final filteredGlyphs = _glyphsConductor.glyphs.value
.where(
(glyph) => matchesSearchTerm(
glyph,
searchQueryController.text,
),
)
.toList();
this.filteredGlyphs.value = glyphsByGroup(filteredGlyphs);
}
}

void clearSearch() {
searchQueryController.clear();
}
}
37 changes: 37 additions & 0 deletions lib/conductors/glyph-details-conductor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:app/models/glyph.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_state_management/flutter_state_management.dart';

class GlyphDetailsConductor extends Conductor {
factory GlyphDetailsConductor.fromContext(BuildContext context) {
return GlyphDetailsConductor();
}

final selectedGlyph = ValueNotifier<Glyph?>(null);

final isGlyphDetailsVisible = ValueNotifier<bool>(false);

GlyphDetailsConductor() {
selectedGlyph.addListener(updateIsGlyphDetailsVisible);
}

@override
void dispose() {
selectedGlyph.removeListener(updateIsGlyphDetailsVisible);

selectedGlyph.dispose();
isGlyphDetailsVisible.dispose();
}

void showDetailsForGlyph(Glyph? glyph) {
selectedGlyph.value = glyph;
}

void hideDetails() {
selectedGlyph.value = null;
}

void updateIsGlyphDetailsVisible() {
isGlyphDetailsVisible.value = selectedGlyph.value != null;
}
}
80 changes: 80 additions & 0 deletions lib/conductors/glyphs-conductor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'dart:convert';

import 'package:app/extensions/string.dart';
import 'package:app/models/emoji.dart';
import 'package:app/models/glyph.dart';
import 'package:app/models/symbol.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_state_management/flutter_state_management.dart';

/// A class that loads and provides the glyphs (emojis, symbols).
class GlyphsConductor extends Conductor {
factory GlyphsConductor.fromContext(BuildContext context) {
return GlyphsConductor();
}

final glyphs = ValueNotifier<List<Glyph>>([]);

GlyphsConductor() {
_init();
}

Future<void> _init() async {
final emojis = await loadEmojis();
final symbols = await loadSymbols();
glyphs.value = [
...emojis,
...symbols,
];
}

@override
void dispose() {
glyphs.dispose();
}
}

@visibleForTesting
Future<Iterable<Glyph>> loadEmojis() async {
final emojisDataFile = await rootBundle.loadString(
'assets/data/emojis.json',
);
final emojisData = json.decode(emojisDataFile) as List<dynamic>;
return emojisData
.map(
(item) => Emoji.fromJson(
item as Map<String, dynamic>,
),
)
.map(
(emoji) => Glyph(
char: emoji.char,
name: emoji.name.toFirstUpperCase(),
keywords: emoji.keywords,
group: emoji.group,
),
);
}

@visibleForTesting
Future<Iterable<Glyph>> loadSymbols() async {
final symbolsDataFile = await rootBundle.loadString(
'assets/data/symbols.json',
);
final symbolsData = json.decode(symbolsDataFile) as List<dynamic>;
return symbolsData
.map(
(item) => Symbol.fromJson(
item as Map<String, dynamic>,
),
)
.map(
(symbol) => Glyph(
char: String.fromCharCode(symbol.charcode),
name: symbol.name,
keywords: [],
group: symbol.group,
),
);
}
Loading

0 comments on commit 1063b6d

Please sign in to comment.