Skip to content

feat: .NET 10 fixes, LinkedIn provider (WIP), moderation UI improvements#555

Open
csharpfritz wants to merge 37 commits intomainfrom
feature/linkedin-event-display
Open

feat: .NET 10 fixes, LinkedIn provider (WIP), moderation UI improvements#555
csharpfritz wants to merge 37 commits intomainfrom
feature/linkedin-event-display

Conversation

@csharpfritz
Copy link
Contributor

@csharpfritz csharpfritz commented Feb 19, 2026

Summary

This branch contains critical .NET 10 compatibility fixes, UI improvements, and the beginning of a LinkedIn social media provider integration.

Working Changes

.NET 10 Breaking Change Fix

  • UseStaticFiles to MapStaticAssets in Program.cs - .NET 10 no longer serves Blazor framework files via UseStaticFiles(). This was causing a complete loss of interactivity site-wide (404 on _framework/blazor.web.js).

Save Button and Users Page Fix

  • Removed broken _isInteractive pattern from LinkedIn config UI that was keeping the Save button permanently disabled
  • Fixed Users admin page by adding rendermode InteractiveServer

Moderation Panel CSS

  • Fixed overflow hidden on taggedContent article that was clipping the moderation action bar
  • Increased action bar height and icon sizes for better usability

Content Lookback

  • Expanded initial content retrieval window from 1 hour to 7 days (affects all providers)

LinkedIn Provider (WIP - Not Production Ready)

The LinkedIn provider includes:

  • OAuth 2.0 token acquisition flow with authorize/callback/debug-auth endpoints
  • Provider implementation using GET /rest/posts?q=author with client-side hashtag filtering
  • Full configuration UI in admin panel with configurable RedirectUri
  • Comprehensive unit tests (all passing)

Status: The OAuth consent flow hits LinkedIn's 'Bummer something went wrong' error on their consent page. This appears to be a LinkedIn Developer App configuration issue rather than a code issue. The provider code is complete and tested but cannot acquire tokens until the OAuth flow is resolved.

LinkedIn requires the Community Management API product enabled on the Developer App for r_member_social scope access.

Test Results

  • 66 total tests: 64 passed, 2 skipped (YouTube - missing API keys), 0 failures
  • Code formatted with dotnet format

csharpfritz and others added 30 commits February 18, 2026 13:59
Session: 2026-02-18-linkedin-planning-waterfall-fixes
Requested by: Jeffrey T. Fritz

Changes:
- Logged session to .ai-team/log/2026-02-18-linkedin-planning-waterfall-fixes.md
- Merged LinkedIn provider plan from inbox into decisions.md
- Deleted merged inbox file
- Propagated updates to Sombra and Ana history files

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove overflow:visible on article hover preventing content bleed
- Add word-break/overflow-wrap on .content for long URLs
- Constrain contentcard images/videos to max-width:100%
- Fix footer fade gradient to use CSS variable for theme support
- Change modal body font-weight from bold to normal for readability
- Fix byline min-width causing overflow on narrow grid columns
- Replace invalid alt on video elements with aria-label
- Fix stray brace in overlay alt attribute
- Fix double-semicolon in modal display style value
- Add role=button and tabindex=0 on waterfall cards for accessibility

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- New TagzApp.Providers.LinkedIn project with full ISocialMediaProvider implementation
- LinkedIn Marketing API integration (GET /rest/posts?q=hashtag) with OAuth 2.0
- Daily API call budget tracking with graceful degradation to Degraded health
- Author profile caching via ConcurrentDictionary to minimize API calls
- Token expiry monitoring in health checks (warns at <7 days)
- Admin config UI (LinkedIn.Config.Ui.razor) with all OAuth/polling fields
- Wired into Service_Providers.cs and GenericProvider.razor
- 32 unit tests covering configuration, content mapping, budget tracking,
  token expiry health, and provider metadata
- Uses bi-linkedin Bootstrap icon
- Default 5-minute polling interval (configurable, min 5)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Session: 2026-02-18-linkedin-provider-implementation
Requested by: Jeff Fritz

Changes:
- Logged session to .ai-team/log/2026-02-18-linkedin-provider-implementation.md
- Merged 4 decisions from inbox into .ai-team/decisions.md
  * LinkedIn provider open questions resolved (Jeff Fritz)
  * LinkedIn provider architecture decision (Sombra)
  * LinkedIn daily budget tracking decision (Sombra)
  * LinkedIn unit tests completion (Ana)
  * Waterfall CSS/HTML rendering fixes (Symmetra)
- Propagated updates to agent history files (Sombra, Ana, Symmetra)
- Deleted inbox decision files after merging

All 32 LinkedIn tests pass. Full suite: 64 passed, 2 skipped, 0 failed.
Build: 0 errors. Commit: 2da5f39

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add /EventDisplay route for kiosk-friendly, auto-scrolling waterfall view
- EventDisplay.razor: full-screen, hands-free display with SignalR, 50-message cap
- eventDisplay.js: smooth auto-scroll with pause-on-new-content behavior
- Branding strip shows tracked hashtag at bottom of display
- Nav link in header with bi-display icon
- README: Add LinkedIn to providers (8 total), add Event Display to overlay section
- doc/README: Add LinkedIn Provider and Event Display documentation sections
- Update .NET version reference from 9 to 10

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Session: 2026-02-18-event-display-and-docs
Requested by: Jeffrey T. Fritz

Changes:
- Logged session to .ai-team/log/2026-02-18-event-display-and-docs.md
- Merged decision: Event Display Page Full-Screen Kiosk Mode (Symmetra)
- Propagated update to Symmetra's history.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds Squad v0.4.1 orchestration files:
- .ai-team-templates/ (casting, charters, ceremonies, routing templates)
- .copilot/mcp-config.json (MCP server configuration)
- .github/agents/squad.agent.md (coordinator agent instructions)
- .github/workflows/squad-*.yml (10 squad automation workflows)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Automated formatting cleanup across 10 source files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Session: 2026-02-19-linkedin-token-ui
Requested by: Jeffrey T. Fritz

Changes:
- Logged session to .ai-team/log/2026-02-19-linkedin-token-ui.md
- Merged decision from inbox into decisions.md
- Propagated update to Sombra's history

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Access Token, Refresh Token, and Token Expires At are now displayed
as obfuscated read-only labels instead of editable inputs. These are
managed by the OAuth flow, not entered by the admin. SaveConfig no
longer overwrites provider-managed token values.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The LinkedIn provider admin UI uses EditForm with OnValidSubmit inside an
InteractiveServer rendered page, so form submission goes through Blazor's
SignalR event system, not HTTP POST. The AntiforgeryToken component is
unnecessary for interactive forms and was causing antiforgery validation
errors on save.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This guardrails workflow is specific to the Squad product repository
and should not be in TagzApp.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Providers page uses InteractiveServer with prerendering enabled.
If the user clicks Save before the SignalR connection is established,
the form submits via HTTP POST instead of Blazor events. The POST
needs both a FormName (to identify the form handler) and an
AntiforgeryToken (for CSRF validation).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rors

The antiforgery token error occurred because the InteractiveServer render mode
prerenders by default. During the prerender-to-interactive transition, form
clicks trigger HTTP POST instead of Blazor SignalR events, causing antiforgery
validation to fail. Disabling prerendering ensures forms only submit through
the interactive event system.

Also reverts FormName on LinkedIn config to match all other providers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Disabling prerendering caused the page to render blank. The Users page
has the same issue with prerender:false (pre-existing). Reverting to
the standard InteractiveServer render mode while investigating a
different approach for the LinkedIn antiforgery issue.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Root cause: during prerender-to-interactive transition, clicking Save
triggers a native HTTP POST instead of a Blazor SignalR event. The
AntiforgeryToken rendered during prerender becomes stale by the time
the POST arrives, causing validation failure.

Fix:
- Remove AntiforgeryToken (unnecessary for interactive-only forms)
- Add _isInteractive guard: Save button is disabled until OnAfterRender
  fires, which only happens after the SignalR circuit is connected.
  This prevents any HTTP POST during the prerender gap.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ider pattern

The LinkedIn.Config.Ui.razor was missing <AntiforgeryToken /> inside the
EditForm, which all other working providers (Bluesky, Mastodon, Twitter,
etc.) include. This caused antiforgery validation errors on form submission.

The _isInteractive guard on the Save button is retained as a defensive
measure  it prevents clicks during the prerender gap before the SignalR
circuit connects, ensuring form submissions always go through SignalR
(where antiforgery tokens are not validated as HTTP POST).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Session: 2026-02-19-antiforgery-fix
Requested by: Jeffrey T. Fritz

Changes:
- Logged session to .ai-team/log/
- Merged decisions from inbox into decisions.md
- Deleted inbox files after merge
- Remove _isInteractive guard from LinkedIn config form  matches
  working provider pattern (Bluesky, Mastodon, Twitter)
- Change Users.razor rendermode to InteractiveServer with prerendering
  to fix blank admin page

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Session: 2026-02-19-globaljson-directive
Requested by: Jeffrey T. Fritz

Changes:
- logged session to .ai-team/log/2026-02-19-globaljson-directive.md
- merged global.json directive from inbox into decisions.md
- consolidated duplicate global.json directives into single definitive ruling
- propagated directive to all agent history files
- deleted merged inbox file

Jeff clarified that global.json rollForward=latestMinor is his intentional setting.
No agent may modify it under any circumstances. This prevents SDK version conflicts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
In .NET 10, UseStaticFiles() no longer serves Blazor framework files
like blazor.web.js. MapStaticAssets() is the required replacement,
fixing the 404 that broke all site interactivity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New Service_LinkedInOAuth.cs with admin-initiated OAuth endpoints:
- GET /api/linkedin/authorize  redirects to LinkedIn consent page
- GET /api/linkedin/callback  exchanges code for access/refresh tokens

Tokens are persisted to LinkedInConfiguration via IConfigureTagzApp.
CSRF protection via state parameter. Admin-only access enforced.

LinkedIn.Config.Ui.razor updated with Authorize button and OAuth
success/error toast notifications. Save button remains always-enabled
(no _isInteractive pattern).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Session: 2026-02-19-linkedin-oauth-and-blazor-fix
Requested by: Jeffrey T. Fritz

Changes:
- Logged session to .ai-team/log/2026-02-19-linkedin-oauth-and-blazor-fix.md
- Merged Sombra's LinkedIn OAuth decision from inbox
- Updated Sombra's history

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The r_organization_social scope requires Marketing Developer Platform
approval. Removing it fixes the generic LinkedIn auth error for apps
with standard w_member_social scope only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Allows providers to retrieve content from the past week on first poll
instead of only the last hour.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Added overflow:visible for modMessage articles to prevent the
moderation bar from being clipped. Increased bar height to 56px
and icon size to 2em for better usability.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
csharpfritz and others added 7 commits February 19, 2026 14:07
Replaced non-existent q=hashtag endpoint with q=author query that
fetches authenticated user's posts and filters client-side for
hashtag matches. Added ResolveMemberUrn() with caching. Updated
LinkedIn-Version header from 202401 to 202506 to fix 426 error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Added r_member_social to OAuth scopes (requires Community Management
API product on LinkedIn Developer portal). Added response body logging
for API errors and element count logging for successful responses.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add logging to LinkedIn OAuth authorize endpoint to debug
'Bummer something went wrong' errors from LinkedIn.
Logs scheme, host, redirect_uri, client_id prefix, and full auth URL.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
LinkedIn OAuth was failing because openid and profile scopes require
the 'Sign In with LinkedIn' product. Community Management API only
provides r_member_social and w_member_social.

Also switch ResolveMemberUrn from /v2/userinfo (OpenID) to /v2/me
(LinkedIn REST API) which works with r_member_social scope.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds /api/linkedin/debug-auth that returns the exact redirect_uri,
client_id, scopes, and detected host/scheme as JSON without
redirecting, to help diagnose 'Bummer' OAuth errors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ial scope

- Add RedirectUri field to LinkedInConfiguration for explicit OAuth redirect URL
- Update OAuth endpoints to use configured RedirectUri when set, with auto-detect fallback
- Show configured vs detected redirect_uri in debug-auth endpoint
- Remove w_member_social scope (only read access needed, write may need extra approval)
- Add RedirectUri to admin UI with helper text

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ging

- Changed hardcoded '202401' to LinkedInApiVersion constant ('202506')
- Added response body logging on /v2/me failure for diagnostics

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@csharpfritz csharpfritz changed the title Feature/linkedin event display feat: .NET 10 fixes, LinkedIn provider (WIP), moderation UI improvements Feb 19, 2026
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.

1 participant