- 🧹 Introduce `lecturers:cleanup` command to delete lecturers without associated courses or events, merging their items into "Einundzwanzig."
- ⚙️ Add `update` method to `UserController` for handling profile updates, allowing name changes while restricting role modifications.
- 🌐 Register `PATCH /api/user` route for profile updates and update related API tests.
- 🧪 Add feature and console tests for `lecturers:cleanup`, covering dry-run, forced deletion, and edge cases.
Chrome follows a server 302 internally and never dispatches the /app/auth
App Link, so the handoff page stayed in the browser and the token never
reached the app. The signed callback (and complete/confirm) now render
the handoff page directly with the einundzwanzig:// deep-link button — the
signer opens the callback in the browser, the user lands on the handoff
page and taps once to return to the app, which stores the token.
The Nostr login is now driven entirely by the app (it launches the
NIP-55 signer via an ACTION_VIEW intent and posts the signed event to
/auth/mobile/signed), so the portal page no longer needs window.nostr or
an Amber button — it only renders the Lightning QR. The path-based
signer callback and token exchange endpoints remain server-side.
Replaces the fragile NIP-55 intent/callback round-trip with the same
mechanism the desktop login uses: openNostrLogin signs the session
challenge via window.nostr — provided by an extension or by
window.nostr.js over a persistent NIP-46 connection (Amber pairing with
permissions). The listener stores a LoginKey for the page's k1 and
navigates to the completion route, which issues the token and redirects
into the app via the verified App Link handoff.
Replaces the custom-scheme auto-redirect (which triggers Chrome's
confirmation prompt) with a verified Android App Link handoff:
- public/.well-known/assetlinks.json for space.einundzwanzig.mobile
(debug cert fingerprint; add the release cert before store builds)
- GET /app/auth handoff: opens the app directly when the App Link is
verified; renders a button-based fallback page otherwise
- POST /api/mobile/token: trades a NIP-55-signed login event for a
Sanctum token — used when Amber's callback opens the app directly
- complete/confirm/signedCallback now redirect to the handoff URL
Amber drops the query string when it rebuilds the callback URL and
appends the signed event directly to the path. The mobile login page now
hands out path-based callback URLs (/auth/mobile/signed/{k1}/) so the
event arrives as the remainder of the path.
The new callback runs in the web middleware group: the signer opens it
in the system browser, which shares cookies with the in-app browser
session, so the flow completes immediately — a bridge page issues the
token and fires the einundzwanzig:// deep link. The LoginKey row is
still written as a fallback for the polling login page.
The Einundzwanzig mobile app opens /auth/mobile in an in-app browser.
After a Lightning (LNURL) or Nostr login the flow issues a personal
access token and hands it back via the einundzwanzig://auth deep link.
- New auth.mobile-login Livewire view: Lightning QR (shared k1) plus
Nostr signing via NIP-55 Android signers (Amber) with server callback,
and a confirmation screen for already authenticated sessions
- MobileAuthController: NIP-55 callback verification, completion route
issuing the token (replacing same-device tokens), redirect whitelist
- Nostr login event verification and npub user resolution extracted to
App\Support\NostrLogin, now shared with the interactive login
- GET /api/user (auth:sanctum) returns the token owner's profile
- Updated `authorizeAccess` to restrict `meetups.edit` views and updates to users in "My-Meetups".
- Attached creators to `meetup_user` pivot for default membership.
- Adjusted related tests to validate membership-based edit permissions.
📱 **Improve sidebar and mobile navigation accessibility**
- Added `aria-labels` to improve screen reader support for sidebar and mobile header elements.
- Updated desktop and mobile user menus alignment for consistency.
⚡ **Enhance Lightning login flow**
- Introduced `lightningLoginInProgress` for smoother polling synchronization with the redirect flow.
- Updated logic to dispatch `lightning-login-ready` event instead of immediate redirect, avoiding race conditions.
- 🔄 `requestNostrChallenge` now issues a new challenge when needed.
- 🛡️ Enhanced fallback logic in `nostrLogin.js` to ensure robust challenge retrieval.
- ✅ Added test coverage for fresh challenge issuance.
✨ **Tests:** Added helper function `makeSignedNostrLoginEvent` for generating NIP-42 signed login events. Updated related tests in `Feature/Auth/NostrLoginTest.php` to use this helper.
🚀 **Livewire Testing:** Enhanced authorization checks and added specific creator-based mounts for `meetups.edit`. Improved tests for `MeetupMountTest` and `EditMeetupTest`.
🎨 **Style:** Standardized `request()->route()` to lowercase country codes across multiple Blade templates for consistency.
🛠️ **Config:** Updated `vite.config.js` formatting for improved readability in ignored paths.
- **Removed:** Unused feature and component tests to clean up the codebase.
- **Added:** `RecurrenceType` enum for handling event recurrence modes.
- **Introduced:** City, Country, and Meetup factories for test data generation.
- **Implemented:** Migration to support recurring event fields in `meetup_events` table.
- **Enhanced:** Livewire meetup events creation with recurrence validation and preview logic.
- **Updated:** PHPUnit test suite configuration and composer dependencies for `pestphp/pest@v4.3`.
- **Refined:** SEO configuration (`favicon`) to standardize icon format.