Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| The phone app no longer has g1t's own agents, to match the site. The Agents tab is gone from the bar, and Customize (skills, connectors and plugins) is gone with its row in More. New message finds and messages people only, and its search and heading say so. A card an agent posted earlier still shows with its title, state, details and links, but its other buttons are no longer drawn, since chat no longer answers them; an old agent's messages and direct messages still show, under the name chat gives them, marked Deleted agent. The app stops asking for agents:run when it signs in. An old g1t:// link to Agents, the skill library, Customize or the Marketplace opens Home, while the integrations directory opens on the site. Notifications, the sign-in screen and the access-token error no longer mention agents waiting on you or agent tokens, and the sign-in screen speaks of reviewing what you and your coding agents make. Pull requests still show the coding agent they were made with. | 1 | import type { ChatMessage, MessageCard } from "@g1t/contracts"; |
| The phone app: Chat in full with phone notifications, plus Code and Customize (#4) | 2 | import * as Haptics from "expo-haptics"; |
| 3 | import { router } from "expo-router"; | |
| 4 | import * as WebBrowser from "expo-web-browser"; | |
| 5 | import { AlertCircle, Clock, MessageSquareReply } from "lucide-react-native"; | |
| The phone app no longer has g1t's own agents, to match the site. The Agents tab is gone from the bar, and Customize (skills, connectors and plugins) is gone with its row in More. New message finds and messages people only, and its search and heading say so. A card an agent posted earlier still shows with its title, state, details and links, but its other buttons are no longer drawn, since chat no longer answers them; an old agent's messages and direct messages still show, under the name chat gives them, marked Deleted agent. The app stops asking for agents:run when it signs in. An old g1t:// link to Agents, the skill library, Customize or the Marketplace opens Home, while the integrations directory opens on the site. Notifications, the sign-in screen and the access-token error no longer mention agents waiting on you or agent tokens, and the sign-in screen speaks of reviewing what you and your coding agents make. Pull requests still show the coding agent they were made with. | 6 | import { memo } from "react"; |
| 7 | import { Pressable, StyleSheet, View } from "react-native"; | |
| The phone app: Chat in full with phone notifications, plus Code and Customize (#4) | 8 | |
| 9 | import { Markdown } from "@/components/chat/markdown"; | |
| 10 | import { Avatar, Badge, Text } from "@/components/ui"; | |
| 11 | import type { Shown } from "@/lib/chat"; | |
| 12 | import { routeFor } from "@/lib/links"; | |
| 13 | import { siteUrl } from "@/lib/server"; | |
| 14 | import { useSignedIn } from "@/lib/session"; | |
| 15 | import { clock } from "@/lib/time"; | |
| 16 | import { radius, space, useColors } from "@/theme"; | |
| 17 | ||
| 18 | type Props = { | |
| 19 | message: Shown; | |
| 20 | /** The first of a run by one author: draws the avatar and name. */ | |
| 21 | head: boolean; | |
| 22 | /** In a thread: no "replies" line under it. */ | |
| 23 | inThread?: boolean; | |
| 24 | onLongPress: (message: Shown) => void; | |
| 25 | onReact: (message: ChatMessage, emoji: string, add: boolean) => void; | |
| 26 | onRetry: (message: Shown) => void; | |
| 27 | }; | |
| 28 | ||
| 29 | /** | |
| 30 | * One message in a conversation. Messages by the same author within a few | |
| The phone app no longer has g1t's own agents, to match the site. The Agents tab is gone from the bar, and Customize (skills, connectors and plugins) is gone with its row in More. New message finds and messages people only, and its search and heading say so. A card an agent posted earlier still shows with its title, state, details and links, but its other buttons are no longer drawn, since chat no longer answers them; an old agent's messages and direct messages still show, under the name chat gives them, marked Deleted agent. The app stops asking for agents:run when it signs in. An old g1t:// link to Agents, the skill library, Customize or the Marketplace opens Home, while the integrations directory opens on the site. Notifications, the sign-in screen and the access-token error no longer mention agents waiting on you or agent tokens, and the sign-in screen speaks of reviewing what you and your coding agents make. Pull requests still show the coding agent they were made with. | 31 | * minutes run together under one name, as on the site. A message an agent |
| 32 | * posted before g1t's own agents were retired still shows, under the name | |
| 33 | * chat gives it, with its marker and a Deleted agent badge. | |
| The phone app: Chat in full with phone notifications, plus Code and Customize (#4) | 34 | */ |
| The phone app no longer has g1t's own agents, to match the site. The Agents tab is gone from the bar, and Customize (skills, connectors and plugins) is gone with its row in More. New message finds and messages people only, and its search and heading say so. A card an agent posted earlier still shows with its title, state, details and links, but its other buttons are no longer drawn, since chat no longer answers them; an old agent's messages and direct messages still show, under the name chat gives them, marked Deleted agent. The app stops asking for agents:run when it signs in. An old g1t:// link to Agents, the skill library, Customize or the Marketplace opens Home, while the integrations directory opens on the site. Notifications, the sign-in screen and the access-token error no longer mention agents waiting on you or agent tokens, and the sign-in screen speaks of reviewing what you and your coding agents make. Pull requests still show the coding agent they were made with. | 35 | export const Message = memo(function Message({ message, head, inThread, onLongPress, onReact, onRetry }: Props) { |
| The phone app: Chat in full with phone notifications, plus Code and Customize (#4) | 36 | const colors = useColors(); |
| 37 | const { server } = useSignedIn(); | |
| 38 | const { author } = message; | |
| 39 | const agent = author.kind === "agent"; | |
| 40 | const failed = message.local === "failed"; | |
| 41 | ||
| 42 | const openThread = () => | |
| 43 | router.push({ pathname: "/chat/thread", params: { channel: message.channel_id, root: message.id } }); | |
| 44 | ||
| 45 | return ( | |
| 46 | <Pressable | |
| 47 | onLongPress={() => { | |
| 48 | if (message.local) return; | |
| 49 | void Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium).catch(() => undefined); | |
| 50 | onLongPress(message); | |
| 51 | }} | |
| 52 | onPress={failed ? () => onRetry(message) : undefined} | |
| 53 | delayLongPress={300} | |
| 54 | style={({ pressed }) => [styles.row, { paddingTop: head ? space.md : 2, backgroundColor: pressed ? colors.raised : "transparent" }]} | |
| 55 | > | |
| 56 | <View style={styles.gutter}> | |
| 57 | {head ? ( | |
| 58 | <Avatar name={author.display_name} uri={author.avatar ? `${server.site}/avatars/${author.avatar}` : null} agent={agent} size={36} /> | |
| 59 | ) : null} | |
| 60 | </View> | |
| 61 | <View style={{ flex: 1, gap: 4, opacity: message.local === "sending" ? 0.6 : 1 }}> | |
| 62 | {head ? ( | |
| 63 | <View style={styles.header}> | |
| 64 | <Text style={{ fontWeight: "700" }} numberOfLines={1}> | |
| 65 | {author.display_name} | |
| 66 | </Text> | |
| The phone app no longer has g1t's own agents, to match the site. The Agents tab is gone from the bar, and Customize (skills, connectors and plugins) is gone with its row in More. New message finds and messages people only, and its search and heading say so. A card an agent posted earlier still shows with its title, state, details and links, but its other buttons are no longer drawn, since chat no longer answers them; an old agent's messages and direct messages still show, under the name chat gives them, marked Deleted agent. The app stops asking for agents:run when it signs in. An old g1t:// link to Agents, the skill library, Customize or the Marketplace opens Home, while the integrations directory opens on the site. Notifications, the sign-in screen and the access-token error no longer mention agents waiting on you or agent tokens, and the sign-in screen speaks of reviewing what you and your coding agents make. Pull requests still show the coding agent they were made with. | 67 | {agent ? <Badge label="Deleted agent" tone="neutral" /> : null} |
| The phone app: Chat in full with phone notifications, plus Code and Customize (#4) | 68 | <Text size="small" tone="faint"> |
| 69 | {clock(message.created_at)} | |
| 70 | </Text> | |
| 71 | </View> | |
| 72 | ) : null} | |
| 73 | {message.body ? <Markdown text={message.body} /> : null} | |
| 74 | {message.edited_at ? ( | |
| 75 | <Text size="small" tone="faint"> | |
| 76 | (edited) | |
| 77 | </Text> | |
| 78 | ) : null} | |
| The phone app no longer has g1t's own agents, to match the site. The Agents tab is gone from the bar, and Customize (skills, connectors and plugins) is gone with its row in More. New message finds and messages people only, and its search and heading say so. A card an agent posted earlier still shows with its title, state, details and links, but its other buttons are no longer drawn, since chat no longer answers them; an old agent's messages and direct messages still show, under the name chat gives them, marked Deleted agent. The app stops asking for agents:run when it signs in. An old g1t:// link to Agents, the skill library, Customize or the Marketplace opens Home, while the integrations directory opens on the site. Notifications, the sign-in screen and the access-token error no longer mention agents waiting on you or agent tokens, and the sign-in screen speaks of reviewing what you and your coding agents make. Pull requests still show the coding agent they were made with. | 79 | {message.kind === "card" && message.card ? <Card card={message.card} site={server.site} /> : null} |
| The phone app: Chat in full with phone notifications, plus Code and Customize (#4) | 80 | {message.reactions && message.reactions.length > 0 ? ( |
| 81 | <View style={styles.reactions}> | |
| 82 | {message.reactions.map((reaction) => ( | |
| 83 | <Pressable | |
| 84 | key={reaction.emoji} | |
| 85 | accessibilityLabel={`${reaction.emoji} ${reaction.count}${reaction.me ? ", yours" : ""}`} | |
| 86 | onPress={() => { | |
| 87 | void Haptics.selectionAsync().catch(() => undefined); | |
| 88 | onReact(message, reaction.emoji, !reaction.me); | |
| 89 | }} | |
| 90 | style={[ | |
| 91 | styles.reaction, | |
| 92 | { borderColor: reaction.me ? colors.accent : colors.line, backgroundColor: reaction.me ? colors.accentLow : colors.surface }, | |
| 93 | ]} | |
| 94 | > | |
| 95 | <Text size="small">{reaction.emoji}</Text> | |
| 96 | <Text size="small" style={{ color: reaction.me ? colors.accent : colors.muted, fontWeight: "600" }}> | |
| 97 | {reaction.count} | |
| 98 | </Text> | |
| 99 | </Pressable> | |
| 100 | ))} | |
| 101 | </View> | |
| 102 | ) : null} | |
| 103 | {!inThread && message.reply_count > 0 ? ( | |
| 104 | <Pressable onPress={openThread} style={styles.replies} hitSlop={8}> | |
| 105 | <MessageSquareReply size={14} color={colors.accent} /> | |
| 106 | <Text size="small" tone="accent" style={{ fontWeight: "600" }}> | |
| 107 | {message.reply_count === 1 ? "1 reply" : `${message.reply_count} replies`} | |
| 108 | </Text> | |
| 109 | </Pressable> | |
| 110 | ) : null} | |
| 111 | {message.local === "sending" ? ( | |
| 112 | <View style={styles.status}> | |
| 113 | <Clock size={12} color={colors.faint} /> | |
| 114 | <Text size="small" tone="faint"> | |
| 115 | Sending | |
| 116 | </Text> | |
| 117 | </View> | |
| 118 | ) : failed ? ( | |
| 119 | <View style={styles.status}> | |
| 120 | <AlertCircle size={12} color={colors.danger} /> | |
| 121 | <Text size="small" tone="danger"> | |
| 122 | Not sent. Tap to try again. | |
| 123 | </Text> | |
| 124 | </View> | |
| 125 | ) : null} | |
| 126 | </View> | |
| 127 | </Pressable> | |
| 128 | ); | |
| 129 | }); | |
| 130 | ||
| 131 | /** | |
| The phone app no longer has g1t's own agents, to match the site. The Agents tab is gone from the bar, and Customize (skills, connectors and plugins) is gone with its row in More. New message finds and messages people only, and its search and heading say so. A card an agent posted earlier still shows with its title, state, details and links, but its other buttons are no longer drawn, since chat no longer answers them; an old agent's messages and direct messages still show, under the name chat gives them, marked Deleted agent. The app stops asking for agents:run when it signs in. An old g1t:// link to Agents, the skill library, Customize or the Marketplace opens Home, while the integrations directory opens on the site. Notifications, the sign-in screen and the access-token error no longer mention agents waiting on you or agent tokens, and the sign-in screen speaks of reviewing what you and your coding agents make. Pull requests still show the coding agent they were made with. | 132 | * A card an agent posted before g1t's own agents were retired: what it was |
| 133 | * about, its state, and its links. Its other buttons no longer do anything, | |
| 134 | * so they are not drawn. | |
| The phone app: Chat in full with phone notifications, plus Code and Customize (#4) | 135 | */ |
| The phone app no longer has g1t's own agents, to match the site. The Agents tab is gone from the bar, and Customize (skills, connectors and plugins) is gone with its row in More. New message finds and messages people only, and its search and heading say so. A card an agent posted earlier still shows with its title, state, details and links, but its other buttons are no longer drawn, since chat no longer answers them; an old agent's messages and direct messages still show, under the name chat gives them, marked Deleted agent. The app stops asking for agents:run when it signs in. An old g1t:// link to Agents, the skill library, Customize or the Marketplace opens Home, while the integrations directory opens on the site. Notifications, the sign-in screen and the access-token error no longer mention agents waiting on you or agent tokens, and the sign-in screen speaks of reviewing what you and your coding agents make. Pull requests still show the coding agent they were made with. | 136 | function Card({ card, site }: { card: MessageCard; site: string }) { |
| The phone app: Chat in full with phone notifications, plus Code and Customize (#4) | 137 | const colors = useColors(); |
| The phone app no longer has g1t's own agents, to match the site. The Agents tab is gone from the bar, and Customize (skills, connectors and plugins) is gone with its row in More. New message finds and messages people only, and its search and heading say so. A card an agent posted earlier still shows with its title, state, details and links, but its other buttons are no longer drawn, since chat no longer answers them; an old agent's messages and direct messages still show, under the name chat gives them, marked Deleted agent. The app stops asking for agents:run when it signs in. An old g1t:// link to Agents, the skill library, Customize or the Marketplace opens Home, while the integrations directory opens on the site. Notifications, the sign-in screen and the access-token error no longer mention agents waiting on you or agent tokens, and the sign-in screen speaks of reviewing what you and your coding agents make. Pull requests still show the coding agent they were made with. | 138 | const links = (card.actions ?? []).filter((action) => !!action.href); |
| The phone app: Chat in full with phone notifications, plus Code and Customize (#4) | 139 | |
| 140 | const open = (href: string) => { | |
| 141 | const route = href.startsWith("/") ? routeFor(href) : null; | |
| 142 | if (route) router.push(route as never); | |
| 143 | else void WebBrowser.openBrowserAsync(href.startsWith("/") ? siteUrl({ site, api: "" }, href) : href); | |
| 144 | }; | |
| 145 | ||
| 146 | return ( | |
| 147 | <View style={[styles.card, { borderColor: colors.lineStrong, backgroundColor: colors.surface }]}> | |
| 148 | <Pressable disabled={!card.href} onPress={() => card.href && open(card.href)} style={{ gap: 4 }}> | |
| 149 | <View style={{ flexDirection: "row", gap: space.sm, alignItems: "flex-start" }}> | |
| 150 | <Text style={{ flex: 1, fontWeight: "700" }}>{card.title}</Text> | |
| 151 | {card.state ? <Badge label={card.state} tone={/fail|stop|denied|error/i.test(card.state) ? "danger" : /merged|done|approved|passed/i.test(card.state) ? "success" : "accent"} /> : null} | |
| 152 | </View> | |
| 153 | {card.detail ? ( | |
| 154 | <Text size="small" tone="muted"> | |
| 155 | {card.detail} | |
| 156 | </Text> | |
| 157 | ) : null} | |
| 158 | {card.body ? <Markdown text={card.body} muted /> : null} | |
| 159 | {card.fields?.length ? ( | |
| 160 | <View style={styles.fields}> | |
| 161 | {card.fields.map((field) => ( | |
| 162 | <View key={field.label} style={{ width: "48%" }}> | |
| 163 | <Text size="small" tone="faint"> | |
| 164 | {field.label} | |
| 165 | </Text> | |
| 166 | <Text size="small" numberOfLines={2}> | |
| 167 | {field.value} | |
| 168 | </Text> | |
| 169 | </View> | |
| 170 | ))} | |
| 171 | </View> | |
| 172 | ) : null} | |
| 173 | </Pressable> | |
| The phone app no longer has g1t's own agents, to match the site. The Agents tab is gone from the bar, and Customize (skills, connectors and plugins) is gone with its row in More. New message finds and messages people only, and its search and heading say so. A card an agent posted earlier still shows with its title, state, details and links, but its other buttons are no longer drawn, since chat no longer answers them; an old agent's messages and direct messages still show, under the name chat gives them, marked Deleted agent. The app stops asking for agents:run when it signs in. An old g1t:// link to Agents, the skill library, Customize or the Marketplace opens Home, while the integrations directory opens on the site. Notifications, the sign-in screen and the access-token error no longer mention agents waiting on you or agent tokens, and the sign-in screen speaks of reviewing what you and your coding agents make. Pull requests still show the coding agent they were made with. | 174 | {links.length ? ( |
| The phone app: Chat in full with phone notifications, plus Code and Customize (#4) | 175 | <View style={styles.actions}> |
| The phone app no longer has g1t's own agents, to match the site. The Agents tab is gone from the bar, and Customize (skills, connectors and plugins) is gone with its row in More. New message finds and messages people only, and its search and heading say so. A card an agent posted earlier still shows with its title, state, details and links, but its other buttons are no longer drawn, since chat no longer answers them; an old agent's messages and direct messages still show, under the name chat gives them, marked Deleted agent. The app stops asking for agents:run when it signs in. An old g1t:// link to Agents, the skill library, Customize or the Marketplace opens Home, while the integrations directory opens on the site. Notifications, the sign-in screen and the access-token error no longer mention agents waiting on you or agent tokens, and the sign-in screen speaks of reviewing what you and your coding agents make. Pull requests still show the coding agent they were made with. | 176 | {links.map((action) => ( |
| 177 | <Pressable | |
| 178 | key={action.id} | |
| 179 | accessibilityRole="link" | |
| 180 | onPress={() => action.href && open(action.href)} | |
| 181 | style={({ pressed }) => [styles.action, { backgroundColor: colors.bg, borderColor: colors.lineStrong, opacity: pressed ? 0.6 : 1 }]} | |
| 182 | > | |
| 183 | <Text size="small" style={{ fontWeight: "700", color: colors.fg }}> | |
| 184 | {action.label} | |
| 185 | </Text> | |
| 186 | </Pressable> | |
| 187 | ))} | |
| The phone app: Chat in full with phone notifications, plus Code and Customize (#4) | 188 | </View> |
| 189 | ) : null} | |
| 190 | </View> | |
| 191 | ); | |
| 192 | } | |
| 193 | ||
| 194 | /** Whether two messages run together: the same author, within five minutes, neither a card. */ | |
| 195 | export function runsOn(previous: ChatMessage | undefined, message: ChatMessage): boolean { | |
| 196 | if (!previous || previous.kind === "card" || message.kind === "card") return false; | |
| 197 | if (previous.author.id !== message.author.id || previous.author.kind !== message.author.kind) return false; | |
| 198 | return Date.parse(message.created_at) - Date.parse(previous.created_at) < 5 * 60_000; | |
| 199 | } | |
| 200 | ||
| 201 | const styles = StyleSheet.create({ | |
| 202 | row: { flexDirection: "row", gap: space.md, paddingHorizontal: space.lg, paddingBottom: 2 }, | |
| 203 | gutter: { width: 36 }, | |
| 204 | header: { flexDirection: "row", alignItems: "center", gap: space.sm }, | |
| 205 | reactions: { flexDirection: "row", flexWrap: "wrap", gap: 6, marginTop: 2 }, | |
| 206 | reaction: { flexDirection: "row", alignItems: "center", gap: 4, borderWidth: 1, borderRadius: radius.full, paddingHorizontal: 8, paddingVertical: 3 }, | |
| 207 | replies: { flexDirection: "row", alignItems: "center", gap: 6, marginTop: 2 }, | |
| 208 | status: { flexDirection: "row", alignItems: "center", gap: 4 }, | |
| 209 | card: { borderWidth: 1, borderRadius: radius.md, padding: space.md, gap: space.sm, marginTop: 2 }, | |
| 210 | fields: { flexDirection: "row", flexWrap: "wrap", gap: space.sm, justifyContent: "space-between" }, | |
| 211 | actions: { flexDirection: "row", flexWrap: "wrap", gap: space.sm }, | |
| 212 | action: { minHeight: 36, minWidth: 72, paddingHorizontal: space.md, borderRadius: radius.sm, borderWidth: 1, alignItems: "center", justifyContent: "center" }, | |
| 213 | }); |
This file's history is long; its oldest lines are credited to the oldest commit read.