Skip to content

Mobile view passage overview rework - #738

Open
jeehoh wants to merge 57 commits into
developfrom
passage-overview-rework
Open

jeehoh wants to merge 57 commits into
developfrom
passage-overview-rework

Conversation

@jeehoh

@jeehoh jeehoh commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Major passage overview rework to match the design

Screenshots:

[Before]
before

[After]
after

jeehoh added 30 commits October 6, 2026 10:54
@jeehoh
jeehoh marked this pull request as ready for review October 12, 2026 19:34
@jeehoh
jeehoh requested a review from josephmyers October 12, 2026 19:35
@josephmyers

Copy link
Copy Markdown
Collaborator

Couple minor things:

  • Can we stack the Description directly under the Title. As it is, the Description appears to be at a set y-coord, accounting for two-line titles, which of course means for one-line titles there's an extra gap between them and the Description.
  • None of this was your change, but I think the "assignment" display is quite busy. Do we need to display both the avatar and the full username? I am interested to see the avatar only and also interested to see replacing the avatar with a grayscale person icon, as in the design. If you're okay experimenting...
  • Can we combine the Note's speech bubble icon into/as the graphic when there is no graphic chosen? I think showing both could be improved. It's just an idea; otherwise, I'd remove the speech bubble.
  • If there is no assignment, can we not show anything? Removing "Unassigned" will help with busyness.
  • As in the screenshot below, the card titles don't work well in Flat hierarchy. Should be an easy fix.
image

I'll start looking at the code itself now.

Comment thread web/src/components/Sheet/PassageCard.tsx Outdated
Comment thread web/src/components/Sheet/PassageCard.tsx Outdated
Comment thread web/src/components/Sheet/PassagePlayButton.tsx Outdated
Comment thread web/src/components/Sheet/PassagePlayButton.tsx
Comment thread web/src/components/Sheet/PassageCard.tsx Outdated
@jeehoh
jeehoh requested a review from josephmyers October 20, 2026 20:15

@josephmyers josephmyers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looking even better now. The code is clean enough that I was able to dive a little deeper into things, so I apologize for commenting on changes I've reviewed before. Some of these may involve a bit of discussion between us, so feel free to hit me up about those. Thanks

minWidth: isMobileWidth ? '100%' : 275,
maxWidth: 400,
boxSizing: 'border-box',
minHeight: isMobileWidth ? minPassageCardHeight : cardHeight,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

In my mind, this would be 1) height and not minHeight, 2) calculating the tallest card height. This would enforce uniform card height. I noticed differing card heights on mobile, and I was wondering if that was intentional.

Comment thread web/src/components/Sheet/PassageCardHeader.tsx
{title}
</Typography>
)}
{mediaId && <PassagePlayButton mediaId={mediaId} />}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this solution is good, but when the title wraps to a second line, the audio button gets put into the top right corner. It looks a little odd when the word that the line actually wraps on is a long word, because it creates a large gap between the title and audio button.

I think a good solution to this would be to 1) make the audio button part of the title line wrapping, i.e. a span, and 2) make the audio button and the last word a single, non-wrapping element to prevent the audio button being on a line by itself. See below:

  const tailIndex = [...new Intl.Segmenter().segment(title)].at(-1)?.index ?? 0;
  ...
          <Typography
            variant="h6"
            sx={{
              minWidth: 0,
              lineHeight: (theme) => theme.spacing(3.5),
              overflowWrap: 'anywhere',
            }}
          >
            {title.slice(0, tailIndex)}
            <Box component="span" sx={{ whiteSpace: 'nowrap' }}>
              {title.slice(tailIndex)}{' '}
              {mediaId && <PassagePlayButton mediaId={mediaId} />}
            </Box>
          </Typography>

Comment thread web/src/components/Sheet/PassageCard.tsx
<>
<Typography variant="body2" color="grey">
{comment || '\u00A0'}
{!isChapter && (

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This doesn't make a lot of sense to me, since chapters can't have descriptions. Why do we need to be explicit about that here? Maybe it would be a little more "closed for modification" if we said {description && (?

mediaId: string;
}

export function PassageSpeakerButton({ mediaId }: IProps) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This looks great. I do notice that it's not being used for book titles, movements, sections, etc. (this looks like a miss from the beginning). Should that be a separate task?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We'll make this a separate task.

mediaId: string;
}

export function PassagePlayButton({ mediaId }: IProps) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Although this component is certainly a step in the right direction, it would be more useful if it weren't tied/limited to loading the passage audio. It would be more reusable, e.g. by Discussions, if it were given the audio (URL, Blob, etc.).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We'll make this a separate task.

mediaId: string;
}

export function PassageSpeakerButton({ mediaId }: IProps) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm also thinking that the name could be improved, so as to not be confused with the "speaker" concept. Something like TitleAudioButton?

}

export function PassageSpeakerButton({ mediaId }: IProps) {
const { playing, loading, play, audio } = usePassagePlayback(mediaId);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If we want to reuse this, we'll have to change this line. It's strange to me that the "passage playback" is what you have to use for the chapters, since that won't return the right thing for the other card types.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We'll make this a separate task.

import { sharedSelector } from '../../selector';

// Only one passage plays at a time: starting one stops whichever was playing.
let stopActive: (() => void) | undefined;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Limiting the audio playing to one at a time in a central place is a good idea. My refactor will (and the prototype does) introduce the concept of an audio manager as the gateway for playing any audio, not just passages. This will be vital moving forward

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I am a little concerned about the duplication of effort between this new file and PassageDetailContext, which contains some of this already. Was it an intentional decision of yours to not use that, e.g. are you wanting to move away from that file?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We will revisit this for future refactor (wrt the audio manager); in the meantime I can investigate how PassageDetailContext works.

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.

2 participants