You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* fix(birdy): reconnect the feed, follow lists, comments and join date (#15)
Six of the reported birdy bugs share one theme: the server work was already
done and the UI simply never reached it. This reconnects those paths rather
than building anything new.
Following tab was permanently empty
Feed.tsx branched on `feed === 'following'` and rendered a hardcoded empty
state that never consulted `posts`, so correctly-fetched posts were thrown
away. store.listFeed already implements the follows join, and Birdy.tsx
already requests it. Collapsed to a single length check with a
tab-dependent message. The dev stub returned [] for the following tab too,
which is presumably how the placeholder survived.
Followers / Following lists always blank
apiFollowList calls 'sd-phone:birdy:followList', which was never proxied in
client/apps/birdy.lua nor registered in server/birdy/init.lua - and no
store/action existed. The screen was fully built against a callback that
did not exist. Added store.followList (both directions, with the reciprocal
follows_you / is_following flags resolved in SQL), actions.followList, and
both registrations. Modelled on photogram, which has this same chain.
Comment button did nothing
The comment ActionButton was rendered with no onClick, and ActionButton
stopPropagation()s - so it also swallowed the card's own open handler,
making it the one spot on a post guaranteed to do nothing. It now opens the
post, where the reply composer already lives.
Tapping a commenter's avatar/name did nothing
The profile SlideOver was gated on `!openPostId`. Reaching comments always
means a post is open, so that gate suppressed it 100% of the time from
comments (it worked from the feed, where no post is open). Dropped that
clause; the SlideOver is z-30 over the post's z-20, so it layers correctly
and closing it returns to the comments.
New posts invisible until you switched tabs
actions.create only fired TriggerEvent, which is server-LOCAL (a hook for
third-party scripts) and reaches no players. There was no broadcast, no
polling, and no foreground refetch - and because AppDeck retains the app,
closing and reopening does not remount it, so nothing re-ran. Added a
TriggerClientEvent broadcast, a client relay, and a nonce the app bumps on
that push and on background->foreground (transition only, so opening the
app does not double-fetch).
Join date freely editable
join_label was a client-writable string that updateProfile accepted
verbatim, so anyone could set "January 1889". The real created_at column
existed but was never selected. It is now read back, the label is derived
from it in serializeProfile, updateProfile ignores the field, and the
editor shows it as read-only text. Legacy rows fall back to the stored
label.
Verified: tsc, eslint (0 errors) and vitest all clean. Not exercised against
a live server - no FiveM here - so the Lua paths are reasoned through rather
than run. Worth a smoke test of the follow lists and the cross-player feed
push before merge.
Still outstanding on #15, all genuinely unbuilt rather than miswired, and
each deserving its own PR: reposts (no table/action at all - the count is
hardcoded 0 server-side and the button only flips local state), private
accounts (the flag round-trips but no query or UI reads it), notification
badges (no seen/read column exists), post fan-out to followers, DM compose
(blocked by there being no handle->citizenid resolver), and avatar/banner
uploads (no columns, buttons are decorative).
* feat(birdy): persist reposts (#15)
Reposts were entirely client-side: no table, no action, no callback, and
serializePost pinned `reposts = 0` for every post. PostCard flipped a local
useState, so the count never moved for anyone else and the repost vanished
on the next refetch.
- phone_birdy_reposts, mirroring phone_birdy_likes (composite PK = idempotent).
- store add/remove/isReposted, plus repost_count and a viewer `reposted` flag
in POST_SELECT. That makes the viewer cid params #1 AND #2, so all seven
POST_SELECT call sites now pass it twice.
- actions.toggleRepost on the toggleLike model, notifying the post author on
a new repost (never on undo, never for self-reposts), registered through
withNotifyPush and proxied on the client.
- A 'repost' notification kind, rendered with the Repeat2 glyph.
- PostCard reads server truth; Birdy.tsx owns the optimistic flip like it
already does for likes.
- deleteAccount now clears reposts, both the account's own and others' on its
posts.
Typecheck and lint clean. Not exercised against a live server.
* feat(birdy): notification badges, post fan-out, private accounts, DM compose, avatars (#15)
Completes the remaining #15 items, all previously unbuilt.
Notification badges
phone_birdy_notifications gains a `seen` column (historic rows backfilled
as seen so nobody logs in to a giant badge) plus an unseen index. Opening
the Bell tab marks everything seen; a notificationCount callback feeds the
in-app Bell badge, birdy is registered in the springboard badge snapshot,
and SERVER_BADGE_APPS gains 'birdy' so the server owns the app-icon count.
The web app finally listens to the existing notification push instead of
letting it fire into the void.
Post fan-out
Posting now writes a 'post' notification for every follower and, for the
ones online, sends the app nudge, a phone banner, and a badge push - birdy
previously never sent a phone banner for anything.
Private accounts (the toggle now does what it says)
The All feed hides protected authors from non-followers in SQL, profile
post tabs return empty + protected for non-followers, follow lists of a
protected account are hidden, and the profile page shows a lock state.
Followers and the account itself see everything as before.
DM compose
dmSend accepts a handle (resolved server-side) and rejects self-DMs; a new
dmResolve callback maps handle -> conversation id so the UI can open a
thread with someone never messaged. Messages gains a compose button with a
people search, and other profiles gain a Message button.
Avatars and banners
avatar/banner VARCHAR(512) columns, validated with the photogram idiom
(http prefix, 512 cap, false clears). serializeAuthor/serializeProfile and
every profile-card query now carry them; the previously-dead camera
buttons in Edit Profile open the shared MediaPickerSheet; the Avatar
component (which already took src) now receives it across the app.
Typecheck, lint, tests all clean. Not exercised against a live server.
---------
Co-authored-by: Samuel Nicol <99494967+Github-Samuel@users.noreply.github.com>
0 commit comments