Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement of repo collection [OSPP 2023] #713

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
0bf655a
feat: developed RepoCollection Button through antd component
andyhuang18 Aug 9, 2023
f1288e6
feat: implement LocalStorage,SetDefault,DeleteCollection,AddCurRepo f…
andyhuang18 Aug 15, 2023
b5b428a
style: yarn run prettier for index.scss
andyhuang18 Aug 15, 2023
e109bfa
feat: refactor view
andyhuang18 Sep 24, 2023
b540522
feat: implement storage feature
andyhuang18 Sep 26, 2023
ed4f400
feat: implement collection modal from popup button
andyhuang18 Sep 26, 2023
f1e3428
feat: implement collection modal using tabs and repo list beside
andyhuang18 Sep 26, 2023
0abec7d
feat: implement collection modal from repository dropdown click
andyhuang18 Sep 27, 2023
72dcdd0
feat: solve reopen modal problem
andyhuang18 Sep 27, 2023
914060c
feat: implement collection editor function (quick import need to be d…
andyhuang18 Sep 28, 2023
c20f2ba
feat: add judgment for whether there is initial data
andyhuang18 Sep 28, 2023
5335062
feat: create CollectionEditor component
andyhuang18 Sep 29, 2023
5cad16d
feat: use GitHub REST API to get repo and its description
andyhuang18 Sep 29, 2023
8f942b2
feat: implement CollectionEditor add and edit feature
andyhuang18 Sep 29, 2023
64e6436
feat: solve formatting and naming issues
andyhuang18 Oct 10, 2023
2f237d2
feat: refactor data structure
andyhuang18 Oct 11, 2023
0b90d6d
refactor: implement the collection button in the original GitHub styl…
andyhuang18 Oct 15, 2023
ba662b0
Merge branch 'repo-collection-final' into feat/repo-collection
tyn1998 Oct 15, 2023
19f3973
chore: update yarn.lock
andyhuang18 Oct 15, 2023
445c6cc
Merge branch 'repo-collection-final' into feat/repo-collection
andyhuang18 Oct 15, 2023
4b5e9c0
fix: update module import
andyhuang18 Oct 15, 2023
737e219
chore: update yarn.lock
andyhuang18 Oct 15, 2023
8bdb270
fix: duplicate rendering after tab switches
andyhuang18 Oct 15, 2023
4e3d2c0
fix: prevent duplicate finally
andyhuang18 Oct 15, 2023
d50520a
refactor: give CollectionModal a separate directory
andyhuang18 Oct 15, 2023
b96ec1b
refactor: CollectionList (30%) and AddToCollections (1%)
andyhuang18 Oct 15, 2023
a993260
feat: support basic interactions with CollectionButton
andyhuang18 Oct 15, 2023
56606f9
chore: disable charts-design
andyhuang18 Oct 15, 2023
81c3225
chore: allow overflow when there are many exsiting collections
andyhuang18 Oct 15, 2023
3fc9fa0
chore: add comments
andyhuang18 Oct 15, 2023
3989dad
chore: add manage button in AddToCollections
andyhuang18 Oct 15, 2023
24f6d52
refactor: a possible final directory
andyhuang18 Oct 15, 2023
fddb551
chore: support to open modal from CollectionList
andyhuang18 Oct 15, 2023
46791e0
fix: fix typo
andyhuang18 Oct 16, 2023
5d0c22d
feat: add confirm check before collection deletion
andyhuang18 Oct 16, 2023
6782d37
feat: set default key for Table and add Radio for quick import (User/…
andyhuang18 Oct 16, 2023
ec01eeb
refactor: move custom type definition to context.ts
andyhuang18 Oct 16, 2023
ec49e31
refactor: use constate to better manage locally global state
andyhuang18 Oct 16, 2023
8b3fc28
feat: store and useStore for repo-collection
andyhuang18 Oct 16, 2023
051d854
chore: update repo name after turbo:restore
andyhuang18 Oct 16, 2023
e544fd1
feat: replace some part with real data
andyhuang18 Oct 16, 2023
6944fd0
feat: AddToCollections(100%)
andyhuang18 Oct 18, 2023
f82543a
fix: a simple lock mechanism to prevent concurrent updates in store.ts
andyhuang18 Oct 18, 2023
868b4c0
feat: implement CollectionDisplayModal and rename filename
andyhuang18 Oct 20, 2023
ba2945c
refactor: rename view.tsx
andyhuang18 Oct 20, 2023
643309b
feat: add divider for footer in AddToCollections
andyhuang18 Oct 20, 2023
3668eb4
refactor: remove redundant initialization
andyhuang18 Oct 20, 2023
1e0bcf7
refactor: rollback and delete displayModal
andyhuang18 Oct 20, 2023
c25f58d
feat: implement of CollectionManageModal
andyhuang18 Oct 21, 2023
ced0589
feat: implement of CollectionEditor confirm
andyhuang18 Oct 21, 2023
20f3cdc
feat: use await/async function
andyhuang18 Oct 21, 2023
2ccd9fc
Merge branch 'feat/repo-collection' into feat/repo-collection
andyhuang18 Oct 21, 2023
b408207
chore: fix typo and add validator for duplicate collection name
andyhuang18 Oct 22, 2023
c23c26f
style: maximize the collection modal size
andyhuang18 Oct 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"description": "Hypertrons Chromium Extension",
"license": "Apache",
"engines": {
"node": ">=16.14"
"node": ">=18"
},
"scripts": {
"build": "cross-env NODE_ENV='production' BABEL_ENV='production' node utils/build.js",
Expand All @@ -28,6 +28,7 @@
"antd": "^5.9.1",
"buffer": "^6.0.3",
"colorthief": "^2.4.0",
"constate": "^3.3.2",
"delay": "^5.0.0",
"dom-loaded": "^3.0.0",
"echarts": "^5.3.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import { useRepoCollectionContext } from '../context';
import { Collection } from '../context/store';

import React, { useEffect, useState } from 'react';

const CheckListItem = (
collection: Collection,
onChange: (collectionId: Collection['id'], checked: boolean) => void,
checked: boolean
) => {
const handleChange = () => {
onChange(collection.id, checked);
};

return (
<div
key={collection.id}
className="form-checkbox mr-3 ml-6 ml-sm-5 mb-2 mt-0"
>
<label className="f5 text-normal">
<input
type="checkbox"
value={collection.id}
checked={checked}
onChange={handleChange}
/>
{collection.name}
</label>
</div>
);
};

/**
* The modal for quickly adding the current repository to existing collections (also for removing)
*/
export const AddToCollections = () => {
const {
currentRepositoryId,
currentRepositoryCollections,
allCollections,
updaters,
hideAddToCollections,
setHideAddToCollections,
setHideCollectionList,
setShowManageModal,
} = useRepoCollectionContext();

const [checkedCollectionIds, setCheckedCollectionIds] = useState<
Collection['id'][]
>([]);

const resetCheckboxes = () => {
setCheckedCollectionIds(currentRepositoryCollections.map((c) => c.id));
};

// reset checkboxes when currentRepositoryCollections changes
useEffect(() => {
resetCheckboxes();
}, [currentRepositoryCollections]);

const handleCheckChange = (
collectionId: Collection['id'],
checked: boolean
) => {
if (checked) {
setCheckedCollectionIds(
checkedCollectionIds.filter((id) => id !== collectionId)
);
} else {
setCheckedCollectionIds([...checkedCollectionIds, collectionId]);
}
};

const goToCollectionList = () => {
setHideAddToCollections(true);
setHideCollectionList(false);
};

const apply = () => {
// add/remove relations
const toAdd = checkedCollectionIds.filter(
(id) => !currentRepositoryCollections.some((c) => c.id === id)
);
const toRemove = currentRepositoryCollections.filter(
(c) => !checkedCollectionIds.includes(c.id)
);
toAdd &&
updaters.addRelations(
toAdd.map((id) => ({
collectionId: id,
repositoryId: currentRepositoryId,
}))
);
toRemove &&
updaters.removeRelations(
toRemove.map((c) => ({
collectionId: c.id,
repositoryId: currentRepositoryId,
}))
);

goToCollectionList();
};

const cancel = () => {
resetCheckboxes();

goToCollectionList();
};

const manage = () => {
// open modal to manage collections
setShowManageModal(true);
};

// if the ids of currentRepositoryCollections are the same as the ids of selectedCollectionIds, then the "Apply" button should be disabled
let isApplyDisabled: boolean;
if (currentRepositoryCollections.length !== checkedCollectionIds.length) {
isApplyDisabled = false;
} else {
isApplyDisabled = currentRepositoryCollections.every((c) =>
checkedCollectionIds.includes(c.id)
);
}

return (
<div
className="notifications-component-dialog"
hidden={hideAddToCollections}
>
<div className="SelectMenu-modal notifications-component-dialog-modal overflow-visible">
<header className="d-none d-sm-flex flex-items-start pt-1">
<button
className="border-0 px-2 pt-1 m-0 Link--secondary f5"
style={{ backgroundColor: 'transparent' }}
type="button"
onClick={goToCollectionList}
>
<svg
style={{ position: 'relative', left: '2px', top: '1px' }}
aria-hidden="true"
height="16"
viewBox="0 0 16 16"
version="1.1"
width="16"
className="octicon octicon-arrow-left"
>
<path d="M7.78 12.53a.75.75 0 0 1-1.06 0L2.47 8.28a.75.75 0 0 1 0-1.06l4.25-4.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L4.81 7h7.44a.75.75 0 0 1 0 1.5H4.81l2.97 2.97a.75.75 0 0 1 0 1.06Z"></path>
</svg>
</button>

<h1 className="pt-1 pr-4 pb-0 pl-0 f5 text-bold">
Add to collections
</h1>
</header>
<legend>
<div className="text-small color-fg-muted pt-0 pr-3 pb-3 pl-6 pl-sm-5 border-bottom mb-3">
Select collections you want the current repository to be added to.
</div>
</legend>
{/* Checklist */}
<div style={{ maxHeight: '200px', overflowY: 'auto' }}>
{allCollections.map((collection) => {
const checked = checkedCollectionIds.includes(collection.id);
return CheckListItem(collection, handleCheckChange, checked);
})}
</div>
{/* 3 buttons */}
<footer className="SelectMenu-footer">
<div className="pt-2 pb-3 px-3 d-flex flex-justify-start flex-row-reverse">
<button
disabled={isApplyDisabled}
className="btn-primary btn-sm btn ml-2"
onClick={apply}
>
Apply
</button>
<button className="btn-sm btn ml-2" onClick={cancel}>
Cancel
</button>
<button className="btn-sm btn" onClick={manage}>
Manage
</button>
</div>
</footer>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { useRepoCollectionContext } from '../context';
import { Collection } from '../context/store';

import React from 'react';

const ListItem = (
collection: Collection,
onClick: (collectionId: Collection['id']) => void
) => {
const handleClick = () => {
onClick(collection.id);
};

return (
<div
key={collection.id}
className="SelectMenu-item flex-items-start btn border-0 rounded-0"
onClick={handleClick}
>
<span className="text-small text-normal wb-break-all">
{collection.name}
</span>
</div>
);
};

/**
* The modal that shows the collections that the repo belongs to
*/
export const CollectionList = () => {
const {
currentRepositoryCollections,
hideCollectionList,
setHideAddToCollections,
setHideCollectionList,
setSelectedCollection,
setShowManageModal,
} = useRepoCollectionContext();

const handleCollectionClick = (collectionId: Collection['id']) => {
setSelectedCollection(collectionId);
setShowManageModal(true);
};

const goToAddToCollections = () => {
setHideAddToCollections(false);
setHideCollectionList(true);
};

return (
<div className="SelectMenu" hidden={hideCollectionList}>
<div className="SelectMenu-modal">
<button
className="SelectMenu-closeButton position-absolute right-0 m-2"
type="button"
data-toggle-for="collection-button-details"
>
<svg
aria-hidden="true"
height="16"
viewBox="0 0 16 16"
version="1.1"
width="16"
data-view-component="true"
className="octicon octicon-x"
>
<path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
</button>
<div className="d-flex flex-column flex-1 overflow-hidden">
<div className="SelectMenu-list">
{/* header */}
<header className="SelectMenu-header">
<h3 className="SelectMenu-title">
Contained in these collections
</h3>
<button
className="SelectMenu-closeButton"
type="button"
aria-label="Close menu"
data-toggle-for="collection-button-details"
>
<svg
aria-hidden="true"
height="16"
viewBox="0 0 16 16"
version="1.1"
width="16"
data-view-component="true"
className="octicon octicon-x"
>
<path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
</button>
</header>
{/* list */}
<div className="overflow-y-auto" style={{ maxHeight: '340px' }}>
{currentRepositoryCollections.map((collection) =>
ListItem(collection, handleCollectionClick)
)}
</div>
{/* footer */}
<footer className="SelectMenu-footer p-0 position-sticky">
<div
className="SelectMenu-item btn rounded-0 border-bottom-0 text-normal f6"
onClick={goToAddToCollections}
>
<svg
width="20"
aria-hidden="true"
height="16"
viewBox="0 0 16 16"
version="1.1"
data-view-component="true"
className="octicon octicon-plus mr-2 text-center"
>
<path d="M7.75 2a.75.75 0 0 1 .75.75V7h4.25a.75.75 0 0 1 0 1.5H8.5v4.25a.75.75 0 0 1-1.5 0V8.5H2.75a.75.75 0 0 1 0-1.5H7V2.75A.75.75 0 0 1 7.75 2Z"></path>
</svg>
Add to collections
</div>
</footer>
</div>
</div>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { CollectionList } from './CollectionList';
import { AddToCollections } from './AddToCollections';
import { useRepoCollectionContext } from '../context';

import React from 'react';
import { FundProjectionScreenOutlined } from '@ant-design/icons';

/**
* The "Collections" button, which is in the left of the "Edit Pins" button
*/
export const CollectionButton = () => {
const { currentRepositoryCollections } = useRepoCollectionContext();

return (
<div className="f5 position-relative">
<details
id="collection-button-details"
className="details-reset details-overlay f5 position-relative"
>
<summary className="btn-sm btn" role="button">
<span>
<FundProjectionScreenOutlined
style={{ fontSize: '14px', color: 'var(--color-fg-muted)' }}
/>
{' Collections '}
</span>
<span
data-pjax-replace="true" // not confirmed to be necessary
data-turbo-replace="true" // this one, either.
className="Counter"
>
{currentRepositoryCollections.length}
</span>
<span className="dropdown-caret"></span>
</summary>
<CollectionList />
<AddToCollections />
</details>
</div>
);
};
Loading