[UEPR-553] Front-end blocking in scratch-www#10189
Conversation
There was a problem hiding this comment.
Pull request overview
Introduces a READ_ONLY_MODE feature flag to disable interactive/editing operations across Studio pages and the Project Detail Page (preview), to support front-end blocking for UEPR-553.
Changes:
- Add
READ_ONLY_MODEflag (env-driven) and thread it through Studio + Preview views to disable/hide interactive controls. - Add “disabled” styling helpers and new disabled color tokens used by Studio UI elements.
- Prevent certain PDP interactions (love/favorite/share/add-to-studio) and gate editor capabilities when in read-only mode.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/views/studio/studio.scss | Adds shared “disabled” styles and thumbnail-edit disabled styles for studios. |
| src/views/studio/studio-title.jsx | Disables title textarea and applies disabled styling in read-only mode. |
| src/views/studio/studio-project-tile.jsx | Hides project tile overflow actions in read-only mode. |
| src/views/studio/studio-project-adder.jsx | Disables project add/browse controls in read-only mode. |
| src/views/studio/studio-member-tile.jsx | Hides member management overflow actions in read-only mode. |
| src/views/studio/studio-image.jsx | Disables studio thumbnail upload and applies disabled styling in read-only mode. |
| src/views/studio/studio-follow.jsx | Disables follow/unfollow in read-only mode. |
| src/views/studio/studio-description.jsx | Disables description textarea and applies disabled styling in read-only mode. |
| src/views/studio/studio-curator-inviter.jsx | Disables curator invite form in read-only mode. |
| src/views/studio/studio-curator-invite.jsx | Adds disabled styling for invite acceptance button (but currently doesn’t fully disable behavior). |
| src/views/preview/project-view.jsx | Blocks love/favorite toggles and gates editor capabilities based on read-only/shared state. |
| src/views/preview/presentation.jsx | Suppresses “share” banner in read-only mode and passes readOnlyMode to child components. |
| src/lib/feature-flags.js | Adds READ_ONLY_MODE flag sourced from process.env.READ_ONLY_MODE. |
| src/_colors.scss | Adds disabled UI color tokens used by new styling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| disabled={submitting} | ||
| onClick={() => { |
There was a problem hiding this comment.
The accept-invite button is still clickable in read-only mode: you add the studio-disabled class when READ_ONLY_MODE is true, but disabled is only driven by submitting. This still allows accepting curator invites while the UI looks disabled. Include READ_ONLY_MODE in the disabled condition (and/or early-return in the click handler) so the action cannot fire in read-only mode.
| disabled={submitting} | |
| onClick={() => { | |
| disabled={submitting || READ_ONLY_MODE} | |
| onClick={() => { | |
| if (READ_ONLY_MODE || submitting) return; |
There was a problem hiding this comment.
Yep, that's a valid point, also pointer-events: none would resolve that even if the button is not disabled, but I agree that we should add {submitting || READ_ONLY_MODE} as we do for the other buttons.
| .studio-disabled { | ||
| background: colors.$ui-background-disabled; | ||
| color: colors.$ui-text-disabled; | ||
| cursor: not-allowed; | ||
|
|
There was a problem hiding this comment.
.studio-disabled sets a disabled background, but for studio title/description textareas it looks like the existing textarea.studio-title/textarea.studio-description &:not(:focus) { background: transparent; } rule later in this file will override it (higher specificity), so READ_ONLY_MODE may not visually show the disabled background there. Consider adding a more specific override for disabled textareas (e.g., .studio-disabled.studio-title:disabled / .studio-disabled.studio-description:disabled) or adjusting the :not(:focus) rule to not override when disabled.
| canSave={this.props.canSave && | ||
| (!READ_ONLY_MODE || !this.props.isShared)} | ||
| canShare={this.props.canShare && !READ_ONLY_MODE} | ||
| canUpdateThumbnail={!READ_ONLY_MODE} |
There was a problem hiding this comment.
canUpdateThumbnail is disabled whenever READ_ONLY_MODE is true, even for unshared projects. That’s inconsistent with the nearby gating for canEditTitle/canSave (which still allow edits when !isShared) and with the existing canManuallySaveThumbnails logic earlier in this file (which explicitly allows thumbnails when READ_ONLY_MODE and !isShared). If the intent is “block shared/public operations only”, gate canUpdateThumbnail the same way (or align/remove the conflicting thumbnail logic so behavior is consistent).
| canUpdateThumbnail={!READ_ONLY_MODE} | |
| canUpdateThumbnail={!READ_ONLY_MODE || !this.props.isShared} |
adzhindzhi
left a comment
There was a problem hiding this comment.
I haven't reviewed everything yet, but I'm sharing my comments so far.
| />); | ||
| } | ||
| } else if (!isShared) { | ||
| } else if (!isShared && !READ_ONLY_MODE) { |
There was a problem hiding this comment.
I think by design the banner is displayed but the Share button is disabled
There was a problem hiding this comment.
@adzhindzhi Doesn't the share button hide once someone shares a project?
| } | ||
| handleFavoriteToggle () { | ||
| if (!this.props.favedLoaded) return; | ||
| if (!this.props.favedLoaded || READ_ONLY_MODE) return; |
There was a problem hiding this comment.
You probably wouldn't need these if you apply the disabled styles to the love/favorite buttons + pointer-events: none.
There was a problem hiding this comment.
I think it doesn't hurt to add that check.
| canSave={this.props.canSave && | ||
| (!READ_ONLY_MODE || !this.props.isShared)} |
There was a problem hiding this comment.
This doesn't handle pressing CTRL + 'S' in the editor, right? I wonder if we should try to disable that, or let the backend handle it and just display an error... 🤔
There was a problem hiding this comment.
Doesn't CTRL+S in Chrome (maybe some Chromium browsers too) activate a system keybind to save a page in a .mhtml?
There was a problem hiding this comment.
It is handled in the sense that the frontend fails by displaying "Project could not save.", because of this very prop. So I think that's enough?
| authorUsername: authorUsername, | ||
| authorAvatarBadge: authorAvatarBadge, | ||
| canAddToStudio: isLoggedIn && isShared, | ||
| canAddToStudio: isLoggedIn && isShared && !READ_ONLY_MODE, |
There was a problem hiding this comment.
Hmm, this would remove the whole button, right? It's disabled by design, I don't mind it, but let's confirm with Tereza if that's okay.
adzhindzhi
left a comment
There was a problem hiding this comment.
One additional general comment: We should also disable the "Anyone can add projects" toggle? It shouldn't make a difference, but it would be more consistent.
| disabled={submitting} | ||
| onClick={() => { |
There was a problem hiding this comment.
Yep, that's a valid point, also pointer-events: none would resolve that even if the button is not disabled, but I agree that we should add {submitting || READ_ONLY_MODE} as we do for the other buttons.
| } | ||
| </div> | ||
| {(canRemove || canPromote || canTransferHost) && | ||
| {(canRemove || canPromote || canTransferHost) && !READ_ONLY_MODE && |
There was a problem hiding this comment.
I think by design the menu is disabled in readonly mode, not hidden.
adzhindzhi
left a comment
There was a problem hiding this comment.
Looks good! I left two small comments, but otherwise I think it's pretty clean! One additional general comment is that a few of the disabled buttons still have cursor: pointer, which may be a bit confusing from UX perspective. I think I've only seen this for the love, favorite on PDP and Edit Thumbnail on Studios, but there may be other buttons also affected.
| {canRemove && process.env.READ_ONLY_MODE !== 'true' && | ||
| <OverflowMenu disabled={process.env.READ_ONLY_MODE === 'true'}> |
There was a problem hiding this comment.
Will the disabled attribute ever be true? Wouldn't we just not show the menu with the process.env.READ_ONLY_MODE !== 'true' check?
Resolves:
Part of https://scratchfoundation.atlassian.net/browse/UEPR-553
Changes:
Disable frontend operations for PDP and Studios