docs(plan): spec Phase 10 Wave 4 — Mix detail popover controls, RadialKnobs, lava-lamp icon, wider body
This commit is contained in:
@@ -196,7 +196,9 @@ Adds a **controls row** above the mix details / below the back button: four cont
|
||||
|
||||
Full design, renderer architecture, the four effects, acceptance criteria, and phasing: `product-notes/mix-visualizer-webgl-renderer.md`.
|
||||
|
||||
**Sequenced as three waves.** Wave 1 (renderer swap at parity — prove WebGL2 on screen at 60 FPS, bridge intact, no new effects) is the load-bearing prerequisite. Wave 2 (controls row + widened state) and Wave 3 (the four effects in the shader) both follow Wave 1; the four effects within Wave 3 are independently shippable and tunable. **Deferred (Daniel):** control-range guards and motion-speed coupling to bubblyness — he tunes bad ranges by hand once on screen. **Landed:** Wave 1 (2026-06-15). Wave 2 (2026-06-15).
|
||||
**Sequenced as four waves.** Wave 1 (renderer swap at parity — prove WebGL2 on screen at 60 FPS, bridge intact, no new effects) is the load-bearing prerequisite. Wave 2 (controls row + widened state) and Wave 3 (the four effects in the shader) both follow Wave 1; the four effects within Wave 3 are independently shippable and tunable. **Deferred (Daniel):** control-range guards and motion-speed coupling to bubblyness — he tunes bad ranges by hand once on screen. **Landed:** Wave 1 (2026-06-15). Wave 2 (2026-06-15).
|
||||
|
||||
**Wave 4 — detail-page polish + controls rework (presentation only; the final wave).** A UI/placement pass over the Mix detail page — **no renderer, state, bridge, or mapping change.** (1) The four controls move out of the always-visible row into a **popover** (`MudPopover`, `SharePopover`-idiom) opened by a new bespoke **lava-lamp icon button** anchored **top-right of the body, across from the `← Back` link** (recommend a new `TopRightAction` slot on `ReleaseDetailScaffold`, laid as a SpaceBetween row with the back link). (2) The lava-lamp SVG lives in `DeepDrftShared.Client/Common/DDIcons.cs` in the hand-rolled gas-lamp style (`currentColor`, 24×24 viewBox, raw-string const) — a recognizable lamp with two-three suspended blobs. (3) The four `MudSlider`s become four **`RadialKnob`s** (`DeepDrftShared.Client/Components/RadialKnob.razor`) **in a row in the popover**, each carrying its existing Material icon (`ZoomIn`/`BubbleChart`/`Air`/`Palette`) **as an adjacent `MudIcon` caption** — RadialKnob has **no icon slot** (its `Label` is SVG text), so icons sit beside each knob. Knobs bind `Value`/`ValueChanged` to the **unchanged** `MixVisualizerControlState` via the **same `OnXChanged` handlers + `NotifyChanged()` seam** the sliders use today (resolution via `MixZoomMapping` fraction; other three normalized [0,1]; `HoldValue=false` for live feel). (4) **Widen the Mix body** to match the Sessions detail page — `MudContainer MaxWidth="Large"` (~1280px, up from the scaffold's 760px), Mix-scoped so Track detail is unaffected. **Depends on Wave 3 merged** (the knobs drive the Wave 3 effects) and **supersedes the controls-row design** (`product-notes/mix-visualizer-webgl-renderer.md` §3 → §7). Read-only contract intact; no knob is a seek surface. Full design + acceptance: that spec's **§7**.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -418,6 +418,250 @@ hand).
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 7. Wave 4 — Detail-page polish + controls rework (presentation only)
|
||||
|
||||
Status: **design-complete, implementation-ready.** Added 2026-06-15. **Depends on Wave 3 being merged**
|
||||
(the knobs in this wave drive the four effects that Wave 3 makes real). **This wave supersedes the §3
|
||||
always-visible controls-row design** — the row moves into a popover and the four MudSliders become four
|
||||
`RadialKnob`s. The renderer, the control *values*, the `MixVisualizerControlState`, and the
|
||||
`Changed`-event bridge seam are all **unchanged**; this is a widget/placement/width rework, not a
|
||||
behavior change.
|
||||
|
||||
Files this wave touches (for orientation; staff-engineer's to implement):
|
||||
- `DeepDrftPublic.Client/Controls/MixVisualizerControls.razor[.cs/.css]` — the four controls; sliders → knobs.
|
||||
- `DeepDrftPublic.Client/Pages/MixDetail.razor[.css]` — container width; the new icon-button + popover placement.
|
||||
- `DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor[.cs]` — a new top-right action slot (see §7c).
|
||||
- `DeepDrftShared.Client/Common/DDIcons.cs` — the new lava-lamp SVG (note: DDIcons lives in
|
||||
**DeepDrftShared.Client**, not DeepDrftPublic.Client — both apps consume it).
|
||||
- `DeepDrftShared.Client/Components/RadialKnob.razor` — **consumed, not modified** (its API is fixed; §7e).
|
||||
|
||||
### 7a. Goal and scope boundary
|
||||
|
||||
**Goal.** Polish the Mix detail page and rework how the visualizer controls are presented: hide the four
|
||||
controls behind a **popover** opened by a bespoke **lava-lamp icon button** anchored top-right of the
|
||||
body (across from the `← Back` link top-left); replace the four sliders with four **RadialKnobs in a
|
||||
row** inside the popover; and **widen the Mix detail body** to match the Sessions detail page.
|
||||
|
||||
**In scope.**
|
||||
- Move the four controls out of the always-visible `TopContent` row into a popover (§7d).
|
||||
- A new lava-lamp SVG icon in `DDIcons.cs` (§7f) and an icon button that triggers the popover (§7c).
|
||||
- Replace each `MudSlider` with a `RadialKnob`, four in a row in the popover, carrying the existing
|
||||
icons as captions adjacent to each knob (§7e).
|
||||
- Widen the Mix body container from its current `760px` to the Sessions detail width (§7g).
|
||||
|
||||
**Out of scope / unchanged.**
|
||||
- **The WebGL2 renderer** and all four effects (Waves 1–3). This wave changes *how you reach the four
|
||||
control values*, not what they do or how the shader consumes them.
|
||||
- **`MixVisualizerControlState`** — same object, same four properties, same `const` defaults, same
|
||||
`Changed` event. The knobs mutate it exactly as the sliders do today (§7e). No new state, no rename.
|
||||
- **The bridge** (`MixWaveformVisualizer`) and its `setBubblyness`/`setDetach`/`setColorShiftSpeed`/
|
||||
`setZoom` pushes — untouched. It still subscribes to `MixVisualizerControlState.Changed`; it cannot
|
||||
tell whether a knob or a slider moved.
|
||||
- **`MixZoomMapping`** — resolution still rides the log-space fraction↔seconds mapping (§7e, knob 1).
|
||||
- **The read-only contract** (8.K §D) — no knob is a seek surface; the popover adds no playback control.
|
||||
- **Persistence model** — still session-scoped via the DI-scoped `MixVisualizerControlState`: survives
|
||||
SPA nav, resets on fresh load (F5). The popover is pure presentation over the same state.
|
||||
|
||||
### 7b. Why a popover (the reframe)
|
||||
|
||||
The §3 always-visible row spends permanent vertical real estate on four controls that, in normal
|
||||
listening, the user sets once and leaves. On a page whose *point* is a full-bleed living visualizer, a
|
||||
persistent control strip competes with the art. Tucking the controls behind a single small affordance —
|
||||
the lava-lamp button — returns the page to the visualizer and makes the controls a deliberate "I want to
|
||||
tune this" gesture. This is the standard "progressive disclosure for advanced controls" move (cf.
|
||||
Lightroom's collapsible panels, a video player's settings gear): the thing you adjust occasionally lives
|
||||
one click away, not always on screen. The lava-lamp glyph also *names* what the controls do — it reads as
|
||||
"the lava-lamp settings," which is exactly the visualizer's identity.
|
||||
|
||||
### 7c. The trigger: lava-lamp icon button, top-right of the body
|
||||
|
||||
**Placement.** Top-right of the body container, **horizontally across from the `← Back` link** (which
|
||||
the scaffold renders top-left). Today `ReleaseDetailScaffold` renders, in order: the back link
|
||||
(top-left), then `TopContent`, then the masthead. There is no top-right anchor today.
|
||||
|
||||
**Recommended scaffold change (minimal, reusable):** add an optional `TopRightAction` render-fragment
|
||||
slot to `ReleaseDetailScaffold` and lay the back link + that slot as a single
|
||||
`MudStack Row Justify="SpaceBetween"` at the top of the container — back link left, action right. This
|
||||
keeps the back link where it is, gives a clean top-right anchor across from it, and is reusable by other
|
||||
media later (it stays null for Track/Session, which don't supply it). The Mix page passes its lava-lamp
|
||||
icon button into `TopRightAction`. Avoid absolute-positioning the button into the container corner — the
|
||||
SpaceBetween row is the scaffold-idiomatic way and survives the width change in §7g.
|
||||
|
||||
The `TopContent` slot (which the §3 row used) is **emptied** by this wave — the controls no longer live
|
||||
there. Leave the slot on the scaffold (other media may want it; it is generic), but `MixDetail` stops
|
||||
passing the controls row into it.
|
||||
|
||||
**The button.** A `MudIconButton` (or `MudButton` with only the icon) rendering the lava-lamp SVG from
|
||||
`DDIcons` (§7f), `Color.Secondary` to match the page's play affordance, with an `aria-label` like
|
||||
"Visualizer settings". It is the popover's anchor and toggle.
|
||||
|
||||
### 7d. The popover
|
||||
|
||||
**Primitive: `MudPopover`, driven by an explicit `Open` bool toggled by the icon button.** Recommended
|
||||
over `MudMenu` because the content is a custom four-knob layout with drag interaction, not a list of
|
||||
menu-items — `MudMenu` is built for actionable item lists and would fight the knob drag/click model.
|
||||
`MudPopover` is the right MudBlazor primitive for "anchored floating panel of arbitrary content," and it
|
||||
is already in the codebase vocabulary (`SharePopover` is the precedent — follow its open/close idiom).
|
||||
|
||||
**Anchor & positioning.** Anchor to the lava-lamp button; open **below and right-aligned to the button**
|
||||
(`AnchorOrigin=TopRight`, `TransformOrigin=TopRight` or the MudBlazor equivalent that drops the panel
|
||||
down-and-left from a top-right trigger) so the panel opens *into* the page, not off the right edge. Add
|
||||
modest elevation and the standard rounded surface so it reads as a floating panel over the visualizer.
|
||||
|
||||
**Open/close behavior.**
|
||||
- Click the lava-lamp button → toggle open/closed.
|
||||
- Click outside the panel → close (use MudPopover's overlay/`OutsideClickClose` idiom as `SharePopover`
|
||||
does; a transparent click-catcher overlay is acceptable if that is the established pattern).
|
||||
- The panel stays open while the user drags knobs (the knob's own global mouse-capture overlay, §7e,
|
||||
must not be read as an outside-click that closes the popover — verify these don't conflict; if they do,
|
||||
gate outside-click-close off while a knob is dragging).
|
||||
- No auto-close on value change — the user tunes multiple knobs in one session.
|
||||
- `Esc` closes (nice-to-have; follow `SharePopover` if it does this).
|
||||
|
||||
**Layout inside the popover:** the four knobs **in a single row** (§7e), each with its icon caption, with
|
||||
comfortable padding. On a narrow viewport the row may wrap to 2×2 — a layout call for staff-engineer, but
|
||||
all four must remain reachable (mirrors the §3b "none may drop" rule). The popover is the *only* home for
|
||||
these controls after this wave.
|
||||
|
||||
### 7e. RadialKnob integration (grounded in the actual control)
|
||||
|
||||
**What `RadialKnob` actually is** (read from `DeepDrftShared.Client/Components/RadialKnob.razor`, 225
|
||||
lines): a self-contained SVG knob — a 270° background arc, a value arc, a center dot, and a pointer line —
|
||||
that the user drags **vertically** (up = increase) to change its value. Confirmed API:
|
||||
|
||||
| Member | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `Value` | `double` | Current value. **Two-way capable** via `ValueChanged`. |
|
||||
| `ValueChanged` | `EventCallback<double>` | Fires on drag (immediately, unless `HoldValue`). This is the binding seam. |
|
||||
| `Min` / `Max` | `double` | Value range. Internally normalizes to `[0,1]` for the arc/pointer. Defaults `0`/`100`. |
|
||||
| `Step` | `double` | Quantization. Default `1`. **Set fine (e.g. `0.001`) for continuous feel** (matches the sliders' `Step="0.001"`). |
|
||||
| `Label` | `string` | Rendered as SVG `<text>` **inside** the knob (centered, bottom). **Text only — there is NO icon slot/parameter.** Default `""`. |
|
||||
| `Size` | `int` | Pixel width/height (square). Default `50`. Use a larger size (e.g. `64`–`80`) so four read clearly in a row. |
|
||||
| `Color` | `MudBlazor.Color` | Maps to a `--mud-palette-*` var for the arc/pointer. Use `Color.Primary` (or `Secondary`) for theme consistency. |
|
||||
| `HoldValue` | `bool` | If `true`, the value display shows the live drag value as `F0` and `ValueChanged` fires only on mouse-up. If `false` (default), `ValueChanged` fires continuously during drag and the `Label` shows. |
|
||||
|
||||
**Two consequences for this wave, called out because they shape the implementation:**
|
||||
|
||||
1. **No icon slot.** The existing controls carry MudBlazor Material icons — confirmed in
|
||||
`MixVisualizerControls.razor`: `Icons.Material.Filled.ZoomIn` (resolution), `.BubbleChart`
|
||||
(bubblyness), `.Air` (detach), `.Palette` (color-shift speed). RadialKnob's `Label` is SVG text, not
|
||||
an icon. **Spec: render each icon as a `MudIcon` adjacent to its knob** (caption above or below the
|
||||
knob, in a small `MudStack` per control), and use the knob's `Label` for nothing or for a short text
|
||||
caption — **not** for the icon. One control = `{ MudIcon + RadialKnob }` stacked; four such stacks in
|
||||
a row. Carry the four existing icons over **exactly** (same four `Icons.Material.Filled.*`).
|
||||
|
||||
2. **`HoldValue` choice.** Leave `HoldValue=false` (the default) so the knobs are **live** — the effect
|
||||
responds continuously as the user drags, matching today's continuous-slider feel and keeping the §5
|
||||
"visibly and continuously affects its target as it is dragged" acceptance. (`HoldValue=true` would
|
||||
make the effect jump only on release — wrong for a "feel it move" tuning surface.) Trade-off: live
|
||||
mode fires `ValueChanged` on every drag delta, which raises `Changed` and pushes a uniform per delta —
|
||||
identical to the slider's current behavior, so no regression. The drag-time global mouse-capture
|
||||
overlay (the `_isDragging` full-viewport div at `z-index: 9999`) must coexist with the popover (§7d
|
||||
open/close note).
|
||||
|
||||
**Per-knob mapping — one knob per control, mutating `MixVisualizerControlState` exactly as the sliders
|
||||
do today** (the `OnXChanged` handlers in `MixVisualizerControls.razor.cs` are reused verbatim — only the
|
||||
widget that calls them changes):
|
||||
|
||||
| Knob | Icon (carried over) | Binds to | Min/Max/Step | Handler (unchanged) |
|
||||
|------|--------------------|----------|--------------|---------------------|
|
||||
| 1. Resolution | `ZoomIn` | `ControlState.VisibleSeconds` via `MixZoomMapping` | `0`/`1`/`0.001` on the **fraction** (`MixZoomMapping.SecondsToFraction` in, `FractionToSeconds` out) — identical to the slider | `OnResolutionChanged(fraction)` |
|
||||
| 2. Bubblyness | `BubbleChart` | `ControlState.Bubblyness` | `0`/`1`/`0.001` | `OnBubblynessChanged(value)` |
|
||||
| 3. Detach | `Air` | `ControlState.Detach` | `0`/`1`/`0.001` | `OnDetachChanged(value)` |
|
||||
| 4. Color-shift speed | `Palette` | `ControlState.ColorShiftSpeed` | `0`/`1`/`0.001` | `OnColorShiftSpeedChanged(value)` |
|
||||
|
||||
Resolution stays special exactly as it is now: the knob's `Value` is the **fraction**
|
||||
(`MixZoomMapping.SecondsToFraction(ControlState.VisibleSeconds)`), and `OnResolutionChanged` maps the
|
||||
fraction back to seconds before raising `Changed`. The other three bind their normalized `[0,1]` value
|
||||
directly. **The `NotifyChanged()` call after each mutation is preserved** — that is the bridge seam, and
|
||||
it is what keeps this wave a pure widget swap. `aria-label` per knob carries over from the sliders.
|
||||
|
||||
### 7f. The lava-lamp SVG icon
|
||||
|
||||
**Home: `DeepDrftShared.Client/Common/DDIcons.cs`** — the same file as the hand-rolled gas-lamp
|
||||
lit/unlit icons, in the same style. Match the existing convention exactly:
|
||||
- A `public const string` using a C# raw-string literal.
|
||||
- `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">` (same 24×24 viewBox as `GasLamp`).
|
||||
- **`fill="currentColor"`** on the structural paths so the icon themes with its context (the gas-lamp
|
||||
icons do this; it is load-bearing for dark-mode and `Color.Secondary` tinting). Accent fills (a warmer
|
||||
blob color, à la the gas-lamp flame's `#FF9800`/`#FFCA28`) are acceptable for the lava blobs if a
|
||||
two-tone read is wanted, but the lamp silhouette must be `currentColor`.
|
||||
|
||||
**What it depicts.** A recognizable **lava lamp**: a tapered conical/hourglass base, a tall rounded glass
|
||||
vessel rising from it, a cap on top, and **two or three rounded lava blobs** suspended at different
|
||||
heights inside the vessel (one larger toward the bottom, one or two smaller rising). The silhouette must
|
||||
read as "lava lamp" at icon size (≈24px) — favor a bold, simple silhouette over fine detail, exactly as
|
||||
the gas-lamp icon reads as a lantern at small size. The blobs are the recognizable cue; keep them
|
||||
generous and few. (Precise path data is staff-engineer's; this describes the asset and its intent.)
|
||||
|
||||
Expose it the same way the gas-lamp icons are consumed (raw SVG string into a MudBlazor icon-rendering
|
||||
context, as `MainLayout` does for the dark-mode toggle).
|
||||
|
||||
### 7g. Container width — match the Sessions detail page
|
||||
|
||||
**Current state.** `MixDetail` composes `ReleaseDetailScaffold`, whose root is
|
||||
`.deepdrft-track-detail-container` — `max-width: 760px; margin: 0 auto` (in
|
||||
`DeepDrftPublic/wwwroot/styles/deepdrft-styles.css`). `SessionDetail` does **not** use the scaffold; it
|
||||
wraps its content in `MudContainer MaxWidth="MaxWidth.Large"` (MudBlazor's `Large` breakpoint, ~1280px),
|
||||
giving it the wider, hero-dominant feel.
|
||||
|
||||
**Spec the widening.** Make the Mix detail body as wide as the Sessions detail body — i.e. the
|
||||
`MudContainer MaxWidth="Large"` width (~1280px), up from 760px. Because `MixDetail` shares the scaffold
|
||||
container with `TrackDetail`, **do not** widen `.deepdrft-track-detail-container` globally (that would
|
||||
also widen Track detail, out of scope). Two acceptable approaches, staff-engineer's call:
|
||||
|
||||
1. **Preferred — wrap the Mix scaffold in `MudContainer MaxWidth="Large"`** and neutralize the inner
|
||||
`max-width: 760px` for that instance (a Mix-specific class on the scaffold, or wrapping such that the
|
||||
`MudContainer` is the constraining width). This reuses the *same primitive* SessionDetail uses, so the
|
||||
two pages match by construction (memory: one source / shared idiom) rather than by a hand-copied pixel
|
||||
value.
|
||||
2. Alternative — a Mix-scoped override class that sets `max-width` to the `Large` breakpoint value. Works,
|
||||
but couples the Mix page to a magic number that can drift from `Large`; prefer option 1.
|
||||
|
||||
Either way the body must visibly match the Sessions detail width. The full-bleed visualizer backdrop
|
||||
(behind `.mix-detail-foreground`) is unaffected — it is already full-page; only the foreground content
|
||||
column widens.
|
||||
|
||||
### 7h. Acceptance criteria (observable)
|
||||
|
||||
1. **Popover trigger.** A lava-lamp icon button sits at the **top-right** of the Mix detail body,
|
||||
horizontally across from the `← Back` link (top-left). Clicking it opens a popover; clicking it again,
|
||||
or clicking outside, closes it.
|
||||
2. **Icon.** The button shows a recognizable lava-lamp glyph (sourced from `DDIcons`), themed via
|
||||
`currentColor` so it tints correctly in light/dark.
|
||||
3. **Four knobs in a row.** The popover contains exactly four `RadialKnob`s in a row (wrap allowed on
|
||||
narrow viewports, none dropped), each captioned with its existing icon — `ZoomIn`, `BubbleChart`,
|
||||
`Air`, `Palette`, in that order.
|
||||
4. **Knobs drive the effects.** Dragging each knob continuously changes its `MixVisualizerControlState`
|
||||
value and the corresponding effect responds live (post-Wave-3): resolution → visible time-span/scroll
|
||||
speed; bubblyness → straight↔liquid bulge; detach → attached↔rising blobs; color-shift speed →
|
||||
slow↔brisk field morph. Resolution still rides `MixZoomMapping`; the other three are normalized [0,1].
|
||||
5. **Persistence preserved.** Knob positions survive SPA navigation to another mix within a session and
|
||||
reset to defaults on a fresh page load (F5) — unchanged `MixVisualizerControlState` behavior.
|
||||
6. **Old row gone.** The always-visible four-slider row (the §3 / Wave 2 design in `TopContent`) is no
|
||||
longer rendered on the Mix detail page. The controls exist *only* in the popover.
|
||||
7. **Width.** The Mix detail body container is as wide as the Sessions detail body
|
||||
(`MudContainer MaxWidth="Large"`), not the former 760px. Track detail's width is unchanged.
|
||||
8. **Bridge & read-only intact.** The visualizer still couples to playback via the unchanged bridge; no
|
||||
knob and no popover element is a seek/playback surface; the renderer and effects are unchanged from
|
||||
Wave 3.
|
||||
|
||||
### 7i. Phasing note
|
||||
|
||||
- **Depends on Wave 3 merged.** The knobs drive the four effects; without Wave 3 the knobs move inert
|
||||
values (as the Wave 2 sliders do today). Wave 4 can be *built* against inert values but its acceptance
|
||||
criterion 4 ("effect responds") requires Wave 3. Sequence after Wave 3.
|
||||
- **Supersedes the §3 controls-row design.** §3 (always-visible MudSlider row in `TopContent`) is the
|
||||
Wave 2 design; Wave 4 replaces it with the popover + knobs. §3 stays in this doc as the record of what
|
||||
Wave 2 shipped; this §7 is the forward design that overtakes it. When Wave 4 lands, doc-keeper archives
|
||||
the §3-vs-§7 transition per house style.
|
||||
- **Pure presentation.** No renderer, state, bridge, or mapping change — the smallest-surface way to get
|
||||
the popover + knobs + width is the right way. Resist scope creep into effect or state changes here.
|
||||
|
||||
---
|
||||
|
||||
## Open items (tuning knobs, none block starting)
|
||||
|
||||
All have recommended defaults inline; Daniel tunes on screen:
|
||||
@@ -427,3 +671,9 @@ All have recommended defaults inline; Daniel tunes on screen:
|
||||
- Whether the controls row wraps or horizontally scrolls on mobile (§3b) — layout call.
|
||||
- Whether control state should later persist cross-session (cookie/localStorage) — deferred upgrade to
|
||||
the one state object (§3c).
|
||||
- **Wave 4:** knob `Size` (px) for the four-knob popover row, and whether each knob's `Label` carries a
|
||||
short text caption or stays blank with only the icon caption (§7e) — layout/feel call.
|
||||
- **Wave 4:** popover anchor/positioning fine-tuning (drop direction, elevation, width) and whether
|
||||
outside-click-close must be gated off mid-knob-drag to avoid the drag overlay closing it (§7d).
|
||||
- **Wave 4:** whether the lava-lamp icon is one-tone (`currentColor` only) or two-tone with accent blob
|
||||
fills à la the gas-lamp flame (§7f) — aesthetic call.
|
||||
|
||||
Reference in New Issue
Block a user