Conversation
robennals
left a comment
There was a problem hiding this comment.
Here are some comments.
Things mostly look sensible. The main issue is that we need to change the server API so that names and verification status are secure and can't just get updated by untrusted client code.
| <Pad size={8} /> | ||
| <RadioGroup value={nameMode} onChange={onChangeNameMode}> | ||
| <RadioOption radioKey='full' text={fbUser.displayName} /> | ||
| {personaPreview.verificationStatus === 'unverified' && <View style={{backgroundColor: colorLightBlueBackground, borderRadius: 8}}> |
There was a problem hiding this comment.
As a minor readability thing, can we push the verification status into it's own component?
There was a problem hiding this comment.
as discussed, will do this later.
| <Pad /> | ||
| <AddVerifiedUser onUsersAdded={() => setRefreshKey(refreshKey + 1)}/> | ||
| <Pad /> | ||
| <Heading level={3} label='Already Verified Users' /> |
There was a problem hiding this comment.
Do we also want the ability to remove verified users, in case the admin made a mistake?
There was a problem hiding this comment.
as discussed, will do this later.
| <Pad /> | ||
| <Heading level={3} label='Already Verified Users' /> | ||
| <VerifiedUserList verifiedUsers={verifiedUsers} /> | ||
| </ConversationScreen> |
There was a problem hiding this comment.
Should this screen also show a list of recent people who have triggered celebrity detection but weren't marked as verified?
There was a problem hiding this comment.
as discussed, will do this later.
| 'instance', personaKey, 'global', 'preview']); | ||
| const fbUser = datastore.getFirebaseUser(); | ||
| if (!personaPreview?.name) { | ||
| const isCelebrity = await datastore.callServerAsync( |
There was a problem hiding this comment.
I think we probably actually need to do the celebrity check as part of getOrCreateUser whenever a user is created, as well as in whatever server side call updates the name.
A user who is banging the server API could potentially bypass the "set up your profile" screen, causing their name to default to whatever the SSO provider set.
There was a problem hiding this comment.
as discussed, will do this later.
client/structure/login.js
Outdated
| const isCelebrity = await datastore.callServerAsync( | ||
| 'verify', 'getIsCelebrity', {name: fbUser.displayName}); | ||
| if (isCelebrity) { | ||
| await datastore.callServerAsync('verify', 'setVerficationStatusOnPersona', {email: fbUser.email}) |
There was a problem hiding this comment.
Email passed from the client isn't secure. Better for the server side to grab the email of the account.
server/component/verify.js
Outdated
| const encodedEmail = stringToFbKey(email.toLowerCase()); | ||
| const verificationStatus = verifiedUsers.includes(encodedEmail) ? "verified" : "unverified"; | ||
|
|
||
| const userRecord = await getUserByEmailAsync(email); |
There was a problem hiding this comment.
Rather that setting these fields directly, we should probably call updateProfileAsync.
That also does the work up updating the previews on all the instances, which this seems to be missing.
server/component/verify.js
Outdated
| const oldPreview = await serverstore.getRemoteGlobalAsync({...path, key: 'preview', }); | ||
| serverstore.setRemoteGlobal( | ||
| {...path, key: 'preview', value: { ...oldPreview, verificationStatus}}); | ||
| serverstore.updateRemoteObject( |
There was a problem hiding this comment.
You also need to update the preview in all the instances that the user has participated in.
But best to do that by calling updateProfileAsync rather than duplicating here.
| exports.getIsCelebrityAsync = getIsCelebrityAsync; | ||
|
|
||
| async function setVerficationStatusOnPersonaAsync({serverstore, email}) { | ||
| const verifiedUsers = await serverstore.getModulePrivateAsync('verify', ['verifiedUsers']) || []; |
There was a problem hiding this comment.
We should also make updateProfileAsync no longer accessible by the client, since that would allow an untrusted client to change the verification status of a user. Let's make a change to drop that method and just have a dedicated method for each field we care about, where all of those server methods do whatever check is needed, and either return an error message (to show to the user) or call updateProfileAsync (which would now be an internal-only method).
There was a problem hiding this comment.
as discussed, will do this later.
| @@ -0,0 +1,4 @@ | |||
| Is "{{name}}" commonly recongized as a celebrity, even a minor one, in any country? | |||
There was a problem hiding this comment.
Not blocking, but we should have an email dataset for this prompt.
There was a problem hiding this comment.
as discussed, will do this later.
| function getUserByEmailAsync(email) { | ||
| return admin.auth().getUserByEmail(email); | ||
| } | ||
|
|
There was a problem hiding this comment.
We probably also need to modify addCurrentUser from datastore.js so that can't just set fake instance data. That shouldn't be hard because we already call linkInstance - so the code can go in there. We just need to replace setObject with setLocalObjectView (doesn't exist yet), and update the database access rules in psi-product
There was a problem hiding this comment.
as discussed, will do this later.
robennals
left a comment
There was a problem hiding this comment.
Mostly looking good, but a few issues:
- It seems that the badge is always showing the verification status for the logged in user, not the user whose profile photo is being shown. Unit tests should also cover this case.
- Can you use profilePhotoLayers rather than modifying people.js
- Can you put the verification badge into a feature module. This will better isolate code, and make it easier for us to play around with different UI options.
client/component/people.js
Outdated
|
|
||
| return <View style={{ width: size, height: size, borderRadius: size / 2, backgroundColor: color, justifyContent: 'center', alignItems: 'center' }}> | ||
| <Text style={[{ fontSize: size / 2 }, s.letter]}>{letter}</Text> | ||
| <View style={{ position: 'absolute', right: -badgePad, bottom: 0 }}> |
There was a problem hiding this comment.
Is there a reason you aren't doing this with profilePhotoLayers?
That config slot was added specifically to allow enhancements like this.
client/component/people.js
Outdated
| }, | ||
| }); | ||
|
|
||
| function Badge({type, name}) { |
There was a problem hiding this comment.
Can we put this in a feature module, rather than in the core "people" component?
You can use profilePhotoLayers to add the badge.
| personaPreview: {name:'Tom Cruise', verificationStatus: 'verified'}, | ||
| stories: [ | ||
| {label: "Don't show message for verified name", actions: [ | ||
| CHECK_TEXT_ABSENCE(verificationMessage) |
There was a problem hiding this comment.
We can keep this if you like, but in general I prefer to rely on the storybook snapshots to check that the right stuff is shown. Otherwise one can quickly end up with a lot of checks.
There was a problem hiding this comment.
Agree there's the possibility for lots of checks, but I can be disciplined about not doing that. So far I've had trouble using the storybook snapshots effectively-- its often many hundreds of lines of html output to sift through and its not trivial to distinguish which diffs are accidental or on purpose. I feel a lot better just having an explicit check for the things that matter.
client/component/people.js
Outdated
| const datastore = useDatastore(); | ||
| const fbUser = datastore.getFirebaseUser(); | ||
| const personaPreview = usePersonaPreview(); | ||
| const verificationStatus = personaPreview?.verificationStatus ?? null; |
There was a problem hiding this comment.
As written, it seems the badge will always be showing the verification status of the user who is logged in. Don't we actually want it to be showing the verification status of the user who we are showing a profile photo for?
There was a problem hiding this comment.
thanks for catching this. fixed.
client/component/people.js
Outdated
| <IconCircleCheck /> | ||
| </View>} | ||
| <View style={{ position: 'absolute', right: -badgePad, bottom: 0 }}> | ||
| <Badge type={type} name={name} /> |
There was a problem hiding this comment.
This call isn't saying what user the badge should be shown for. The badge should be the status of the user whose face is being shown, not of the logged in user.
client/component/people.js
Outdated
| return null; | ||
| } | ||
|
|
||
| if (verificationStatus === 'verified') { |
There was a problem hiding this comment.
Note that the verification status is the status of their name, so we only want to show the verification badge if we are also showing their real name, rather than a pseudonym.
Right now the photo editing UI doesn't record whether the 'name' is a pseudonym. We could either have a field to check that, or we could look at the text and see if it contains any uppercase letters or spaces.
There was a problem hiding this comment.
i was solving for this case by checking (fbUser?.displayName !== persona?.name), assuming if the name is different from the firebase user name then they've set a psuedonym. does that not work?
There was a problem hiding this comment.
fbUser is the fbUser record of the logged in user. But we are showing the badge for a different user. So this check won't work.
client/demo/designsystem-demo.js
Outdated
| </Datastore> | ||
| </DemoSection> | ||
| <DemoSection label='Verified Profile Photo'> | ||
| <Datastore personaPreview={{ verificationStatus: 'verified' }}> |
There was a problem hiding this comment.
This seems to only be testing the case where we show the badge of the current logged in user for an instance that they aren't attached to.
We should test that use case, but the most important use case is the case where you are seeing the profile photo for a different user - and in that case, their persona info will be in the persona collection.
client/component/people.js
Outdated
| if (profilePhotoLayers?.length > 0) { | ||
| return <> | ||
| <FaceImage face={face} photoUrl={photo ?? persona?.photoUrl} type={type} border={border} faint={faint} check={check} /> | ||
| <FaceImage face={face} photoUrl={photo ?? persona?.photoUrl} type={type} border={border} faint={faint} /> |
There was a problem hiding this comment.
You've added a "name" property to FaceImage, but aren't passing it in these cases. Is that intentional?
There was a problem hiding this comment.
added the name so i could do the pseudonym check. i'm now just getting the name from the persona object so not passing this anymore.
client/component/people.js
Outdated
| </> | ||
| } else { | ||
| return <FaceImage face={face} photoUrl={photo ?? persona?.photoUrl} type={type} border={border} faint={faint} check={check} /> | ||
| return <FaceImage face={face} photoUrl={photo ?? persona?.photoUrl} type={type} border={border} faint={faint} /> |
There was a problem hiding this comment.
This reminds me that we probably need to show profilePhotoLayers for LetterFace and AnonymousFace (which I don't think we are actually using right now) too - since a person with a celebrity name may not be showing a profile photo.
If we do that, then we probably want to turn the photo layers into a component that can easily wrap any photo, so we can easily re-use the same code in all these locations - and also in the login screen.
I am still having a couple small issues, but I thought I'd just get this out at a reasonable stopping point so I can move on to the higher priority things. Some outstanding issues:
|
robennals
left a comment
There was a problem hiding this comment.
Making progress, but it seems this still has a few issues:
- The name check to see if this is a real name or pseduonym is comparing to the name of the logged in user, not the name of the user whose badge we are showing
- This is being done with the PhotoAndName module, which is only supposed to be applied to the profile structure. The verification badge should be it's own module.
client/component/people.js
Outdated
| return null; | ||
| } | ||
|
|
||
| if (verificationStatus === 'verified') { |
There was a problem hiding this comment.
fbUser is the fbUser record of the logged in user. But we are showing the badge for a different user. So this check won't work.
| return <FaceImage face={face} photoUrl={photo ?? persona?.photoUrl} type={type} border={border} faint={faint} check={check} /> | ||
| } | ||
| if (face || photo || persona?.photoUrl) { | ||
| return <ProfileWithLayers faceComponent={FaceImage} face={face} persona={persona} |
There was a problem hiding this comment.
Not a blocker, but I'd have implemented this using {children} rather that faceComponent.
E.g.
<ProfileOverlay personaKey={personaKey} type={type}>
<FaceImage ... />
</ProfileOverlay>
| import { CLICK, DemoSection, POPUP, SpacedArray } from "../system/demo"; | ||
| import { Image, View } from "react-native"; | ||
| import { FeatureMenuScreen } from "./featuremenu-demo"; | ||
| import { VerificationBadge } from "../feature/ProfilePhotoAndName"; |
There was a problem hiding this comment.
Not a blocker, but this feels like a separate feature from ProfilePhotoAndName - which is about giving you the ability to edit your profile photo and name from a profile. Might be better in a separate module file.
| } | ||
| } | ||
|
|
||
| export function VerificationBadge({persona, size}) { |
There was a problem hiding this comment.
Not a blocker, but as a general principle, it's usually better to pass a key (e.g. personaKey) rather than an object (e.g. persona).
When doing react re-renders, keys are strings and are compared by value, while objects are compared by reference. Thus using keys makes it easier to avoid unwanted re-renders.
That said, this is somewhat moot right now because all data access hooks currently refresh all components for every data change - though I'm planning to do that nicer in the future.
|
|
||
| export function VerificationBadge({persona, size}) { | ||
| const datastore = useDatastore(); | ||
| const fbUser = datastore.getFirebaseUser(); |
There was a problem hiding this comment.
getFirebaseUser is getting the user record for the logged in user, not the user the badge is being shown for.
| const fbUser = datastore.getFirebaseUser(); | ||
| const verificationStatus = persona?.verificationStatus ?? null; | ||
|
|
||
| if (fbUser?.displayName !== persona?.name) { |
There was a problem hiding this comment.
This will always be true, except for the badge of the currently logged in user. I don't think that's what you want.
I think you probably want to check if the name starts with a capital letter - and maybe also enforce that for real names in the setup screen.
| makePreview: previewPhotoAndName, | ||
| }] | ||
| }], | ||
| profilePhotoLayers: [VerificationBadge] |
There was a problem hiding this comment.
I think this should be a different feature.
ProfilePhotoAndName is a feature applied to profiles, that makes the profile names and photos editable.
The feature you are writing is applied to conversations, and shows a badge on people's photos.
These should probably be different features.
We currently don't have a feature list what structures they apply to, since that would get awkward features that add functionality to things like bylines that apply to multiple structures, but it's possible it would make sense to have some kind of convention for documenting what we think a feature is supposed to be applied to.
| defaultConfig: { | ||
| profileWidgets: [], | ||
| profileModules: [], | ||
| profilePhotoLayers: [VerificationBadge], |
There was a problem hiding this comment.
Why is this here? Wouldn't we just turn on the verification feature on profiles by default, rather than having this hard-coded into the structure?
We do need to add profilePhotoLayers to the structure, so it's accessible as a config slot, but I think it should default to empty.
DO NOT SUBMIT - just looking for feedback
demo video: https://drive.google.com/file/d/1n8r3pTqSvKp34a0Umk-FbQMLOatM3S6I/view?usp=sharing
Adds admin screen to verify users
Does gpt check of first time login users to determine if they're a celebrity
If they are a celebrity, checks verified users list to determine whether to verify
Adds verification_status to persona and persona preview
Need feedback:
This is my first time managing data across instances: