Skip to content

feat: add runtime PAT entry and user avatar#15

Merged
matejvasek merged 1 commit into
masterfrom
011-feat-github-pat-avatar-ver-C
Apr 28, 2026
Merged

feat: add runtime PAT entry and user avatar#15
matejvasek merged 1 commit into
masterfrom
011-feat-github-pat-avatar-ver-C

Conversation

@matejvasek
Copy link
Copy Markdown
Collaborator

@matejvasek matejvasek commented Apr 23, 2026

Summary

  • Replace compile-time PAT injection (webpack DefinePlugin) with runtime entry via a modal dialog, storing the PAT in sessionStorage
  • Add custom Octokit authStrategy that reads the token lazily per-request from sessionStorage
  • Add PatModal component (PAT entry with validation feedback) and UserAvatar component (connection status in page header)
  • Add shared useUserAvatar hook managing PAT state, sessionStorage persistence, and modal lifecycle
  • Extend EmptyState with hint text and disabled Create button when unauthenticated
  • Wire all three views (list, create, edit) with the avatar; list page auto-opens the modal and skips GitHub API calls when no PAT is stored

Test plan

  • 62 tests pass (yarn test), zero lint errors (yarn lint), zero type errors in src/ (npx tsc --noEmit)
  • Open list page without PAT: modal appears, Create button is disabled, no GitHub API calls
  • Enter valid PAT: modal closes, avatar shows username, function repos load
  • Enter invalid PAT: error alert shown in modal
  • Navigate to create/edit pages: avatar shows username (non-clickable)
  • Refresh page: PAT and user restored from sessionStorage, no modal
  • Verify no __GITHUB_PAT__ references remain in src/ or webpack.config.ts

Demo

Screencast.From.2026-04-27.22-09-24.mp4

🤖 Generated with Claude Code

@matejvasek matejvasek changed the title feat: add runtime PAT entry and user avatar feat: add runtime PAT entry and user avatar [draft C] Apr 23, 2026
Copy link
Copy Markdown
Collaborator

@twoGiants twoGiants left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First batch. Second batch follows after the next call 🤙 😸

Comment thread src/views/FunctionsListPage.tsx Outdated
Comment thread src/components/EmptyState.tsx Outdated
Comment thread src/components/EmptyState.tsx Outdated
Comment thread src/components/PatModal.tsx Outdated
Comment thread src/components/PatModal.tsx Outdated
Comment thread src/components/PatModal.tsx Outdated
Comment thread src/components/PatModal.tsx Outdated
Copy link
Copy Markdown
Collaborator

@twoGiants twoGiants left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next batch.

Comment thread src/components/EmptyState.tsx Outdated
Comment thread src/components/EmptyState.tsx
Comment thread src/components/UserAvatar.tsx Outdated
Comment thread src/services/types.ts Outdated
Comment thread src/hooks/useUserAvatar.ts Outdated
Copy link
Copy Markdown
Collaborator

@twoGiants twoGiants left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, last batch.

Comment thread docs/features.json Outdated
Comment thread docs/claude-progress.txt
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets update this after approval.

Comment thread src/components/UserAvatar.test.tsx Outdated
Comment thread src/components/UserAvatar.tsx Outdated
Comment thread src/components/UserAvatar.tsx Outdated
Comment thread src/components/PatModal.tsx Outdated
Comment thread src/services/source-control/GithubService.ts Outdated
Comment thread src/components/UserAvatar.tsx Outdated
Comment thread src/views/FunctionsListPage.tsx Outdated
Comment thread src/views/FunctionsListPage.tsx Outdated
matejvasek added a commit that referenced this pull request Apr 27, 2026
Simplify EmptyState to a single isCreateDisabled prop. When disabled,
show only the PAT hint text instead of both messages, and remove the
redundant Tooltip from the disabled button. Remove Tooltip from the
table-view Create button in FunctionsListPage as well.

Protect FunctionCreatePage against unauthenticated access via URL by
showing a warning alert and hiding the form when no PAT is stored.

Clean up UserAvatar: remove all useCallback wrappers, remove unused
logout, use guard pattern in readStoredUser, extract usePatModal hook,
shrink modal to small variant, disable Cancel and modal close while
validating. Extract shared errorMessage utility.

Remove stale question from features.json and update i18n keys.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Matej Vašek <matejvasek@gmail.com>
@matejvasek matejvasek changed the title feat: add runtime PAT entry and user avatar [draft C] feat: add runtime PAT entry and user avatar Apr 27, 2026
@matejvasek
Copy link
Copy Markdown
Collaborator Author

How it looks now:
https://github.com/user-attachments/assets/830d6903-1598-471e-a635-14c3b124d474

@matejvasek matejvasek requested a review from twoGiants April 27, 2026 20:14
@matejvasek matejvasek marked this pull request as ready for review April 27, 2026 20:23
Copy link
Copy Markdown
Collaborator

@twoGiants twoGiants left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, you and Claude did great! I would approve and merge.

Can you open a cleanup follow up with the fixes I mention in the comments below?

I'd like to have this PR in my branch for a full e2e flow test.

Comment on lines +159 to +176
it('disables Cancel button while validating', async () => {
const user = userEvent.setup();
let resolveConnect: () => void;
mockFetchUserInfo.mockReturnValue(
new Promise<void>((resolve) => {
resolveConnect = resolve;
}),
);

renderWithContext(<UserAvatar enableReconnect />);

await user.type(screen.getByLabelText('Personal Access Token'), 'ghp_slow');
await user.click(screen.getByRole('button', { name: 'Connect' }));

expect(screen.getByRole('button', { name: 'Cancel' })).toBeDisabled();

resolveConnect!();
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to resolve as we don't assert after resolution. You can remove resolveConnect and change the mockReturnValue to new Promise(() => {}). Makes the test a bit simpler.


export default function FunctionCreatePage() {
const { t } = useTranslation('plugin__console-functions-plugin');
const isConnected = !!sessionStorage.getItem(PAT_KEY);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is a hack. Use context for that. Same pattern as in the List page:

function useFunctionListPage(): {
  // ...
  isConnectedToForge: boolean;
} {
  const isConnectedToForge = useContext(ForgeConnectionContext).isActive;

Comment on lines +35 to +39
jest.mock('../components/UserAvatar', () => ({
UserAvatar: ({ enableReconnect }: { enableReconnect: boolean }) => (
<span data-testid="user-avatar">{enableReconnect ? 'reconnect' : 'no-reconnect'}</span>
),
}));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you have to mock the component here, only the services. If you can do without mocking it, do it without.

Comment on lines +41 to +45
jest.mock('../components/UserAvatar', () => ({
UserAvatar: ({ enableReconnect }: { enableReconnect: boolean }) => (
<span data-testid="user-avatar">{enableReconnect ? 'reconnect' : 'no-reconnect'}</span>
),
}));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't mock if you don't have to.

Replace compile-time PAT injection (webpack DefinePlugin, __GITHUB_PAT__)
with runtime entry via a modal dialog. PAT is stored in sessionStorage
and read lazily per-request through a cached Octokit getter.

UserAvatar component renders in ListPageHeader on every page. On the
list page it is clickable (opens PatModal to connect or reconnect); on
create and edit pages it is read-only. PatModal auto-opens on first
visit when no PAT is stored. Cancel and modal close are disabled while
validation is in flight.

ForgeConnectionProvider context lets FunctionsListPage react when the
user connects. EmptyState shows a PAT hint and disables the Create
button when not connected. FunctionCreatePage hides the form entirely
and shows a warning alert when accessed without a PAT.

GithubService refactored: single Octokit instance with a cached getter
that re-creates it only when the token changes. fetchUserInfo added to
SourceControlService interface (forge-agnostic). GitHubUser renamed to
ForgeUser.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Matej Vašek <matejvasek@gmail.com>
@matejvasek matejvasek force-pushed the 011-feat-github-pat-avatar-ver-C branch from d8e19cb to 1e51bf1 Compare April 28, 2026 13:55
@matejvasek matejvasek merged commit 0d93997 into master Apr 28, 2026
4 checks passed
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.

2 participants