package chat import ( "fmt" "log/slog" "github.com/ayn2op/discordo/internal/config" "github.com/ayn2op/tview" "github.com/ayn2op/tview/help" "github.com/ayn2op/tview/keybind" "github.com/ayn2op/tview/picker" "github.com/diamondburned/arikawa/v3/discord" ) const emojiPickerLayerName = "emojiPicker" type emojiPicker struct { *picker.Model chatView *Model browseMode bool targetMessageID discord.MessageID targetChannelID discord.ChannelID } var _ help.KeyMap = (*emojiPicker)(nil) func newEmojiPicker(cfg *config.Config, chatView *Model) *emojiPicker { ep := &emojiPicker{Model: picker.NewModel(), chatView: chatView} ConfigurePicker(ep.Model, cfg, "Emoji") return ep } func (ep *emojiPicker) resetBrowse() { ep.browseMode = false } // commonEmoji returns a list of frequently used unicode emoji for the picker. var commonEmoji = []struct { emoji string names string }{ {"๐Ÿ‘", "thumbsup thumbs_up +1 like"}, {"๐Ÿ‘Ž", "thumbsdown thumbs_down -1 dislike"}, {"โค๏ธ", "heart love red_heart"}, {"๐Ÿ˜‚", "joy laughing tears"}, {"๐Ÿ˜ญ", "sob crying"}, {"๐Ÿ˜Š", "blush smile happy"}, {"๐Ÿ˜", "heart_eyes love"}, {"๐Ÿคฃ", "rofl rolling"}, {"๐Ÿ™‚", "slightly_smiling_face"}, {"๐Ÿ˜”", "pensive sad"}, {"๐Ÿ”ฅ", "fire lit hot"}, {"โœ…", "white_check_mark check yes"}, {"โŒ", "x cross no"}, {"๐Ÿ‘€", "eyes look"}, {"๐ŸŽ‰", "tada party celebration"}, {"๐Ÿ’€", "skull dead"}, {"๐Ÿค”", "thinking hmm"}, {"๐Ÿ˜", "neutral_face"}, {"๐Ÿคท", "shrug"}, {"๐Ÿ‘‹", "wave hello hi"}, {"๐Ÿ™", "pray please thanks"}, {"๐Ÿ’ฏ", "100 hundred perfect"}, {"๐Ÿ˜Ž", "sunglasses cool"}, {"๐Ÿฅณ", "partying_face party"}, {"๐Ÿ˜ข", "cry sad"}, {"๐Ÿ˜…", "sweat_smile nervous"}, {"๐Ÿค—", "hugs hugging"}, {"๐Ÿ˜ค", "triumph angry"}, {"๐Ÿฅบ", "pleading_face"}, {"๐Ÿ’ช", "muscle strong flex"}, {"โœจ", "sparkles stars"}, {"๐Ÿ’œ", "purple_heart"}, {"๐Ÿ’™", "blue_heart"}, {"๐Ÿ’š", "green_heart"}, {"๐Ÿงก", "orange_heart"}, {"๐Ÿ’›", "yellow_heart"}, {"๐Ÿ–ค", "black_heart"}, {"๐Ÿค", "white_heart"}, {"โญ", "star"}, {"๐ŸŒŸ", "star2 glowing_star"}, {"๐Ÿ˜ฎ", "open_mouth surprised"}, {"๐Ÿ˜ฑ", "scream shocked"}, {"๐Ÿ˜ˆ", "smiling_imp devil"}, {"๐Ÿ’€", "skull"}, {"๐Ÿคก", "clown clown_face"}, {"๐Ÿซก", "salute saluting_face"}, {"๐Ÿซ ", "melting_face"}, {"๐Ÿ’ค", "zzz sleep"}, {"๐Ÿค", "handshake"}, {"๐Ÿณ๏ธ", "white_flag surrender"}, } func (ep *emojiPicker) update() { items := make(picker.Items, 0, len(commonEmoji)+50) for _, e := range commonEmoji { items = append(items, picker.Item{ Text: e.emoji + " " + e.names[:min(len(e.names), indexOf(e.names, ' '))], FilterText: e.names, Reference: discord.APIEmoji(e.emoji), }) } // Add guild custom emoji if available selectedChannel := ep.chatView.SelectedChannel() if selectedChannel != nil && selectedChannel.GuildID.IsValid() { emojis, err := ep.chatView.state.Cabinet.Emojis(selectedChannel.GuildID) if err == nil { for _, emoji := range emojis { if !emoji.Available { continue } items = append(items, picker.Item{ Text: ":" + emoji.Name + ":", FilterText: emoji.Name, Reference: emoji.APIString(), }) } } } ep.Model.SetItems(items) } func indexOf(s string, c byte) int { for i := range len(s) { if s[i] == c { return i } } return len(s) } func (ep *emojiPicker) HandleEvent(event tview.Event) tview.Command { switch event := event.(type) { case *tview.KeyEvent: if cmd, handled := pickerBrowseHandleKey(event, &ep.browseMode, ep.Model, func() { ep.chatView.closeEmojiPicker() }); handled { return cmd } case *picker.SelectedEvent: apiEmoji, ok := event.Reference.(discord.APIEmoji) if !ok { return nil } channelID := ep.targetChannelID messageID := ep.targetMessageID ep.chatView.closeEmojiPicker() return func() tview.Event { if err := ep.chatView.state.React(channelID, messageID, apiEmoji); err != nil { slog.Error("failed to add reaction", "err", err, "emoji", fmt.Sprint(apiEmoji)) } return nil } case *picker.CancelEvent: ep.chatView.closeEmojiPicker() return nil } return ep.Model.HandleEvent(event) } func (ep *emojiPicker) ShortHelp() []keybind.Keybind { cfg := ep.chatView.cfg.Keybinds.Picker return []keybind.Keybind{cfg.Up.Keybind, cfg.Down.Keybind, cfg.Select.Keybind, cfg.Cancel.Keybind} } func (ep *emojiPicker) FullHelp() [][]keybind.Keybind { cfg := ep.chatView.cfg.Keybinds.Picker return [][]keybind.Keybind{ {cfg.Up.Keybind, cfg.Down.Keybind, cfg.Top.Keybind, cfg.Bottom.Keybind}, {cfg.Select.Keybind, cfg.Cancel.Keybind}, } }