Repository navigation
refactor(llc, ui)!: route user updates through the generated client - #3052
Draft
VelikovPetar wants to merge 11 commits into
Draft
VelikovPetar wants to merge 11 commits into
VelikovPetar wants to merge 11 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
3 tasks done
…refactor/flu-953_user_updates_openapi_migration
…refactor/flu-953_user_updates_openapi_migration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…refactor/flu-953_user_updates_openapi_migration Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e user fields of their own Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…penapi_migration' into refactor/flu-953_user_updates_openapi_migration Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
…Id's v10 fallback Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…elds out of OwnUser Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Submit a pull request
Linear: FLU-953
Github Issue: -
CLA
Description of the pull request
Moves creating, updating and partially updating users onto the OpenAPI-generated client (plan group 20, User Updates, split out of group 09). As the first group to map a user, it also renames
User's andOwnUser'sextraDatatocustomand turns the values that lived in it into fields (README rule 9).User updates
StreamChatClient.updateUserandupdateUsersreturnResult<UpdateUsersResponse>instead of throwing.partialUpdateUserandpartialUpdateUsersare renamedupdateUserPartialandupdateUsersPartial, and returnResult<UpdateUsersResponse>;PartialUpdateUserRequestis renamedUpdateUserPartialRequest(freezed, notoJson).UpdateUsersResponsemoves tomodels/response/as a freezed class with a non-nullduration.updateUsersends only the user's name, image, language, visibility, custom data and, for an own user, privacy settings.UsersRepositorygains the two operations;users_mapper.dartgainsFullUserResponseMapper,UpdateUsersResponseMapperandUpdateUserPartialRequestMapper. The two methods are removed fromStreamChatApi.user.User custom data and fields
User.extraData→User.custom(also the constructor andcopyWitharguments).customis the API's custom data, unfiltered, in both directions;toJsonleaves out an entry named like one of the user's fields.User.nameandimageare nullable fields, stored as given.User.nameOrIdgives the name, or the id when the name is missing or empty, which is what v10'snamereturned.New and retyped fields:
Username,imageString?fields (were a non-nullnameand a nullableimagegetter)UsernameOrIdStringgetterUserdeactivatedAt,deletedAtDateTime?UsershadowBannedbool?UserblockedUserIdsList<String>(moved up fromOwnUser)OwnUser.fromUsercopies the user's fields instead of round-tripping through JSON.OwnUser.mergebuilds through the constructor: a nullable value the incoming user leaves out keeps the current one, exceptdeactivatedAt,deletedAtandshadowBanned, which are cleared.user.updated: the event's user is decoded as anOwnUser, so the privacy settings it carries for the current user reachcurrentUser, as in v10.ConnectUserDetails.toJsonno longer lets a custom entry overwrite a user field (a customidreplaced the real one).Persistence: users get
name,image,deactivated_at,deleted_at,shadow_banned,blocked_user_idscolumns andextra_databecomescustom; schema1100 + 7, so the cache is rebuilt once.UI: every place that names a user by text uses
nameOrId(mentions, labels, previews, a11y, translations in all locales).StreamUserAvatarshows its placeholder icon for a user with no name instead of initials taken from the id, and treats an empty image as none, as v10'simagedid. Filters and sorts read the rawname.DefaultApi.updateUsers(POST /api/v2/users)StreamChatClient.updateUser,updateUsersDefaultApi.updateUsersPartial(PATCH /api/v2/users)StreamChatClient.updateUserPartial,updateUsersPartialUpdateUsersResponseUpdateUsersResponse(ours, freezed)FullUserResponse,UserResponseUserUpdateUserPartialRequestUpdateUserPartialRequest(ours, freezed; wasPartialUpdateUserRequest)UserRequestUserbytoRequest()Why
The migration moves every endpoint onto the generated client, which returns a
Resultfor every call. The API calls the custom datacustom, and it types the user's name, image and the other values as fields; keeping them inextraDatalet a custom entry with the same name overwrite them, and a name that fell back to the id could not tell an unnamed user apart.Notes for reviewers
Important
FullUserResponsemaps toUser; there is no public full-user type. The generated response answers aFullUserResponseper user: the profile plus the fields only that user can see (devices, mutes, privacy settings, unread counts, …).UpdateUsersResponsekeeps v10'sMap<String, User>and drops that private part. A client-side token can only update its own user, whose private fields are oncurrentUser. Mapping the caller's own entry to anOwnUserstays an additive option onceMute,ChannelMuteandPrivacySettingshave response mappers.Important
User.namechanges meaning;nameOrIdis the old getter.User.nameStringgetter overextraData['name']; the id when the name is missing or''String?field;nullwhen the user has no nameUser.nameOrId'': the oldnameUser.imageextraData['image'];nullwhen missing or''String?fieldname:argument, orextraData: {'name': …}name:argument only;custom: {'name': …}is just custom dataEverything that used the old
nameto name a user by text now callsnameOrId, so the UI reads the same. Code that relied onnamenever being empty (string interpolation,.isNotEmptychecks) compiles unchanged and printsnullfor a user with no name.Important
Event.fromJsondecodes auser.updatedevent's user a second time, as anOwnUser.Userdoesn't modelprivacy_settings, which the current user's copy of the event carries; v10 kept them inextraDataandOwnUser.fromUserread them back through JSON. Re-decoding the user as anOwnUserkeeps them, so a privacy change still reachescurrentUser(verified live: the own copy carries the new settings, even after a privacy-only change). It is a temporary adapter, taggedTODO(openapi-migration)and listed in the README: when the WebSocket moves to v2, the generatedUserUpdatedEvent.user(UserResponsePrivacyFields) is mapped once and the handler applies only the fields that type carries.Decisions:
nameOrIdvsname:nameOrIdwherever a user is named by text; rawnamefor avatar initials, filters, sorts and mapping. A customTranslationsinterpolatinguser.namenow printsnullfor a user with no name; the guide says so.toJsonwrites every field. Persistence stores the current user asOwnUser.toJsonand reads it back withOwnUser.fromJson, so these fields can't useincludeToJson: falsethe wayChannelModelandMemberdo.User.topLevelFieldslists onlyUser's own keys. Keys only an own user carries are read byOwnUser; a key the SDK doesn't model stays in a v1 user'scustom.User/OwnUserkeep their hand-writtencopyWith. freezed can't generate one forOwnUserwhile it takesUser's fields through super-parameters; the group 09 plan records that the move must keepmerge's rules.UpdateUsersResponseandUpdateUserPartialRequesthad no v10copyWith, so no audit applies.updateChannelPartialandupdateMemberPartial. Hard renames, no deprecated aliases.updateUserno longer sendsrole,teamsorteamsRole. A client-side token never stored them.user.updatedre-decode inEvent.fromJsongoes when the WebSocket moves to v2 (README table).Left out on purpose:
OwnUserdoesn't exposeunread_count,total_unread_count_by_teamorlatest_hidden_channels, which the connection'smecarries. No event keeps them current (the per-team counts arrive on only some events and never after a mark read), so exposing them would ship values that are right only after connecting. A v1-decoded own user keeps them incustom, like any key the SDK doesn't model. Per-team unread counts and the hidden-channels list are a follow-up feature: FLU-1015.Known gaps, unchanged from v10: the
user.updatedhandler still emptiesblockedUserIds(the event always sends[]) and resetsshadowBannedandbanExpires, which the event doesn't carry.Testing
melos run analyzeclean;dart formatclean;generate_plan.py --checkreports 0 problems.stream_chat1,946 tests,stream_chat_persistence314,stream_chat_flutter_core385,stream_chat_localizations36, all green.stream_chat_flutter(GITHUB_ACTIONS=true) has the same 40 golden failures as the base.client_update_users_test.dart,client_update_users_partial_test.dart(sharedusers_fixtures.dart, every mapped field set to a non-default value, so a dropped or swapped field fails) andclient_user_updated_test.dart(privacy settings applied).user_test.dartandown_user_test.dartrewritten for the new fields,nameOrIdandmerge; persistence mapper tests extended for the new columns; avatar tests for a user with no name and for an empty image.nulls work;user.updatedcarries the new privacy settings after a privacy-only change, which doesn't moveupdated_at.Screenshots / Videos
Not applicable: no visible UI change beyond the avatar placeholder for a user with no name.
🤖 Generated with Claude Code