mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-06 12:22:11 +00:00
feat: add localStorage persistence for repository selection (#127)
* Apply patch * Apply patch * Apply patch * fix: add getRepository func * cr * cr
This commit is contained in:
parent
001d38228a
commit
38fc7f9cbd
2 changed files with 138 additions and 1 deletions
|
|
@ -1,8 +1,47 @@
|
||||||
import { useState, useEffect, useRef, useCallback, useMemo } from "react";
|
import { useState, useEffect, useRef, useCallback, useMemo } from "react";
|
||||||
import { useQueryState } from "nuqs";
|
import { useQueryState } from "nuqs";
|
||||||
import { Repository, getRepositoryBranches, Branch } from "@/utils/github";
|
import { Repository, getRepositoryBranches, Branch } from "@/utils/github";
|
||||||
|
import { getRepository } from "@/utils/github";
|
||||||
import type { TargetRepository } from "@open-swe/shared/open-swe/types";
|
import type { TargetRepository } from "@open-swe/shared/open-swe/types";
|
||||||
|
|
||||||
|
const SELECTED_REPO_STORAGE_KEY = "selected-repository";
|
||||||
|
|
||||||
|
const saveRepositoryToLocalStorage = (repo: TargetRepository | null) => {
|
||||||
|
try {
|
||||||
|
if (repo) {
|
||||||
|
localStorage.setItem(SELECTED_REPO_STORAGE_KEY, JSON.stringify(repo));
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(SELECTED_REPO_STORAGE_KEY);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Failed to save repository to localStorage:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRepositoryFromLocalStorage = (): TargetRepository | null => {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(SELECTED_REPO_STORAGE_KEY);
|
||||||
|
if (stored) {
|
||||||
|
const parsed = JSON.parse(stored);
|
||||||
|
if (
|
||||||
|
parsed &&
|
||||||
|
typeof parsed.owner === "string" &&
|
||||||
|
typeof parsed.repo === "string"
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
owner: parsed.owner,
|
||||||
|
repo: parsed.repo,
|
||||||
|
// Don't restore branch from localStorage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Failed to retrieve repository from localStorage:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
interface UseGitHubAppReturn {
|
interface UseGitHubAppReturn {
|
||||||
// Installation and general state
|
// Installation and general state
|
||||||
isInstalled: boolean | null;
|
isInstalled: boolean | null;
|
||||||
|
|
@ -70,6 +109,9 @@ export function useGitHubApp(): UseGitHubAppReturn {
|
||||||
// Track if auto-selection has been attempted to prevent re-triggering
|
// Track if auto-selection has been attempted to prevent re-triggering
|
||||||
const hasAutoSelectedRef = useRef(false);
|
const hasAutoSelectedRef = useRef(false);
|
||||||
|
|
||||||
|
// Track if we've attempted to load from localStorage
|
||||||
|
const hasCheckedLocalStorageRef = useRef(false);
|
||||||
|
|
||||||
const selectedRepository = useMemo(() => {
|
const selectedRepository = useMemo(() => {
|
||||||
if (!selectedRepositoryParam) return null;
|
if (!selectedRepositoryParam) return null;
|
||||||
try {
|
try {
|
||||||
|
|
@ -104,6 +146,9 @@ export function useGitHubApp(): UseGitHubAppReturn {
|
||||||
const setSelectedRepository = useCallback(
|
const setSelectedRepository = useCallback(
|
||||||
(repo: TargetRepository | null) => {
|
(repo: TargetRepository | null) => {
|
||||||
setSelectedRepositoryParam(repo ? `${repo.owner}/${repo.repo}` : null);
|
setSelectedRepositoryParam(repo ? `${repo.owner}/${repo.repo}` : null);
|
||||||
|
// Persist to localStorage whenever repository is selected
|
||||||
|
saveRepositoryToLocalStorage(repo);
|
||||||
|
|
||||||
if (!repo) {
|
if (!repo) {
|
||||||
setSelectedBranchParam(null);
|
setSelectedBranchParam(null);
|
||||||
setBranches([]);
|
setBranches([]);
|
||||||
|
|
@ -227,6 +272,72 @@ export function useGitHubApp(): UseGitHubAppReturn {
|
||||||
checkInstallation();
|
checkInstallation();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!hasCheckedLocalStorageRef.current &&
|
||||||
|
!selectedRepository &&
|
||||||
|
!isLoading &&
|
||||||
|
!error &&
|
||||||
|
isInstalled === true &&
|
||||||
|
repositories.length > 0
|
||||||
|
) {
|
||||||
|
hasCheckedLocalStorageRef.current = true;
|
||||||
|
|
||||||
|
const storedRepo = getRepositoryFromLocalStorage();
|
||||||
|
if (storedRepo) {
|
||||||
|
const existsInResponse = repositories.some(
|
||||||
|
(repo) => repo.full_name === `${storedRepo.owner}/${storedRepo.repo}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existsInResponse) {
|
||||||
|
setSelectedRepository(storedRepo);
|
||||||
|
hasAutoSelectedRef.current = true;
|
||||||
|
} else {
|
||||||
|
const fetchSpecificRepo = async () => {
|
||||||
|
try {
|
||||||
|
const specificRepo = await getRepository(
|
||||||
|
storedRepo.owner,
|
||||||
|
storedRepo.repo,
|
||||||
|
);
|
||||||
|
if (specificRepo) {
|
||||||
|
setSelectedRepository(storedRepo);
|
||||||
|
hasAutoSelectedRef.current = true;
|
||||||
|
} else {
|
||||||
|
const firstRepo = repositories[0];
|
||||||
|
const targetRepo = {
|
||||||
|
owner: firstRepo.full_name.split("/")[0],
|
||||||
|
repo: firstRepo.full_name.split("/")[1],
|
||||||
|
};
|
||||||
|
setSelectedRepository(targetRepo);
|
||||||
|
saveRepositoryToLocalStorage(targetRepo);
|
||||||
|
hasAutoSelectedRef.current = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Failed to fetch specific repository:", error);
|
||||||
|
const firstRepo = repositories[0];
|
||||||
|
const targetRepo = {
|
||||||
|
owner: firstRepo.full_name.split("/")[0],
|
||||||
|
repo: firstRepo.full_name.split("/")[1],
|
||||||
|
};
|
||||||
|
setSelectedRepository(targetRepo);
|
||||||
|
saveRepositoryToLocalStorage(targetRepo);
|
||||||
|
hasAutoSelectedRef.current = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchSpecificRepo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
repositories,
|
||||||
|
selectedRepository,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
isInstalled,
|
||||||
|
setSelectedRepository,
|
||||||
|
]);
|
||||||
|
|
||||||
// Auto-select first repository on initial page load
|
// Auto-select first repository on initial page load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
|
|
@ -235,7 +346,8 @@ export function useGitHubApp(): UseGitHubAppReturn {
|
||||||
!isLoading && // Not loading repositories
|
!isLoading && // Not loading repositories
|
||||||
!error && // No error occurred
|
!error && // No error occurred
|
||||||
isInstalled === true && // GitHub App is installed
|
isInstalled === true && // GitHub App is installed
|
||||||
repositories.length > 0 // Repositories are available
|
repositories.length > 0 && // Repositories are available
|
||||||
|
hasCheckedLocalStorageRef.current // Only after localStorage check is complete
|
||||||
) {
|
) {
|
||||||
const firstRepo = repositories[0];
|
const firstRepo = repositories[0];
|
||||||
const targetRepo = {
|
const targetRepo = {
|
||||||
|
|
@ -243,6 +355,7 @@ export function useGitHubApp(): UseGitHubAppReturn {
|
||||||
repo: firstRepo.full_name.split("/")[1],
|
repo: firstRepo.full_name.split("/")[1],
|
||||||
};
|
};
|
||||||
setSelectedRepository(targetRepo);
|
setSelectedRepository(targetRepo);
|
||||||
|
saveRepositoryToLocalStorage(targetRepo);
|
||||||
hasAutoSelectedRef.current = true;
|
hasAutoSelectedRef.current = true;
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
|
@ -252,6 +365,7 @@ export function useGitHubApp(): UseGitHubAppReturn {
|
||||||
error,
|
error,
|
||||||
isInstalled,
|
isInstalled,
|
||||||
setSelectedRepository,
|
setSelectedRepository,
|
||||||
|
hasCheckedLocalStorageRef.current,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const refreshRepositories = async () => {
|
const refreshRepositories = async () => {
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,29 @@ export async function getRepositoryBranches(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a specific repository using OAuth access token
|
||||||
|
*/
|
||||||
|
export async function getRepository(owner: string, repo: string) {
|
||||||
|
const response = await fetch(
|
||||||
|
`${getBaseApiUrl()}github/proxy/repos/${owner}/${repo}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Accept: "application/vnd.github.v3+json",
|
||||||
|
"User-Agent": "OpenSWE-Agent",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json();
|
||||||
|
throw new Error(`Failed to fetch repository: ${JSON.stringify(errorData)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository interface representing GitHub repository data
|
* Repository interface representing GitHub repository data
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue