wip new emoji picker
has perf issues currently
This commit is contained in:
parent
e5f560d03b
commit
a0fa30e036
4 changed files with 142 additions and 15 deletions
|
|
@ -4,7 +4,6 @@ import "package:collection/collection.dart";
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:flutter/services.dart";
|
import "package:flutter/services.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:material_ui/material_ui.dart";
|
|
||||||
import "package:nexus/models/emoji.dart";
|
import "package:nexus/models/emoji.dart";
|
||||||
import "package:nexus/models/emoji_category.dart";
|
import "package:nexus/models/emoji_category.dart";
|
||||||
|
|
||||||
|
|
@ -25,7 +24,7 @@ class EmojiController extends AsyncNotifier<IList<EmojiCategory>> {
|
||||||
return .new(
|
return .new(
|
||||||
grouped.entries.map(
|
grouped.entries.map(
|
||||||
(entry) => .new(
|
(entry) => .new(
|
||||||
icon: Text(entry.value.first.emoji.emoji),
|
icon: entry.value.first.emoji.widget,
|
||||||
name: entry.key,
|
name: entry.key,
|
||||||
emojis: .new(entry.value.map((e) => e.emoji)),
|
emojis: .new(entry.value.map((e) => e.emoji)),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:freezed_annotation/freezed_annotation.dart";
|
import "package:freezed_annotation/freezed_annotation.dart";
|
||||||
|
import "package:flutter/widgets.dart";
|
||||||
part "emoji.freezed.dart";
|
part "emoji.freezed.dart";
|
||||||
part "emoji.g.dart";
|
part "emoji.g.dart";
|
||||||
|
|
||||||
@Freezed(toJson: false, fromJson: false)
|
@Freezed(toJson: false, fromJson: false)
|
||||||
@JsonSerializable()
|
@JsonSerializable(createToJson: false)
|
||||||
class Emoji({
|
class Emoji({
|
||||||
required final String emoji,
|
@JsonKey(fromJson: Emoji.widgetFromJson, name: "emoji")
|
||||||
|
required final Widget widget,
|
||||||
required final IList<String> aliases,
|
required final IList<String> aliases,
|
||||||
required final String description,
|
required final String description,
|
||||||
required final IList<String> tags,
|
required final IList<String> tags,
|
||||||
}) with _$Emoji {
|
}) with _$Emoji {
|
||||||
Map<String, Object?> toJson() => _$EmojiToJson(this);
|
static Widget widgetFromJson(String emoji) => Text(emoji);
|
||||||
|
|
||||||
factory Emoji.fromJson(Map<String, Object?> json) => _$EmojiFromJson(json);
|
factory Emoji.fromJson(Map<String, Object?> json) => _$EmojiFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,131 @@
|
||||||
import "package:flutter/widgets.dart";
|
import "package:collection/collection.dart";
|
||||||
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
class EmojiPicker extends StatelessWidget {
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
const new({super.key});
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:nexus/controllers/emoji.dart";
|
||||||
|
import "package:nexus/helpers/extensions/better_when.dart";
|
||||||
|
import "package:nexus/models/emoji_category.dart";
|
||||||
|
import "package:nexus/widgets/divider_text.dart";
|
||||||
|
import "package:super_sliver_list/super_sliver_list.dart";
|
||||||
|
|
||||||
|
final class const EmojiPicker({
|
||||||
|
final IList<EmojiCategory> extraCategories = const IList.empty(),
|
||||||
|
super.key,
|
||||||
|
}) extends HookConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Placeholder(); // TODO: Emoji picker
|
Widget build(BuildContext context, WidgetRef ref) => Container(
|
||||||
|
padding: .symmetric(horizontal: 16),
|
||||||
|
constraints: .loose(.new(500, 700)),
|
||||||
|
child: ref
|
||||||
|
.watch(EmojiController.provider)
|
||||||
|
.betterWhen(
|
||||||
|
data: (categories) {
|
||||||
|
final combined = categories.addAll(extraCategories);
|
||||||
|
|
||||||
|
return HookBuilder(
|
||||||
|
builder: (context) {
|
||||||
|
final selectedCategory = useState<int?>(null);
|
||||||
|
final scrollController = useScrollController();
|
||||||
|
final listController = useMemoized(ListController.new);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
void onChange() {
|
||||||
|
final range = listController.visibleRange;
|
||||||
|
if (range != null) {
|
||||||
|
final firstVisible = range.$1;
|
||||||
|
if (selectedCategory.value != firstVisible) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
selectedCategory.value = firstVisible;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listController.addListener(onChange);
|
||||||
|
return () => listController.removeListener(onChange);
|
||||||
|
}, [listController]);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
SearchAnchor.bar(
|
||||||
|
barHintText: "Search emoji",
|
||||||
|
suggestionsBuilder: (context, controller) {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 48,
|
||||||
|
child: ListView(
|
||||||
|
scrollDirection: .horizontal,
|
||||||
|
children: combined
|
||||||
|
.mapIndexed(
|
||||||
|
(index, category) => Padding(
|
||||||
|
padding: .symmetric(horizontal: 4),
|
||||||
|
child: FilterChip(
|
||||||
|
label: Text(category.name),
|
||||||
|
avatar: category.icon,
|
||||||
|
selected: selectedCategory.value == index,
|
||||||
|
showCheckmark: false,
|
||||||
|
onSelected: (_) {
|
||||||
|
selectedCategory.value = index;
|
||||||
|
|
||||||
|
listController.animateToItem(
|
||||||
|
index: index,
|
||||||
|
scrollController: scrollController,
|
||||||
|
curve: (_) => Curves.easeInOut,
|
||||||
|
duration: (_) =>
|
||||||
|
Duration(milliseconds: 300),
|
||||||
|
alignment: 0,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: SuperListView.builder(
|
||||||
|
listController: listController,
|
||||||
|
controller: scrollController,
|
||||||
|
itemCount: combined.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final category = combined[index];
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
DividerText(category.name),
|
||||||
|
DefaultTextStyle(
|
||||||
|
style: TextStyle(fontSize: 24),
|
||||||
|
child: GridView(
|
||||||
|
gridDelegate:
|
||||||
|
const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
|
maxCrossAxisExtent: 52,
|
||||||
|
mainAxisExtent: 52,
|
||||||
|
),
|
||||||
|
children: category.emojis
|
||||||
|
.map(
|
||||||
|
(emoji) => IconButton(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: emoji.widget,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import "package:nexus/models/content/message.dart";
|
||||||
import "package:nexus/models/event.dart";
|
import "package:nexus/models/event.dart";
|
||||||
import "package:nexus/models/relation_type.dart";
|
import "package:nexus/models/relation_type.dart";
|
||||||
import "package:nexus/widgets/composer/composer.dart";
|
import "package:nexus/widgets/composer/composer.dart";
|
||||||
|
import "package:nexus/widgets/emoji_picker.dart";
|
||||||
import "package:nexus/widgets/pinned_events_drawer.dart";
|
import "package:nexus/widgets/pinned_events_drawer.dart";
|
||||||
import "package:nexus/widgets/renderers/event.dart";
|
import "package:nexus/widgets/renderers/event.dart";
|
||||||
import "package:nexus/widgets/member_list.dart";
|
import "package:nexus/widgets/member_list.dart";
|
||||||
|
|
@ -237,11 +238,14 @@ final class const RoomChat({
|
||||||
icon: Icon(Icons.emoji_emotions),
|
icon: Icon(Icons.emoji_emotions),
|
||||||
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
// TODO: More adaptive
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
showModalBottomSheet(
|
||||||
// TODO: Push
|
showDragHandle: true,
|
||||||
// onSelection: (emoji) =>
|
isScrollControlled: true,
|
||||||
// notifier.sendReaction(emoji, event).onError(showError),
|
context: context,
|
||||||
|
builder: (context) => EmojiPicker(),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue