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

Appearance settings

[Aichat] Teacher view of student chat history - UI updates - #60483

#60483
Merged
fisher-alice merged 9 commits into
stagingcode-dot-org/code-dot-org:stagingfrom
alice/student-chat-history-part1code-dot-org/code-dot-org:alice/student-chat-history-part1Copy head branch name to clipboard
Aug 21, 2024
Merged

[Aichat] Teacher view of student chat history - UI updates#60483
fisher-alice merged 9 commits into
stagingcode-dot-org/code-dot-org:stagingfrom
alice/student-chat-history-part1code-dot-org/code-dot-org:alice/student-chat-history-part1Copy head branch name to clipboard

Conversation

@fisher-alice

@fisher-alice fisher-alice commented Aug 16, 2024

Copy link
Copy Markdown
Contributor

This PR makes UI updates to the teacher view of student chat history in aichat levels which includes:
-ability to view/hide user messages marked as profane

  • removes x (delete button) from notifications and model updates
  • Adds auto-scrolling to the most recent message when in teacher view of student chat history
  • Notifications such as 'The user cleared the chat workspace.' and 'The user loaded the aichat level.' use Alert information UI (blue)

Note that I checked with @markabarrett about these UI updates in this Slack thread.

I also included updating the prop showWaitingAnimation to be a boolean to address #60322 (comment).

The auto-scrolling to the most recent chat event was quite tricky. Thanks to @sanchitmalhotra126 for helping me figure out that the parent of the student chat history div (_TabPanel div) requires display: flex and height: 100% in order for the computed scroll height to have effect. Without height: 100%, the parent div was set to the height of the entire chat history. I don't think I fully understand why display: flex is necessary - I welcome thoughts on this. 😄
display: flex is needed since the child component (scrollable container) has flex-grow: 1 but this doesn't have any effect unless the parent (tab panel) has display: flex. (Thanks Sanchit!)

The other update I needed to include was a condition on which tab was active. If I didn't include the condition that the style above was only when the student chat history tab was active, then the chat history was in view even when 'Test student model' tab was active. Initially, I thought I needed to add additional classNames for the Tabs component for the active/hidden tabs, but thankfully can just update style according to which tab is active.
Based on #60483 (comment) - updated style for the tab panels container so that for the active panel, display is set to flex and for the hidden panel, display is set to none.

Screencast video:

Screen.Recording.2024-08-19.at.10.26.07.AM.mov

Links

jra

Testing story

I tested locally in aichat levels in /allthethings with a teacher account that has a section with 3 students.

Deployment strategy

Follow-up work

Remove experiment flag after testing on production.

Privacy

Security

Caching

PR Checklist:

  • Tests provide adequate coverage
  • Privacy and Security impacts have been assessed
  • Code is well-commented
  • New features are translatable or updates will not break translations
  • Relevant documentation has been added or updated
  • User impact is well-understood and desirable
  • Pull Request is labeled appropriately
  • Follow-up work items (including potential tech debt) are tracked and linked

interface ChatEventsListProps {
events: ChatEvent[];
showWaitingAnimation: () => React.ReactNode;
showWaitingAnimation: boolean;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@sanchitmalhotra126 - addressing comment from #60322 (comment).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

FYI - refactored so prop no longer needed based on #60483 (comment).

@fisher-alice
fisher-alice marked this pull request as ready for review August 16, 2024 22:16
@fisher-alice
fisher-alice requested a review from a team August 16, 2024 22:16
Comment thread apps/src/aiComponentLibrary/chatMessage/ChatMessage.tsx

@thomasoniii thomasoniii left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minor syntactic suggestion, but otherwise lgtm 🚀

@fisher-alice fisher-alice changed the title [Aichat] Teacher view of student chat history - part 1 [Aichat] Teacher view of student chat history Aug 19, 2024
@fisher-alice

Copy link
Copy Markdown
Contributor Author

Hi @thomasoniii - I re-requested your review bc I added a substantive commit that allows auto-scrolling since you approved. thanks!

Comment on lines +86 to +92
if (
profaneMessageViewToggle === ProfaneMessageViewToggle.VIEW
) {
setProfaneMessageViewToggle(ProfaneMessageViewToggle.HIDE);
} else {
setProfaneMessageViewToggle(ProfaneMessageViewToggle.VIEW);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I must've missed commenting on this...another suggestion as opposed to a necessity, but this is the sort of thing I'd tend to put in a ternary:

setProfaneMessageViewToggle(
  profaneMessageViewToggle  === ProfaneMessageViewToggle.VIEW
    ? ProfaneMessageViewToggle.HIDE
    : ProfaneMessageViewToggle.HIDE
);

Main benefit is to not have the function call in two places.

And, further waxing philosophical as opposed to making a serious suggestion here, state machines can work great for this sort of thing.

// defined..somewhere
const nextMessageState = {
  ProfaneMessageViewToggle.HIDE : ProfaneMessageViewToggle.VIEW,
  ProfaneMessageViewToggle.VIEW : ProfaneMessageViewToggle.HIDE,
}

// and then in the onClick
  onClick = { () => setProfaneMessageViewToggle( nextMessageState[profaneMessageViewToggle] ) }

Since we've only got two states I think sticking with the ternary is smart, this strategy is really most useful for when there are more states. I just figured I'd jot down the concept somewhere. 😄

@fisher-alice fisher-alice Aug 19, 2024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh cool (the nextMessageState idea)! Great for future reference.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will update to ternary.

/>
))}
{showWaitingAnimation()}
{isWaitingForChatResponse && displayWaitingAnimation()}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm sorry, I must have zipped past this too fast last time - isWaitingForChatResponse is redundant here.

Don't get me wrong - it's okay as is, but I'm gonna forge ahead with my suggestion here. isWaitingForChatResponse is already baked into the callback for displayWaitingAnimation. The function would return an undefined if it's false already, so the logical wrapper doesn't add to it.

I'm pretty sure the displayWaitingAnimation function would need an else { return null } to make react happy, but otherwise it'd be fine. Then it could just be { displayWaitingAnimation() }

Taking it a step further, I can see a couple of different ways to refine it further.

  • instead of a useCallback, it could be useMemo and skip the function call during the render, just embedding { displayWaitingAnimation }
  • I might not write this as a hook at all - I think I'd prefer to have it as a regular function (defined outside the component) that takes a boolean arg (shouldDisplay?), and then it'd be called as { displayWaitingAnimation( isWaitingForChatResponse ) }. That way it's a little more generic (but I doubt this'd ever be re-used externally) and it makes it a little more clear that it depends upon the isWaitingForChatReponse variable. Yes, I'm aware that my earlier comment refactored away the conditional that explicitly had that variable. Refactoring has a lot of backtracking 😅
  • Last thing, taking that function-with-arg a step further, it could be pulled out into a separate component (either inline or external). And then maybe it'd be called as <WaitingAnimation shouldDisplay={isWaitingForChatResponse} /> (where this component would basically look exactly like the function). Just to put a component in the display instead of a variable. I tend to skew towards components over inline functions/variables where possible.

Again, not that any of this is necessary, just more musing outloud to see if anything looks preferable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Making an inline component makes sense to me. I really appreciate how you outlined your thought process here. Thanks!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oooo - now I can remove unnec showWaitingAnimation prop. Thanks again @thomasoniii !

@thomasoniii thomasoniii left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

still lgtm 🚀

(though I did add a few more non-blocking suggestions, since I clearly paid more attention in my second pass 😅 )

@sanchitmalhotra126 sanchitmalhotra126 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good overall! Some minor comments/questions

Comment thread apps/src/aiComponentLibrary/chatMessage/ChatMessage.tsx Outdated
Comment thread apps/src/aiComponentLibrary/chatMessage/ChatMessage.tsx Outdated

.userProfaneMessageButton {
width: 120px;
margin-top: -10px;

@sanchitmalhotra126 sanchitmalhotra126 Aug 20, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What's this needed for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The button that hides/shows the profane user message.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh yep, was wondering specifically why we needed the negative margin? Is it to cut into the chat message margin?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

oh yeah - so that the button was a little closer to its associate chat message.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah interesting - I don't think the chat message itself has any extra margin, so I'm wondering if it might be better to nest the button inside the container div and add a bit of padding/margin/gap there instead? I'm just thinking that the -10px makes some assumptions about how chat messages are spaced out which may potentially differ from app to app?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah - I tried that at first, but then the button and message were set in a row.
Screenshot 2024-08-21 at 11 10 06 AM

So then I tried updating flex-direction to column, but then it messed with the user chat message styling (should be aligned to the right but after style update the message takes up entire width).
Screenshot 2024-08-21 at 11 11 11 AM

Here's what it should look like:
Screenshot 2024-08-21 at 11 12 02 AM

I think I'll leave as-is for now and we can address in a follow-up if you have ideas to resolve this. Thanks!

Comment thread apps/src/aichat/views/ChatEventView.tsx
Comment thread apps/src/aichat/views/ChatEventView.tsx Outdated
Comment on lines -48 to +52
{showWaitingAnimation()}
<WaitingAnimation shouldDisplay={isWaitingForChatResponse} />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nice!

}

.tabPanels {
.tabPanelsChatWithModelActive {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why does this class not need the display: flex and height: 100% in its inner div?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ahh...when I included

  > div {
    display: flex;
    height: 100%;
  }

The hidden attribute was overridden and the content from both tab panels were displayed.

Screen.Recording.2024-08-20.at.3.34.51.PM.mov

But I overlooked that now when the 'Test student model' tab is active, auto-scrolling no longer works.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We chatted offline and now using the attribute selector hidden to address this. Will updated - thanks so much!

@sanchitmalhotra126 sanchitmalhotra126 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

One overall note (discussed offline) - as other AI features adopt this component, we may want to pull out some of the logic from this component (i.e. determining what text to display) and keep it more presentational. For now thought I think it makes sense to keep it all in here since AI Chat is the only consumer of this component and we can always refactor later.

Comment thread apps/src/aichat/views/ChatEventView.tsx Outdated
@fisher-alice
fisher-alice merged commit 29b2db8 into staging Aug 21, 2024
@fisher-alice
fisher-alice deleted the alice/student-chat-history-part1 branch August 21, 2024 18:04
@fisher-alice fisher-alice changed the title [Aichat] Teacher view of student chat history [Aichat] Teacher view of student chat history - UI updates Aug 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.