Repository navigation
Conversation
There was a problem hiding this comment.
Pull request overview
This PR improves the Android “native feel” of Coho by adding a Capacitor bridge for richer system haptics and expanding the Android Material You integration to expose full tonal palettes + resolved Material 3 light/dark schemes to the web layer. It also updates web/theme initialization and a handful of UI components (composer, dialogs, timeline actions) to consume the richer haptic + theme capabilities.
Changes:
- Added Android
RichHapticsBridgeand updated web haptics utilities to support semantic haptic types with graceful fallbacks. - Expanded
DynamicThemeBridge+ web theme utilities to apply full Material You tonal palettes and resolved M3 schemes (including startup restoration viaindex.html). - Updated composer/dialog/timeline components for improved UX (mobile publish action, avatar layout, refined haptic triggers, borderless text area variant, draggable “sheet” dialog behavior).
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/timeline-actions.ts | Swaps generic impact haptics for semantic confirm/toggle/reject feedback on timeline actions and rollbacks. |
| src/utils/theme-color.ts | Adds support for applying full native dynamic palette/scheme tokens and meta theme-color updates from schemes. |
| src/utils/haptics.ts | Introduces semantic haptic API with Android rich-haptics bridge + fallbacks; keeps backward-compatible helpers. |
| src/utils/dynamic-theme.ts | Adds full dynamic palette + scheme retrieval/caching via DynamicThemeBridge.getPalette(). |
| src/styles/post-composer-styles.ts | Adjusts composer layout to include avatar + flexible text area wrapper. |
| src/components/user-profile.ts | Refines click handling to prevent event bubbling from profile component interactions. |
| src/components/timeline.ts | Updates pull-to-refresh threshold haptic to a semantic “threshold” haptic. |
| src/components/timeline-renderers.ts | Stops click propagation from right-side post actions; minor markup cleanup. |
| src/components/post-dialog.ts | Adds a mobile header publish button driven by composer state events and richer publish UI states. |
| src/components/post-composer/publish-orchestrator.ts | Adds semantic success/failure haptics to publish flow outcomes. |
| src/components/post-composer/attachment-manager.ts | Adds semantic confirm/reject haptics for attachment selection and size-limit rejection. |
| src/components/post-composer.ts | Adds avatar display, borderless textarea usage, state-change event for dialog integration, and programmatic submit. |
| src/components/md/md-text-area.ts | Adds a new borderless variant for textarea styling. |
| src/components/md/md-tab.ts | Updates tab haptics to semantic segment tick. |
| src/components/md/md-switch.ts | Updates switch haptics to semantic toggle on/off. |
| src/components/md/md-segmented-button.ts | Updates segmented-button haptics to semantic segment tick. |
| src/components/md/md-dialog.ts | Adds optional “sheet” behavior with drag handle and touch drag-to-dismiss interactions. |
| src/components/app-theme.ts | Updates device theme handling to prefer full palettes/schemes when available. |
| src/app-index.ts | Updates theme initialization to apply dynamic palette schemes when available (vs. accent-only). |
| index.html | Restores full scheme tokens synchronously at startup when device theme is selected. |
| android/app/src/main/java/place/coho/app/RichHapticsBridge.java | New Capacitor plugin mapping semantic haptic types to Android HapticFeedbackConstants with version fallbacks. |
| android/app/src/main/java/place/coho/app/MainActivity.java | Registers the new RichHapticsBridge plugin. |
| android/app/src/main/java/place/coho/app/DynamicThemeBridge.java | Extends dynamic theme bridge to return full tonal palettes + resolved M3 light/dark schemes. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| this.dispatchEvent( | ||
| new CustomEvent('composer-state-changed', { | ||
| bubbles: true, | ||
| composed: true, | ||
| detail: { | ||
| canPublish: !( | ||
| (!this.hasStatus && !this.publishSuccess) || | ||
| this.attaching || | ||
| this.attachments.some((a) => a.pending) || | ||
| this.isPublishing || | ||
| this.publishSuccess | ||
| ), | ||
| isPublishing: this.isPublishing, | ||
| publishSuccess: this.publishSuccess, | ||
| primaryLabel: this.replyTo | ||
| ? 'Reply' | ||
| : this.scheduleEnabled | ||
| ? 'Schedule' | ||
| : 'Publish', | ||
| }, | ||
| }) | ||
| ); |
| @click="${() => this._handleSubmit()}" | ||
| style="${this.dialogMode && window.matchMedia('(max-width: 820px)').matches ? 'display: none;' : ''}" | ||
| > |
| ? html`<img | ||
| class="author-avatar" | ||
| src="${this.avatarUrl}" | ||
| alt="Author avatar" | ||
| />` | ||
| : nothing |
| textarea.borderless:focus-visible { | ||
| outline: none; | ||
| } |
| ? html`<span | ||
| class="publish-label" | ||
| style="display: flex; align-items: center; gap: 6px;" | ||
| ><span | ||
| class="publish-spinner" | ||
| style="width: 14px; height: 14px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin 0.8s linear infinite;" | ||
| ></span> | ||
| ${msg('Publishing...')}</span | ||
| >` |
| @PluginMethod | ||
| public void perform(PluginCall call) { | ||
| String type = call.getString("type", "click"); | ||
|
|
||
| getActivity().runOnUiThread(() -> { | ||
| View view = null; | ||
| if (getBridge() != null && getBridge().getWebView() != null) { | ||
| view = getBridge().getWebView(); | ||
| } else if (getActivity() != null && getActivity().getWindow() != null) { | ||
| view = getActivity().getWindow().getDecorView(); | ||
| } | ||
|
|
||
| if (view == null) { | ||
| call.resolve(); | ||
| return; | ||
| } | ||
|
|
||
| int constant = getHapticConstant(type); | ||
| view.performHapticFeedback(constant, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); | ||
| call.resolve(); | ||
| }); | ||
| } |
| if (this._currentDragY > dismissThreshold || velocity > 0.4) { | ||
| this.dialog.style.setProperty( | ||
| '--md-sheet-drag-offset', | ||
| `${this._currentDragY}px` | ||
| ); | ||
| this.dialog.style.transform = ''; | ||
| this.hide(); |
|
Visit the preview URL for this PR (updated for commit 9ab47ff): https://coho-mastodon--pr442-polishing-uzz4xd4n.web.app (expires Tue, 25 Aug 2026 15:56:56 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: 22a5c1adab53191fa6702e9454dee7bd02b2edd7 |
Fixes #441
This pull request introduces a new Capacitor plugin for rich haptic feedback and a comprehensive Material You dynamic color palette bridge. The most important changes are:
Android Native Features
RichHapticsBridgeCapacitor plugin, enabling rich, hardware-tuned system haptic feedback on Android via a range of system constants, with graceful fallbacks based on OS version. (android/app/src/main/java/place/coho/app/RichHapticsBridge.java,android/app/src/main/java/place/coho/app/MainActivity.java) [1] [2]DynamicThemeBridgeplugin to expose the full set of Material You tonal palettes (Accent 1-3, Neutral 1-2) and resolved Material 3 color schemes for both light and dark modes, not just the accent color. (android/app/src/main/java/place/coho/app/DynamicThemeBridge.java) [1] [2]Web/App Integration
index.html) and app code (src/app-index.ts,src/components/app-theme.ts) to detect and apply the full dynamic palette and resolved color schemes, ensuring all Material 3 color variables are set when "device" theme is selected. [1] [2] [3] [4] [5]These changes allow the app to deliver a more native, immersive experience on Android devices by leveraging system-driven color and haptic feedback, while maintaining compatibility and graceful degradation on older versions.