Skip to content

Commit e0138f6

Browse files
authored
experimental(dashboard): star repo feature (#7006)
1 parent 0ad80c2 commit e0138f6

File tree

6 files changed

+129
-3
lines changed

6 files changed

+129
-3
lines changed

packages/app/src/app/overmind/namespaces/dashboard/actions.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2035,3 +2035,48 @@ export const getRepositoryByDetails = async (
20352035
);
20362036
}
20372037
};
2038+
2039+
export const getStarredRepos = ({ state, effects }: Context) => {
2040+
const { dashboard, activeTeam } = state;
2041+
2042+
const persistedStarredRepos = effects.browser.storage.get(
2043+
`CSB/EXPERIMENTAL_STARRED/${activeTeam}`
2044+
) as Array<{ owner: string; name: string }>;
2045+
2046+
dashboard.starredRepos = persistedStarredRepos ?? [];
2047+
};
2048+
2049+
export const starRepo = (
2050+
{ state, effects }: Context,
2051+
{ owner, name }: { owner: string; name: string }
2052+
) => {
2053+
const { dashboard, activeTeam } = state;
2054+
2055+
const existingRepo = dashboard.starredRepos.find(
2056+
repo => repo.owner === owner && repo.name === name
2057+
);
2058+
if (existingRepo) {
2059+
return;
2060+
}
2061+
2062+
dashboard.starredRepos.push({ owner, name });
2063+
effects.browser.storage.set(
2064+
`CSB/EXPERIMENTAL_STARRED/${activeTeam}`,
2065+
dashboard.starredRepos
2066+
);
2067+
};
2068+
2069+
export const unstarRepo = (
2070+
{ state, effects }: Context,
2071+
{ owner, name }: { owner: string; name: string }
2072+
) => {
2073+
const { dashboard, activeTeam } = state;
2074+
2075+
dashboard.starredRepos = dashboard.starredRepos.filter(
2076+
repo => repo.owner !== owner || repo.name !== name
2077+
);
2078+
effects.browser.storage.set(
2079+
`CSB/EXPERIMENTAL_STARRED/${activeTeam}`,
2080+
dashboard.starredRepos
2081+
);
2082+
};

packages/app/src/app/overmind/namespaces/dashboard/state.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export type State = {
6868
contributions: Branch[] | null;
6969
/** v2 repositories (formerly projects) */
7070
repositories: Repository[] | null;
71+
starredRepos: Array<{ owner: string; name: string }>;
7172
};
7273

7374
export const DEFAULT_DASHBOARD_SANDBOXES: DashboardSandboxStructure = {
@@ -182,4 +183,5 @@ export const state: State = {
182183
),
183184
contributions: null,
184185
repositories: null,
186+
starredRepos: [],
185187
};

packages/app/src/app/pages/Dashboard/Components/Header/index.tsx

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import { useLocation } from 'react-router-dom';
33
import { useAppState, useActions } from 'app/overmind';
44
import { Stack, Text, Button, Icon } from '@codesandbox/components';
@@ -51,7 +51,7 @@ export const Header = ({
5151
selectedRepo,
5252
}: Props) => {
5353
const location = useLocation();
54-
const { modals } = useActions();
54+
const { modals, dashboard: dashboardActions } = useActions();
5555
const { dashboard } = useAppState();
5656

5757
const repositoriesListPage =
@@ -61,6 +61,17 @@ export const Header = ({
6161
'/repositories/github'
6262
);
6363

64+
const [experimentalMode] = useState(() => {
65+
return window.localStorage.getItem('CSB_DEBUG') === 'ENABLED';
66+
});
67+
68+
const selectedRepoIsStarred =
69+
selectedRepo &&
70+
dashboard.starredRepos.some(
71+
repo =>
72+
repo.owner === selectedRepo.owner && repo.name === selectedRepo.name
73+
);
74+
6475
return (
6576
<Stack
6677
align="center"
@@ -129,6 +140,33 @@ export const Header = ({
129140
</Button>
130141
)}
131142

143+
{repositoryBranchesPage && selectedRepo && experimentalMode && (
144+
<Button
145+
css={css({
146+
fontSize: 2,
147+
color: 'mutedForeground',
148+
padding: 0,
149+
width: 'auto',
150+
})}
151+
onClick={() => {
152+
if (selectedRepoIsStarred) {
153+
dashboardActions.unstarRepo(selectedRepo);
154+
} else {
155+
dashboardActions.starRepo(selectedRepo);
156+
}
157+
}}
158+
variant="link"
159+
>
160+
<Icon
161+
name="star"
162+
size={20}
163+
title="Star repo"
164+
css={css({ paddingRight: 2 })}
165+
/>
166+
{selectedRepoIsStarred ? 'Unstar' : 'Star'}
167+
</Button>
168+
)}
169+
132170
{repositoryBranchesPage &&
133171
dashboard.viewMode === 'list' &&
134172
selectedRepo && (

packages/app/src/app/pages/Dashboard/Components/Selection/ContextMenus/RepositoryMenu.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import { Menu } from '@codesandbox/components';
33
import { ProjectFragment as Repository } from 'app/graphql/types';
44
import {
55
dashboard,
66
v2DraftBranchUrl,
77
} from '@codesandbox/common/lib/utils/url-generator';
88
import { useHistory } from 'react-router-dom';
9+
import { useActions, useAppState } from 'app/overmind';
910
import { Context, MenuItem } from '../ContextMenu';
1011

1112
type RepositoryMenuProps = {
@@ -16,6 +17,12 @@ export const RepositoryMenu: React.FC<RepositoryMenuProps> = ({
1617
}) => {
1718
const { visible, setVisibility, position } = React.useContext(Context);
1819
const history = useHistory();
20+
const state = useAppState();
21+
const actions = useActions();
22+
23+
const [experimentalMode] = useState(() => {
24+
return window.localStorage.getItem('CSB_DEBUG') === 'ENABLED';
25+
});
1926

2027
const { repository: providerRepository } = repository;
2128
const repositoryUrl = dashboard.repository({
@@ -27,6 +34,12 @@ export const RepositoryMenu: React.FC<RepositoryMenuProps> = ({
2734
providerRepository.name
2835
);
2936

37+
const repositoryIsStarred = state.dashboard.starredRepos.find(
38+
repo =>
39+
repo.owner === providerRepository.owner &&
40+
repo.name === providerRepository.name
41+
);
42+
3043
const githubUrl = `https://github.com/${providerRepository.owner}/${providerRepository.name}`;
3144

3245
return (
@@ -50,6 +63,20 @@ export const RepositoryMenu: React.FC<RepositoryMenuProps> = ({
5063
Create branch
5164
</MenuItem>
5265

66+
{experimentalMode && (
67+
<MenuItem
68+
onSelect={() => {
69+
if (repositoryIsStarred) {
70+
actions.dashboard.unstarRepo(providerRepository);
71+
} else {
72+
actions.dashboard.starRepo(providerRepository);
73+
}
74+
}}
75+
>
76+
{repositoryIsStarred ? 'Unstar repository' : 'Star repository'}
77+
</MenuItem>
78+
)}
79+
5380
{/* TODO: Implement remove repository
5481
<Menu.Divider />
5582
<MenuItem

packages/app/src/app/pages/Dashboard/Content/routes/Repositories/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ export const RepositoriesPage = () => {
134134
</Text>
135135
)}
136136
</Notification>
137+
137138
<VariableGrid page={pageType} items={itemsToShow} />
138139
</SelectionProvider>
139140
);

packages/app/src/app/pages/Dashboard/Sidebar/index.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
6767

6868
React.useEffect(() => {
6969
actions.dashboard.getAllFolders();
70+
actions.dashboard.getStarredRepos();
7071
}, [actions.dashboard, state.activeTeam]);
7172

7273
React.useEffect(() => {
@@ -223,6 +224,18 @@ export const Sidebar: React.FC<SidebarProps> = ({
223224
path={dashboardUrls.repositories(activeTeam)}
224225
icon="repository"
225226
/>
227+
{dashboard.starredRepos.map(repo => (
228+
<RowItem
229+
name={repo.name}
230+
page="repositories"
231+
path={dashboardUrls.repository(repo)}
232+
icon="star"
233+
nestingLevel={1}
234+
style={{
235+
whiteSpace: 'nowrap',
236+
}}
237+
/>
238+
))}
226239
<Element marginTop={4} />
227240
<Element paddingX={7} paddingY={2}>
228241
<Text

0 commit comments

Comments
 (0)