@@ -30,10 +30,11 @@ import Yoga.React.Native.MacOS.ScrollView (nativeScrollView)
30import Yoga.React.Native.MacOS.Sidebar (sidebarLayout)
30import Yoga.React.Native.MacOS.Sidebar (sidebarLayout)
31import Yoga.React.Native.MacOS.TextField (nativeTextField)
31import Yoga.React.Native.MacOS.TextField (nativeTextField)
32import Yoga.React.Native.MacOS.Types as T
32import Yoga.React.Native.MacOS.Types as T
33+import Yoga.React.Native.Pressable (pressable)
33import Yoga.React.Native.Reanimated as R
34import Yoga.React.Native.Reanimated as R
34import Yoga.React.Native.Style as Style
35import Yoga.React.Native.Style as Style
36−type Reaction = { emoji :: String, count :: Int }
37+type Reaction = { emoji :: String, count :: Int, mine :: Boolean }
38type Message =
39type Message =
39 { sender :: String
40 { sender :: String
@@ -90,8 +91,12 @@ customEmojiMap = emojiMap
91addReaction :: String -> Array Reaction -> Array Reaction
92addReaction :: String -> Array Reaction -> Array Reaction
92addReaction emoji rs = case filter (\r -> r.emoji == emoji) rs of
93addReaction emoji rs = case filter (\r -> r.emoji == emoji) rs of
93− [] -> snoc rs { emoji, count: 1 }
94+ [] -> snoc rs { emoji, count: 1, mine: true }
94− _ -> map (\r -> if r.emoji == emoji then r { count = r.count + 1 } else r) rs
95+ _ -> map (\r -> if r.emoji == emoji then r { count = r.count + 1, mine = true } else r) rs
97+removeReaction :: String -> Array Reaction -> Array Reaction
98+removeReaction emoji rs = filter (\r -> r.count > 0) $
99+ map (\r -> if r.emoji == emoji && r.mine then r { count = r.count - 1, mine = false } else r) rs
96mockRooms :: Array Room
101mockRooms :: Array Room
97mockRooms =
102mockRooms =
@@ -107,40 +112,43 @@ mockMessages room = map (\m -> m { body = replaceEmoji m.body }) (mockMessagesRa
107noReactions :: Array Reaction
112noReactions :: Array Reaction
108noReactions = []
113noReactions = []
115+mkReaction :: String -> Int -> Reaction
116+mkReaction emoji count = { emoji, count, mine: false }
110mockMessagesRaw :: String -> Array Message
118mockMessagesRaw :: String -> Array Message
111mockMessagesRaw = case _ of
119mockMessagesRaw = case _ of
112 "general" ->
120 "general" ->
113− [ { sender: "Alice", body: "Hey everyone! Welcome to the chat demo :wave:", isMine: false, reactions: [ { emoji: "👋", count: 3 } ], replyTo: Nothing }
121+ [ { sender: "Alice", body: "Hey everyone! Welcome to the chat demo :wave:", isMine: false, reactions: [ mkReaction "👋" 3 ], replyTo: Nothing }
114− , { sender: "Bob", body: "This is built with :ps: + React Native macOS", isMine: false, reactions: [ { emoji: "🔥", count: 2 }, { emoji: "❤️", count: 1 } ], replyTo: Nothing }
122+ , { sender: "Bob", body: "This is built with :ps: + React Native macOS", isMine: false, reactions: [ mkReaction "🔥" 2, mkReaction "❤️" 1 ], replyTo: Nothing }
115 , { sender: "You", body: "That's awesome! :fire:", isMine: true, reactions: noReactions, replyTo: Just 1 }
123 , { sender: "You", body: "That's awesome! :fire:", isMine: true, reactions: noReactions, replyTo: Just 1 }
116 , { sender: "Alice", body: "Try typing :rocket: or :heart: in a message", isMine: false, reactions: noReactions, replyTo: Nothing }
124 , { sender: "Alice", body: "Try typing :rocket: or :heart: in a message", isMine: false, reactions: noReactions, replyTo: Nothing }
117− , { sender: "You", body: "The bubbles look great :sparkles:", isMine: true, reactions: [ { emoji: "👍", count: 2 } ], replyTo: Nothing }
125+ , { sender: "You", body: "The bubbles look great :sparkles:", isMine: true, reactions: [ mkReaction "👍" 2 ], replyTo: Nothing }
118 , { sender: "Bob", body: "Click the smiley to react!", isMine: false, reactions: noReactions, replyTo: Just 4 }
126 , { sender: "Bob", body: "Click the smiley to react!", isMine: false, reactions: noReactions, replyTo: Just 4 }
119− , { sender: "Alice", body: ":party: :shipit:", isMine: false, reactions: [ { emoji: "🎉", count: 5 } ], replyTo: Nothing }
127+ , { sender: "Alice", body: ":party: :shipit:", isMine: false, reactions: [ mkReaction "🎉" 5 ], replyTo: Nothing }
120− , { sender: "Bob", body: ":parrot: Party Parrot! :parrot:", isMine: false, reactions: [ { emoji: "🦜", count: 3 } ], replyTo: Nothing }
128+ , { sender: "Bob", body: ":parrot: Party Parrot! :parrot:", isMine: false, reactions: [ mkReaction "🦜" 3 ], replyTo: Nothing }
121 , { sender: "You", body: ":dealwithit: deal with it :doge:", isMine: true, reactions: noReactions, replyTo: Nothing }
129 , { sender: "You", body: ":dealwithit: deal with it :doge:", isMine: true, reactions: noReactions, replyTo: Nothing }
122 , { sender: "Alice", body: ":typingcat:", isMine: false, reactions: noReactions, replyTo: Nothing }
130 , { sender: "Alice", body: ":typingcat:", isMine: false, reactions: noReactions, replyTo: Nothing }
124 "purescript" ->
132 "purescript" ->
125− [ { sender: "Phil", body: "Has anyone tried the new compiler release?", isMine: false, reactions: [ { emoji: "👍", count: 4 } ], replyTo: Nothing }
133+ [ { sender: "Phil", body: "Has anyone tried the new compiler release?", isMine: false, reactions: [ mkReaction "👍" 4 ], replyTo: Nothing }
126 , { sender: "You", body: "Yes! The build times are much better", isMine: true, reactions: noReactions, replyTo: Just 0 }
134 , { sender: "You", body: "Yes! The build times are much better", isMine: true, reactions: noReactions, replyTo: Just 0 }
127− , { sender: "Phil", body: "Row polymorphism makes FFI so clean", isMine: false, reactions: [ { emoji: "❤️", count: 3 } ], replyTo: Nothing }
135+ , { sender: "Phil", body: "Row polymorphism makes FFI so clean", isMine: false, reactions: [ mkReaction "❤️" 3 ], replyTo: Nothing }
128 , { sender: "Jordan", body: "The ecosystem keeps getting better", isMine: false, reactions: noReactions, replyTo: Nothing }
136 , { sender: "Jordan", body: "The ecosystem keeps getting better", isMine: false, reactions: noReactions, replyTo: Nothing }
129− , { sender: "You", body: "Agreed, especially for React Native", isMine: true, reactions: [ { emoji: "🔥", count: 1 } ], replyTo: Just 2 }
137+ , { sender: "You", body: "Agreed, especially for React Native", isMine: true, reactions: [ mkReaction "🔥" 1 ], replyTo: Just 2 }
130− , { sender: "Jordan", body: ":opensourceparrot: :ps: :haskell: :opensourceparrot:", isMine: false, reactions: [ { emoji: "❤️", count: 4 } ], replyTo: Nothing }
138+ , { sender: "Jordan", body: ":opensourceparrot: :ps: :haskell: :opensourceparrot:", isMine: false, reactions: [ mkReaction "❤️" 4 ], replyTo: Nothing }
132 "react-native" ->
140 "react-native" ->
133− [ { sender: "Christoph", body: "macOS support is looking solid", isMine: false, reactions: [ { emoji: "🔥", count: 3 } ], replyTo: Nothing }
141+ [ { sender: "Christoph", body: "macOS support is looking solid", isMine: false, reactions: [ mkReaction "🔥" 3 ], replyTo: Nothing }
134 , { sender: "You", body: "Native controls feel right at home", isMine: true, reactions: noReactions, replyTo: Just 0 }
142 , { sender: "You", body: "Native controls feel right at home", isMine: true, reactions: noReactions, replyTo: Just 0 }
135 , { sender: "Christoph", body: "NSOutlineView, NSSplitView, toolbars...", isMine: false, reactions: noReactions, replyTo: Nothing }
143 , { sender: "Christoph", body: "NSOutlineView, NSSplitView, toolbars...", isMine: false, reactions: noReactions, replyTo: Nothing }
136− , { sender: "You", body: "Even the vibrancy effects work", isMine: true, reactions: [ { emoji: "😮", count: 2 } ], replyTo: Nothing }
144+ , { sender: "You", body: "Even the vibrancy effects work", isMine: true, reactions: [ mkReaction "😮" 2 ], replyTo: Nothing }
137− , { sender: "Christoph", body: "🚀", isMine: false, reactions: [ { emoji: "🚀", count: 7 } ], replyTo: Nothing }
145+ , { sender: "Christoph", body: "🚀", isMine: false, reactions: [ mkReaction "🚀" 7 ], replyTo: Nothing }
138− , { sender: "You", body: ":fastparrot: :congaparrot: :ultraparrot: conga line!", isMine: true, reactions: [ { emoji: "🎉", count: 3 } ], replyTo: Nothing }
146+ , { sender: "You", body: ":fastparrot: :congaparrot: :ultraparrot: conga line!", isMine: true, reactions: [ mkReaction "🎉" 3 ], replyTo: Nothing }
140 "random" ->
148 "random" ->
141 [ { sender: "Eve", body: "Anyone here?", isMine: false, reactions: noReactions, replyTo: Nothing }
149 [ { sender: "Eve", body: "Anyone here?", isMine: false, reactions: noReactions, replyTo: Nothing }
142− , { sender: "You", body: "👋", isMine: true, reactions: [ { emoji: "👋", count: 1 } ], replyTo: Just 0 }
150+ , { sender: "You", body: "👋", isMine: true, reactions: [ mkReaction "👋" 1 ], replyTo: Just 0 }
143− , { sender: "Eve", body: ":partyblob: :partyblob: :partyblob:", isMine: false, reactions: [ { emoji: "🎉", count: 2 } ], replyTo: Nothing }
151+ , { sender: "Eve", body: ":partyblob: :partyblob: :partyblob:", isMine: false, reactions: [ mkReaction "🎉" 2 ], replyTo: Nothing }
@@ -260,16 +268,35 @@ chatDemo = component "ChatDemo" \dp -> React.do
271+ toggleReaction idx emoji = do
272+ setMessages \msgs -> fromMaybe msgs
276+ existing = filter (\rx -> rx.emoji == emoji) m.reactions
278+ case existing !! 0 of
279+ Just rx | rx.mine -> m { reactions = removeReaction emoji m.reactions }
280+ _ -> m { reactions = addReaction emoji m.reactions }
263− reactionPills msg =
285+ reactionPills idx msg =
264 if length msg.reactions == 0 then mempty
286 if length msg.reactions == 0 then mempty
265 else view { style: tw "flex-row mt-1" <> Style.style { alignSelf: if msg.isMine then "flex-end" else "flex-start" } }
287 else view { style: tw "flex-row mt-1" <> Style.style { alignSelf: if msg.isMine then "flex-end" else "flex-start" } }
266− ( msg.reactions <#> \r ->
288+ ( msg.reactions <#> \rx ->
268− { style: tw "flex-row items-center rounded-full px-2 py-0.5 mr-1"
290+ { onPress: handler_ (toggleReaction idx rx.emoji)
269− <> Style.style { backgroundColor: reactionBg }
293+ { style: tw "flex-row items-center rounded-full px-2 py-0.5 mr-1"
295+ { backgroundColor: if rx.mine then sentBubbleBg else reactionBg }
271− [ text { style: Style.style { fontSize: 12.0 } } r.emoji
297+ [ text { style: Style.style { fontSize: 12.0 } } rx.emoji
272− , text { style: tw "text-xs ml-1" <> Style.style { color: dp.dimFg } } (show r.count)
298+ , text { style: tw "text-xs ml-1" <> Style.style { color: if rx.mine then "#FFFFFF" else dp.dimFg } } (show rx.count)
@@ -405,7 +432,7 @@ chatDemo = component "ChatDemo" \dp -> React.do
406 , if not msg.isMine then reactionPicker idx else mempty
433 , if not msg.isMine then reactionPicker idx else mempty
408− , reactionPills msg
435+ , reactionPills idx msg