Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(site): implement notification ui#14175

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
BrunoQuaresma merged 34 commits intomainfrombq/user-notifications
Aug 9, 2024
Merged

Conversation

BrunoQuaresma
Copy link
Collaborator

@BrunoQuaresmaBrunoQuaresma commentedAug 5, 2024
edited
Loading

Deployment Notification Settings:

Screenshot 2024-08-06 at 10 32 40

User Notification Settings:

Screenshot 2024-08-06 at 10 34 06

To QA this locally you can run:

CODER_EXPERIMENTS=*,notifications ./scripts/develop.sh

@BrunoQuaresmaBrunoQuaresma self-assigned thisAug 5, 2024
@wiz-inc-56b28a72bd
Copy link

Wiz Scan Summary

IaC Misconfigurations0C0H0M0L0I
Vulnerabilities0C0H0M0L0I
Sensitive Data0C0H0M0L1I
Total0C0H0M0L1I
Secrets0🔑

@alwaysmeticulousalwaysmeticulous
Copy link

alwaysmeticulousbot commentedAug 5, 2024
edited
Loading

🤖 Meticulous spotted visual differences in 203 of 1313 screens tested:view and approve differences detected.

Last updated for commit7858a5a. This comment will update as new commits are pushed.

@BrunoQuaresmaBrunoQuaresma marked this pull request as ready for reviewAugust 6, 2024 13:32
@BrunoQuaresmaBrunoQuaresma requested review fromdannykopping,a team andjaaydenh and removed request fora teamAugust 6, 2024 13:34
Copy link
Contributor

@dannykoppingdannykopping left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I'm not too well-versed on the frontend so I won't comment on the code itself, but I did highlight a couple aspects for discussion.

BrunoQuaresma reacted with thumbs up emoji
@@ -187,6 +201,13 @@ const EventsView: FC<EventsViewProps> = ({
Webhook method is enabled, but the endpoint is not configured.
</Alert>
)}

{isUsingSmpt && !smtpConfig && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I don't think SMTP config can be blank.

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

From the types, it could be tho, but you shared this message a few minutes ago:

We can't assume SMTP will always be configured. We should have the same check here.

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Done!

Parkreiner
Parkreiner previously requested changesAug 8, 2024
Copy link
Member

@ParkreinerParkreiner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

A lot of this looks really good, but I've got some concerns about how the UI is architected right now, not just from a coding standpoint, but also a UI standpoint

Setting this to "Request changes" to be on the safe side, but if you think that these are acceptable, I can change my tune and flip the approval status

Comment on lines 35 to 42
Object.entries(data.template_disabled_map).map(
([id, disabled]) =>
({
id,
disabled,
updated_at: new Date().toISOString(),
}) as NotificationPreference,
),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

For this part, I think it'd be better to swapas forsatisfies. That way, you can't accidentally pass in too many properties, and you don't get runtime/compile-time drift

I also tried adding the type parameter to the.map callback, but that didn't raise any complaints about extra properties

BrunoQuaresma reacted with thumbs up emoji
Comment on lines +19 to +29
export const castNotificationMethod = (value: string) => {
if (notificationMethods.includes(value as NotificationMethod)) {
return value as NotificationMethod;
}

throw new Error(
`Invalid notification method: ${value}. Accepted values: ${notificationMethods.join(
", ",
)}`,
);
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Tagging this for now while I look through the other files. I don't fully understand the point of this function, and at the very least, I think that it should be redefined as a type predicate

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This is just easier to use IMO.

Type predicate:

constisNotificationMethod=(v:string):v isNotificationMethod=>{}// Usagevalues.map(v=>{if(!isNotificationMethod(v)){thrownewError("v is not valid")}return<Componentmethod={method}/>})

Cast function

constcastNotificationMethod=(v:string):NotificationMethod=>{}// Usagevalues.map(v=>{constmethod=castNotificationMethod(v)return<Componentmethod={method}/>})

endpoint: "https://example.com",
},
},
} as DeploymentValues,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Another case where I thinkas can be swapped forsatisfies

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

In this case,as is intentional. I want to be able to use partial data and not have to pass more than 100+ values.

Parkreiner reacted with thumbs up emoji
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yeah, sorry. Misread that

});
const ready =
templatesByGroup.data && dispatchMethods.data && deploymentValues;
const tab = searchParams.get("tab") || "events";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Do you think this might be a good use case foruseSearchParamsKey? It'd have to be changed a little bit to account for the|| instead of the??, though

Copy link
CollaboratorAuthor

@BrunoQuaresmaBrunoQuaresmaAug 8, 2024
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I don't think it would make too much difference since I'm using regukar links to update the search params. I'm using|| instead of?? to grab empty strings as well.

}) => {
return (
<Stack spacing={3}>
{Object.entries(templatesByGroup).map(([group, templates]) => (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Could this have a.sort tacked on to make sure there's no risk of the UI elements jumping around? Mainly worried that as the objects gets updated over time, the entries array could serialize the values in different orders across re-renders

BrunoQuaresma reacted with thumbs up emoji
Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Going to implement this in theselectTemplatesByGroup

value={value}
/>
</ListItem>
<Divider css={styles.divider} />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Not a big deal, but could this changed so that we check the array index and conditionally choose not to render out the divider if it's the last one? I know we have thelast-child styling, but it feels like it could be a bit more fragile over time

BrunoQuaresma reacted with thumbs up emoji
templatesByGroup,
}) => {
return (
<Stack spacing={3}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think the tables could use a little more spacing between them. They have the borders, so there's no real risk of them getting confused for each other, but they feel a little tense right now

BrunoQuaresma reacted with thumbs up emoji
{ready ? (
<Stack spacing={3}>
{Object.entries(templatesByGroup.data).map(([group, templates]) => {
const allDisabled = templates.some((tpl) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think this is a bit confusing from a user standpoint
Screenshot 2024-08-08 at 11 39 21 AM

When I see a parent switch that's off, I assume the whole group is off

I think I would expect this behavior:

  1. When the parent switch is off, all child switches are off
  2. When the parent switch is on, at least one child switch is on
  3. Switching the parent switch from on to off turns all child switches off
  4. Switching the parent switch from off to on turns all child switches on
  5. Switching a child switch to on will switch the parent to on if the parent isn't already on
  6. If a child switch is the only one that's on, and it's flipped off, the parent switch flips off, too

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I also think the current email icons are a bit misleading, because before I tried interacting with them, I expected them to be buttons. As in, clicking one would send off an email in some way

Not sure what a good change would be. Maybe something badge-like?

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Related to the first comment, I see how this can be confusing. I will put more thought into it. I just would rather do it in a different PR to avoid this one getting longer than it is.

Parkreiner reacted with thumbs up emoji
Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I also think the current email icons are a bit misleading, because before I tried interacting with them, I expected them to be buttons. As in, clicking one would send off an email in some way

I can try a badge and see how it looks like

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yeah, I can deal with a slightly confusing UI (or at least confusing from my point of view) as long as it works predictably

BrunoQuaresma reacted with thumbs up emoji
Comment on lines 123 to 129
onToggle={(checked) => {
const updated = { ...disabledPreferences.data };
for (const tpl of templates) {
updated[tpl.id] = !checked;
}
return updated;
}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I'm not sure what the best alternative is, but this feels like a code smell to me. Feels like it's breaking out of React's one-way data binding because even though there's shared state between the container and the individual switches, the parent container has little say in how the state is allowed to change. I don't think this is big enough of an edge case to justify breaking React conventions

My gut feeling is also that, just to reduce coupling, any handler likeonToggle should always be a void function

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I don't think this is big enough of an edge case to justify breaking React conventions

Is there a place where I can read about this convention?

My gut feeling is also that, just to reduce coupling, any handler like onToggle should always be a void function

I don't know if I agree with this... what are the pros and cons?

Eg. AnonChange handler returns an event and not a void. I think it is pretty common to have handlers returning data.

Copy link
Member

@ParkreinerParkreinerAug 9, 2024
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

To be clear,onChange and all the event handlersreceive an event as input, but they don't return anything. This is the type React has:

typeReact.EventHandler<EextendsSyntheticEvent<any>>=(event:E)=>void;

In real HTML, you can have the event handlers return booleans to affect event bubbling behavior, but React shaves that off for the synthetic events.

Like, if I have a component like this:

functionMyComponent(){const[color,setColor]=useState("blue");return<buttononClick={()=>setColor("red"}>Click me</button>;}

I don't think anyone would expect the button to have any way to affectMyComponent's state, beyond theonClick. But even then,MyComponent is in full control of the event handler definition – it fully prescribes how its own state is allowed to change.MyComponent has a dependency onbutton, but everything related to its state is self-contained in the one component.button doesn't have direct access to the state setter, so all it can do is follow the very restricted set of instructions it's given

But with the switches, the main state is being split across four different places: the RQ cache living outside the UI tree, the parent page component, the individual switches, and the mutation key factory function. It felt like I had to jump through a lot of hoops (and two different files) to figure out exactly how the state was changing

I don't think there's a way to fully consolidate the state in one place, just because we do need RQ, but four places for one main piece of state feels like a bit much to me

My preference would be to have the switches be "dumb" and just call an event handler when clicked. Then put all the state related to the functionality inside the parent page component

BrunoQuaresma reacted with thumbs up emoji
Copy link
Member

@ParkreinerParkreinerAug 9, 2024
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

But as far as conventions around one-way data-binding/unidirectional data flow, I tried looking on React.dev, and I'm surprised there wasn't too much dedicated information on it. There'sThinking in React, particularly parts 4 and 5, but I think it's the sort of thing that's not explicitly shouted out, even though it informs pretty much every lesson on the site

The main reason I know about all this is because I just happen to know that React was designed around the flux architecture pattern.Facebook has an archive page describing what's it's about

BrunoQuaresma reacted with thumbs up emoji
Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think I better understand your concerns now. Thanks for explaining them to me. I will fix it.

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Done!

@github-actionsGitHub Actions
Copy link

github-actionsbot commentedAug 8, 2024
edited
Loading


✔️ PR 14175 Updated successfully.
🚀 Access the credentialshere.

cc:@BrunoQuaresma

github-actions[bot] reacted with rocket emoji

@BrunoQuaresmaBrunoQuaresma changed the titlefeat(site): add notifications uifeat(site): implement notification uiAug 8, 2024
@stirby
Copy link
Collaborator

The UI feels great. The profile settings page is perfect.

Why do we allow administrators to select webhook delivery methods if no endpoint is configured? Can we prevent them from selecting it?

Screenshot 2024-08-08 at 4 01 51 PM

@BrunoQuaresma
Copy link
CollaboratorAuthor

Why do we allow administrators to select webhook delivery methods if no endpoint is configured? Can we prevent them from selecting it?

I've been thinking about that too. I'm wondering if we could validate this on the dispatch-methods endpoint by only returning the methods that would work, or by not allowing Coder Server to start without the correct notification configurations. What do you think,@dannykopping?

@dannykopping
Copy link
Contributor

I'm wondering if we could validate this on the dispatch-methods endpoint by only returning the methods that would work

I'm not sure we want to add this complexity. What you have currently is the best way IMHO; this prompts folks to configure their server correctly if things are not correct.

or by not allowing Coder Server to start without the correct notification configurations

No, because that would require everyone to always set webhook endpoints (we use a default value for SMTP smarthosts but may drop that ahead of the release).

BrunoQuaresma and stirby reacted with thumbs up emoji

Copy link
Contributor

@dannykoppingdannykopping left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

LGTM, one nit and a suggestion but I don't need to review again.

Comment on lines 160 to 162
<SidebarNavSubItem href="notifications">
Notifications
</SidebarNavSubItem>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Bruno, you're so good at your job. This was the first thing I thought to check, knowing how easy it would be to miss adding it here. 😄 Should it be behind the same experiment check as in the other sidebar tho?

BrunoQuaresma reacted with heart emoji
Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Thanks@aslilac ❤️. Yes, it definitely needs to be under the experiment.

@BrunoQuaresmaBrunoQuaresma dismissedParkreiner’sstale reviewAugust 9, 2024 16:14

I've applied the required changes and received approval from another engineer

Copy link
Member

@ParkreinerParkreiner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Looks good! Thanks for putting up with all my nagging

BrunoQuaresma reacted with heart emoji
endpoint: "https://example.com",
},
},
} as DeploymentValues,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yeah, sorry. Misread that

@BrunoQuaresmaBrunoQuaresma merged commit21942af intomainAug 9, 2024
31 of 32 checks passed
@BrunoQuaresmaBrunoQuaresma deleted the bq/user-notifications branchAugust 9, 2024 16:43
@github-actionsgithub-actionsbot locked and limited conversation to collaboratorsAug 9, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account?Sign in.
Reviewers

@dannykoppingdannykoppingdannykopping approved these changes

@aslilacaslilacaslilac left review comments

@ParkreinerParkreinerParkreiner approved these changes

@jaaydenhjaaydenhAwaiting requested review from jaaydenhjaaydenh was automatically assigned from coder/ts

@stirbystirbyAwaiting requested review from stirby

@johnstcnjohnstcnAwaiting requested review from johnstcn

Assignees

@BrunoQuaresmaBrunoQuaresma

Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

6 participants
@BrunoQuaresma@stirby@dannykopping@aslilac@johnstcn@Parkreiner

[8]ページ先頭

©2009-2025 Movatter.jp