Skip to content

Commit 07af2b8

Browse files
feat: Add recently visited repo at top of list
1 parent ee8df7c commit 07af2b8

File tree

10 files changed

+692
-7
lines changed

10 files changed

+692
-7
lines changed

src/shared/ListRepo/RepoTitleLink/RepoTitleLink.jsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import PropTypes from 'prop-types'
22

33
import AppLink from 'shared/AppLink'
4+
import { transformStringToLocalStorageKey } from 'shared/utils/transformStringToLocalStorageKey'
45
import Icon from 'ui/Icon'
56
import Label from 'ui/Label'
67

78
const getRepoIconName = ({ activated, isRepoPrivate, active }) =>
89
!activated && active ? 'ban' : isRepoPrivate ? 'lock-closed' : 'globe-alt'
910

10-
function RepoTitleLink({ repo, showRepoOwner, pageName, disabledLink }) {
11+
function RepoTitleLink({
12+
repo,
13+
showRepoOwner,
14+
pageName,
15+
disabledLink,
16+
isRecentlyVisited,
17+
}) {
1118
const options = {
1219
owner: repo.author.username,
1320
repo: repo.name,
@@ -49,6 +56,12 @@ function RepoTitleLink({ repo, showRepoOwner, pageName, disabledLink }) {
4956
pageName={pageName}
5057
options={options}
5158
className="flex items-center text-ds-gray-quinary hover:underline"
59+
onClick={() => {
60+
if (repo?.name && repo?.author?.username && repo?.active) {
61+
const key = transformStringToLocalStorageKey(repo.author.username)
62+
localStorage.setItem(`${key}_recently_visited`, repo.name)
63+
}
64+
}}
5265
>
5366
<Icon
5467
size="sm"
@@ -75,6 +88,11 @@ function RepoTitleLink({ repo, showRepoOwner, pageName, disabledLink }) {
7588
System generated
7689
</Label>
7790
)}
91+
{isRecentlyVisited && (
92+
<Label variant="plain" className="ml-2">
93+
recently visited
94+
</Label>
95+
)}
7896
</div>
7997
)
8098
}
@@ -93,6 +111,7 @@ RepoTitleLink.propTypes = {
93111
showRepoOwner: PropTypes.bool.isRequired,
94112
pageName: PropTypes.string.isRequired,
95113
disabledLink: PropTypes.bool.isRequired,
114+
isRecentlyVisited: PropTypes.bool,
96115
}
97116

98117
export default RepoTitleLink

src/shared/ListRepo/ReposTable/ReposTable.test.tsx

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { mockIsIntersecting } from 'react-intersection-observer/test-utils'
1717
import { MemoryRouter, Route } from 'react-router-dom'
1818

1919
import { ActiveContext } from 'shared/context'
20+
import { transformStringToLocalStorageKey } from 'shared/utils/transformStringToLocalStorageKey'
2021

2122
import ReposTable from './ReposTable'
2223

@@ -1083,4 +1084,96 @@ describe('ReposTable', () => {
10831084
expect(demoLink.length).toBe(0)
10841085
})
10851086
})
1087+
1088+
describe('handles recently visited repo', () => {
1089+
beforeEach(() => {
1090+
setup({})
1091+
localStorage.clear()
1092+
localStorage.setItem(
1093+
`${transformStringToLocalStorageKey('owner1')}_recently_visited`,
1094+
'gazebo'
1095+
)
1096+
server.use(
1097+
graphql.query('ReposForOwner', async (info) => {
1098+
const recentlyVisitedRepo = [
1099+
{
1100+
node: {
1101+
private: false,
1102+
activated: true,
1103+
author: {
1104+
username: 'owner1',
1105+
},
1106+
name: 'gazebo',
1107+
latestCommitAt: subDays(new Date(), 3).toISOString(),
1108+
coverageAnalytics: {
1109+
percentCovered: 0,
1110+
lines: 123,
1111+
},
1112+
active: true,
1113+
updatedAt: '2020-08-25T16:36:19.67986800:00',
1114+
repositoryConfig: null,
1115+
coverageEnabled: true,
1116+
bundleAnalysisEnabled: true,
1117+
},
1118+
},
1119+
]
1120+
1121+
const myRepos = [
1122+
{
1123+
node: {
1124+
private: false,
1125+
activated: false,
1126+
author: {
1127+
username: 'owner1',
1128+
},
1129+
name: 'Repo name 1',
1130+
latestCommitAt: subDays(new Date(), 3).toISOString(),
1131+
coverageAnalytics: {
1132+
percentCovered: 10,
1133+
lines: 123,
1134+
},
1135+
active: true,
1136+
updatedAt: '2020-08-25T16:36:19.67986800:00',
1137+
repositoryConfig: null,
1138+
coverageEnabled: true,
1139+
bundleAnalysisEnabled: false,
1140+
},
1141+
},
1142+
]
1143+
1144+
let reposToReturn = myRepos.filter(
1145+
(repo) =>
1146+
!info.variables.filters.term ||
1147+
repo.node.name.includes(info.variables.filters.term)
1148+
)
1149+
1150+
if (info.variables.filters.repoNames) {
1151+
reposToReturn = recentlyVisitedRepo
1152+
}
1153+
1154+
return HttpResponse.json({
1155+
data: {
1156+
owner: {
1157+
repositories: {
1158+
edges: reposToReturn,
1159+
pageInfo: {
1160+
hasNextPage: false,
1161+
endCursor: '3',
1162+
},
1163+
},
1164+
},
1165+
},
1166+
})
1167+
})
1168+
)
1169+
})
1170+
1171+
it('shows recently visited repo', async () => {
1172+
render(<ReposTable searchValue="" owner="owner1" />, {
1173+
wrapper: wrapper('', '/github/owner1', '/:provider/:owner'),
1174+
})
1175+
const recentlyVisitedRepo = await screen.findByText(/recently visited/)
1176+
expect(recentlyVisitedRepo).toBeInTheDocument()
1177+
})
1178+
})
10861179
})

src/shared/ListRepo/ReposTable/ReposTable.tsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import { useIsTeamPlan } from 'services/useIsTeamPlan'
2222
import { useOwner, useUser } from 'services/user'
2323
import { ActiveContext } from 'shared/context'
2424
import { DEMO_REPO, formatDemoRepos, isNotNull } from 'shared/utils/demo'
25+
import { getFilteredRecentlyVisitedRepo } from 'shared/utils/getFilteredRecentlyVisitedRepo'
26+
import { transformStringToLocalStorageKey } from 'shared/utils/transformStringToLocalStorageKey'
2527
import Icon from 'ui/Icon'
2628
import Spinner from 'ui/Spinner'
2729

@@ -159,6 +161,18 @@ const ReposTable = ({
159161
})
160162
)
161163

164+
const recentlyVisitedRepoName = localStorage.getItem(
165+
`${transformStringToLocalStorageKey(owner)}_recently_visited`
166+
)
167+
168+
const { data: recentlyVisitedRepoData } = useInfiniteQueryV5(
169+
ReposQueryOpts({
170+
provider,
171+
owner,
172+
repoNames: recentlyVisitedRepoName ? [recentlyVisitedRepoName] : [],
173+
})
174+
)
175+
162176
const isMyOwnerPage = currentUser?.user?.username === owner
163177

164178
const tableData = useMemo(() => {
@@ -180,13 +194,30 @@ const ReposTable = ({
180194
? formatDemoRepos(demoReposData, searchValue)
181195
: []
182196

183-
return [...demoRepos, ...repos]
197+
const filteredRecentlyVisitedRepo = getFilteredRecentlyVisitedRepo(
198+
recentlyVisitedRepoData,
199+
searchValue,
200+
owner
201+
)
202+
// only filter out the recently visited repo from the repos list if we are including it
203+
const filteredRepos = filteredRecentlyVisitedRepo
204+
? repos.filter((repo) => recentlyVisitedRepoName !== repo.name)
205+
: repos
206+
207+
return [
208+
...demoRepos,
209+
...(filteredRecentlyVisitedRepo ? [filteredRecentlyVisitedRepo] : []),
210+
...filteredRepos,
211+
]
184212
}, [
185213
reposData?.pages,
186214
demoReposData,
187215
searchValue,
188216
isMyOwnerPage,
189217
mayIncludeDemo,
218+
recentlyVisitedRepoData,
219+
recentlyVisitedRepoName,
220+
owner,
190221
])
191222

192223
useEffect(() => {
@@ -202,6 +233,7 @@ const ReposTable = ({
202233
owner,
203234
}),
204235
getCoreRowModel: getCoreRowModel(),
236+
// @ts-expect-error taking in both ReposQueryData and ReposTeamQueryData
205237
data: tableData,
206238
state: {
207239
sorting,

src/shared/ListRepo/ReposTable/getReposColumnsHelper.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createColumnHelper } from '@tanstack/react-table'
22

33
import { RepositoryResult } from 'services/repos/ReposQueryOpts'
44
import { formatTimeToNow } from 'shared/utils/dates'
5+
import { transformStringToLocalStorageKey } from 'shared/utils/transformStringToLocalStorageKey'
56
import TotalsNumber from 'ui/TotalsNumber'
67

78
import NoRepoCoverage from './NoRepoCoverage'
@@ -21,6 +22,9 @@ export const getReposColumnsHelper = ({
2122
const columnHelper = createColumnHelper<
2223
RepositoryResult & { isDemo?: boolean }
2324
>()
25+
const recentlyVisitedRepoName = localStorage.getItem(
26+
`${transformStringToLocalStorageKey(owner)}_recently_visited`
27+
)
2428
const nameColumn = columnHelper.accessor('name', {
2529
header: 'Name',
2630
id: 'name',
@@ -42,6 +46,9 @@ export const getReposColumnsHelper = ({
4246
showRepoOwner={!owner}
4347
pageName={pageName}
4448
disabledLink={!isCurrentUserPartOfOrg && !repo?.active}
49+
isRecentlyVisited={
50+
!!recentlyVisitedRepoName && recentlyVisitedRepoName === repo?.name
51+
}
4552
/>
4653
)
4754
},

0 commit comments

Comments
 (0)