mirror of
https://github.com/jlengrand/open-source-mac-os-apps.git
synced 2026-03-10 08:31:19 +00:00
- packages updated
- models refactored - updated the eslint config - updated the project folders structure - prepared small TODO's list for project improving - removed unnecessary packages - implemented small components updates
This commit is contained in:
@@ -1,26 +1,36 @@
|
||||
{
|
||||
"extends": ["react-app", "airbnb", "prettier", "prettier/react"],
|
||||
"extends": [
|
||||
"react-app",
|
||||
"airbnb",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"plugins": ["prettier"],
|
||||
"rules": {
|
||||
"max-len": ["error", { "code": 100 }],
|
||||
"prefer-promise-reject-errors": ["off"],
|
||||
"react/jsx-filename-extension": ["off"],
|
||||
"react/prop-types": ["off"],
|
||||
"no-return-assign": ["off"],
|
||||
"no-undef": "off",
|
||||
"no-use-before-define": "off",
|
||||
|
||||
"camelcase": "off",
|
||||
|
||||
"no-shadow": "off",
|
||||
"@typescript-eslint/no-shadow": ["error"],
|
||||
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["warn"],
|
||||
|
||||
"@typescript-eslint/no-explicit-any": "off", // FIXME: remove this rule
|
||||
|
||||
"react/prop-types": [0],
|
||||
"react/jsx-filename-extension": [1, { "extensions": ["ts", "tsx"] }],
|
||||
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"ignorePackages",
|
||||
{
|
||||
"js": "never",
|
||||
"jsx": "never",
|
||||
"ts": "never",
|
||||
"tsx": "never"
|
||||
}
|
||||
],
|
||||
|
||||
"prettier/prettier": "error"
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
|
||||
3
page-src/.vscode/settings.json
vendored
3
page-src/.vscode/settings.json
vendored
@@ -3,5 +3,6 @@
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.stylelint": true
|
||||
}
|
||||
},
|
||||
"cSpell.words": ["browserslist", "camelcase", "craco", "middlewares", "predeploy"]
|
||||
}
|
||||
|
||||
@@ -3,24 +3,25 @@
|
||||
"version": "0.1.0",
|
||||
"homepage": "http://serhii-londar.github.io/open-source-mac-os-apps",
|
||||
"private": false,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.31",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.0",
|
||||
"@fortawesome/react-fontawesome": "^0.1.11",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
"node-sass": "^4.14.1",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@testing-library/user-event": "^13.2.1",
|
||||
"node-sass": "^6.0.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-redux": "^7.2.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "3.4.3",
|
||||
"react-scripts": "4.0.3",
|
||||
"redux": "^4.0.5",
|
||||
"redux-devtools-extension": "^2.13.8",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-saga": "^1.1.3",
|
||||
"typescript": "~3.7.2"
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
@@ -47,23 +48,23 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.0.0",
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/react": "^16.9.0",
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.7.1",
|
||||
"@types/react": "^17.0.19",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@types/react-redux": "^7.1.9",
|
||||
"@types/react-router-dom": "^5.1.5",
|
||||
"@types/redux-logger": "^3.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^4.29.3",
|
||||
"@typescript-eslint/parser": "^4.29.3",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-airbnb": "^18.2.0",
|
||||
"eslint-config-prettier": "^6.12.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.22.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"eslint-plugin-react": "^7.21.2",
|
||||
"gh-pages": "^3.1.0",
|
||||
"prettier": "^2.1.2",
|
||||
"prettier-eslint": "^11.0.0"
|
||||
"prettier": "^2.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
@import "./shared/css/colors.scss";
|
||||
@import "./components/shared/css/colors.scss";
|
||||
|
||||
$sidebar-width: 300px;
|
||||
|
||||
.container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.categories-list {
|
||||
width: $sidebar-width;
|
||||
background-color: $charcoal;
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: $white;
|
||||
margin-left: $sidebar-width;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { FC, useEffect } from "react";
|
||||
|
||||
import { useDispatch } from "react-redux";
|
||||
import { Redirect, Route, BrowserRouter as Router, Switch } from "react-router-dom";
|
||||
import { fetchAllCategoriesAction } from "./actions/category";
|
||||
@@ -8,7 +9,7 @@ import styles from "./App.module.scss";
|
||||
|
||||
import Application from "./components/Application/Application";
|
||||
import CategoriesList from "./components/CategoriesList/CategoriesList";
|
||||
import Categoty from "./components/Category/Category";
|
||||
import Category from "./components/Category/Category";
|
||||
import Home from "./components/Home/Home";
|
||||
import PathBuilder from "./services/PathBuilder";
|
||||
|
||||
@@ -18,6 +19,15 @@ enum APP_ROUTES {
|
||||
APPLICATION = "/:category/:application",
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
- [ ]: setup husky
|
||||
- [ ]: setup stylelint
|
||||
- [ ]: use redux toolkit
|
||||
- [ ]: revisit the API service. use axios
|
||||
- [ ]: prepare config for PATH alias (craco)
|
||||
- [ ]: prepare some model for errors
|
||||
*/
|
||||
|
||||
const App: FC = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@@ -35,7 +45,7 @@ const App: FC = () => {
|
||||
<section className={styles.content}>
|
||||
<Switch>
|
||||
<Route exact path={PathBuilder.build(APP_ROUTES.HOME)} component={Home} />
|
||||
<Route exact path={PathBuilder.build(APP_ROUTES.CATEGORY)} component={Categoty} />
|
||||
<Route exact path={PathBuilder.build(APP_ROUTES.CATEGORY)} component={Category} />
|
||||
<Route exact path={PathBuilder.build(APP_ROUTES.APPLICATION)} component={Application} />
|
||||
|
||||
<Route path="*">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RawApplication } from "../models/application";
|
||||
import Application from "../models/application";
|
||||
|
||||
export enum ApplicationActions {
|
||||
FETCH_ALL = "APPLICATIONS_FETCH_ALL",
|
||||
@@ -21,10 +21,9 @@ export const fetchAllApplicationsAction = (): FetchAllApplicationsAction => ({
|
||||
|
||||
// ---------------------------------
|
||||
|
||||
export type FetchAllApplicationsSucceedPayload = RawApplication[];
|
||||
export type FetchAllApplicationsSucceedAction = ApplicationAction<
|
||||
FetchAllApplicationsSucceedPayload
|
||||
>;
|
||||
export type FetchAllApplicationsSucceedPayload = Application[];
|
||||
export type FetchAllApplicationsSucceedAction =
|
||||
ApplicationAction<FetchAllApplicationsSucceedPayload>;
|
||||
|
||||
export const fetchAllApplicationsSucceedAction = (
|
||||
payload: FetchAllApplicationsSucceedPayload,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RawCategory } from "../models/category";
|
||||
import Category from "../models/category";
|
||||
|
||||
export enum CategoryActions {
|
||||
FETCH_ALL = "CATEGORY_FETCH_ALL",
|
||||
@@ -21,7 +21,7 @@ export const fetchAllCategoriesAction = (): FetchAllCategoriesAction => ({
|
||||
|
||||
// ---------------------------------
|
||||
|
||||
export type FetchAllCategoriesSucceedPayload = RawCategory[];
|
||||
export type FetchAllCategoriesSucceedPayload = Category[];
|
||||
export type FetchAllCategoriesSucceedAction = CategoryAction<FetchAllCategoriesSucceedPayload>;
|
||||
|
||||
export const fetchAllCategoriesSucceedAction = (
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import React, { FC } from "react";
|
||||
|
||||
const Application: FC = () => {
|
||||
return <div>Application</div>;
|
||||
};
|
||||
const Application: FC = () => <div>Application</div>;
|
||||
|
||||
export default Application;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../shared/css/colors.scss";
|
||||
@import "../shared/css/colors.scss";
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
|
||||
@@ -14,7 +14,7 @@ import styles from "./CategoriesList.module.scss";
|
||||
import { ApplicationsState } from "../../reducers/application";
|
||||
import Application from "../../models/application";
|
||||
|
||||
const icon = require("../../../assets/app-icon.png");
|
||||
import icon from "../../../assets/app_icon.png";
|
||||
|
||||
const CategoriesList: FC = () => {
|
||||
const categoriesState = useSelector<RootState, CategoriesState>((state) => state.categories);
|
||||
@@ -22,13 +22,12 @@ const CategoriesList: FC = () => {
|
||||
(state) => state.applications,
|
||||
);
|
||||
|
||||
const appsInCategory = (category: Category): number => {
|
||||
return applicationsState.data.reduce(
|
||||
const appsInCategory = (category: Category): number =>
|
||||
applicationsState.data.reduce(
|
||||
(acc: number, application: Application) =>
|
||||
acc + application.categories.filter((c: Category) => c.id === category.id).length,
|
||||
0,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
@@ -47,18 +46,16 @@ const CategoriesList: FC = () => {
|
||||
<li className={styles.item}>
|
||||
<CategoryItem badge={false} path="/" label="Home" className={styles.link} />
|
||||
</li>
|
||||
{categoriesState.data.map((category: Category) => {
|
||||
return (
|
||||
<li className={styles.item} key={category.id}>
|
||||
<CategoryItem
|
||||
path={`/${category.shortName}`}
|
||||
label={category.name}
|
||||
className={styles.link}
|
||||
items={applicationsState.loading ? "..." : appsInCategory(category)}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
{categoriesState.data.map((category: Category) => (
|
||||
<li className={styles.item} key={category.id}>
|
||||
<CategoryItem
|
||||
path={`/${category.shortName}`}
|
||||
label={category.name}
|
||||
className={styles.link}
|
||||
items={applicationsState.loading ? "..." : appsInCategory(category)}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../../shared/css/colors.scss";
|
||||
@import "../../shared/css/colors.scss";
|
||||
|
||||
$top-bottom-padding: 2px;
|
||||
$left-padding: 9px;
|
||||
|
||||
@@ -19,18 +19,16 @@ const CategoryItem: FC<CategoryItemProps> = ({
|
||||
items = 0,
|
||||
className = "",
|
||||
badge = true,
|
||||
}) => {
|
||||
return (
|
||||
<NavLink
|
||||
activeClassName={styles.active}
|
||||
to={PathBuilder.build(path)}
|
||||
className={`${styles.container} ${className}`}
|
||||
exact
|
||||
>
|
||||
<span>{label}</span>
|
||||
{badge && <span className={styles.badge}>{items}</span>}
|
||||
</NavLink>
|
||||
);
|
||||
};
|
||||
}) => (
|
||||
<NavLink
|
||||
activeClassName={styles.active}
|
||||
to={PathBuilder.build(path)}
|
||||
className={`${styles.container} ${className}`}
|
||||
exact
|
||||
>
|
||||
<span>{label}</span>
|
||||
{badge && <span className={styles.badge}>{items}</span>}
|
||||
</NavLink>
|
||||
);
|
||||
|
||||
export default CategoryItem;
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
import React, { FC, useMemo } from "react";
|
||||
|
||||
import { useSelector } from "react-redux";
|
||||
import { useParams } from "react-router";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import styles from "./Category.module.scss";
|
||||
|
||||
import ApplicationCard from "../../shared/components/ApplicatioinCard/ApplicationCard";
|
||||
import ApplicationCard from "../shared/ApplicatioinCard/ApplicationCard";
|
||||
import Application from "../../models/application";
|
||||
import Category from "../../models/category";
|
||||
import TCategory from "../../models/category";
|
||||
import { RootState } from "../../reducers";
|
||||
import { ApplicationsState } from "../../reducers/application";
|
||||
|
||||
const Categoty: FC = () => {
|
||||
import styles from "./Category.module.scss";
|
||||
|
||||
const Category: FC = () => {
|
||||
const params = useParams<{ category: string }>();
|
||||
|
||||
const applicationsState = useSelector<RootState, ApplicationsState>(
|
||||
(state) => state.applications,
|
||||
);
|
||||
|
||||
const appsInCategory = useMemo<Application[]>(() => {
|
||||
return applicationsState.data.reduce(
|
||||
(acc: Application[], application: Application) => [
|
||||
...acc,
|
||||
...(application.categories.some((c: Category) => c.shortName === params.category)
|
||||
? [application]
|
||||
: []),
|
||||
],
|
||||
[],
|
||||
);
|
||||
}, [params, applicationsState]);
|
||||
const appsInCategory = useMemo<Application[]>(
|
||||
() =>
|
||||
applicationsState.data.reduce(
|
||||
(acc: Application[], application: Application) => [
|
||||
...acc,
|
||||
...(application.categories.some((c: TCategory) => c.shortName === params.category)
|
||||
? [application]
|
||||
: []),
|
||||
],
|
||||
[],
|
||||
),
|
||||
[params, applicationsState],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.header}>Header</div>
|
||||
<div className={styles.applications}>
|
||||
{appsInCategory.map((app: Application) => {
|
||||
return (
|
||||
<div className={styles.item} key={app.id}>
|
||||
<ApplicationCard application={app} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{appsInCategory.map((app: Application) => (
|
||||
<div className={styles.item} key={app.id}>
|
||||
<ApplicationCard application={app} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Categoty;
|
||||
export default Category;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import React, { FC } from "react";
|
||||
|
||||
const Home: FC = () => {
|
||||
return <div>Home</div>;
|
||||
};
|
||||
const Home: FC = () => <div>Home</div>;
|
||||
|
||||
export default Home;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../css/colors.scss";
|
||||
@import "../css/colors.scss";
|
||||
|
||||
$height: 120px;
|
||||
$height-hover: 200px;
|
||||
@@ -20,6 +20,33 @@ $height-hover: 200px;
|
||||
width: $height-hover;
|
||||
height: $height-hover;
|
||||
}
|
||||
|
||||
.right {
|
||||
grid-template-rows: 30px 1fr 60px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
grid-template-rows: repeat(3, 1fr);
|
||||
grid-template-columns: none;
|
||||
height: 60px;
|
||||
justify-content: flex-end;
|
||||
|
||||
.label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.stars,
|
||||
.forks,
|
||||
.watchers {
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 70px
|
||||
}
|
||||
|
||||
.forks {
|
||||
padding: 3px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,9 +131,21 @@ $height-hover: 200px;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
background: rgb(255,255,255);
|
||||
background: linear-gradient(0deg, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
@@ -118,7 +157,33 @@ $height-hover: 200px;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
font-size: 14px;
|
||||
justify-items: center;
|
||||
font-size: 13px;
|
||||
align-items: center;
|
||||
padding: 0 5px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.icon {
|
||||
width: 15px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: none;
|
||||
text-align: end;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.stars,
|
||||
.forks,
|
||||
.watchers {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.forks {
|
||||
padding: 0 3px;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,19 @@
|
||||
import React, { FC, useCallback, useState } from "react";
|
||||
import { faCodeBranch, faEye, faStar } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import React, { FC, useCallback, useState } from "react";
|
||||
import Application from "../../../models/application";
|
||||
|
||||
import styles from "./ApplicationCard.module.scss";
|
||||
|
||||
const icon = require("../../../../assets/placeholdersvg.svg");
|
||||
import icon from "../../../../assets/placeholder_svg.svg";
|
||||
|
||||
type ApplicationCard = {
|
||||
type ApplicationCardProps = {
|
||||
application: Application;
|
||||
};
|
||||
|
||||
const ApplicationCard: FC<ApplicationCard> = ({ application }) => {
|
||||
// TODO: split component
|
||||
|
||||
const ApplicationCard: FC<ApplicationCardProps> = ({ application }) => {
|
||||
const { title } = application;
|
||||
|
||||
const [animationPosition, setAnimationPosition] = useState<string>("");
|
||||
@@ -48,14 +50,32 @@ const ApplicationCard: FC<ApplicationCard> = ({ application }) => {
|
||||
<p>{application.description}</p>
|
||||
</div>
|
||||
<div className={styles.footer}>
|
||||
<span>
|
||||
<FontAwesomeIcon icon={faStar} /> {application.stars}
|
||||
<span className={styles.stars}>
|
||||
<span className={styles.label}>Stars</span>
|
||||
<span className={styles.data}>
|
||||
<span className={styles.icon}>
|
||||
<FontAwesomeIcon icon={faStar} color="#ffca28" />
|
||||
</span>
|
||||
{application.stars || "-"}
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
<FontAwesomeIcon icon={faCodeBranch} /> {application.forks}
|
||||
<span className={styles.forks}>
|
||||
<span className={styles.label}>Forks</span>
|
||||
<span className={styles.data}>
|
||||
<span className={styles.icon}>
|
||||
<FontAwesomeIcon icon={faCodeBranch} color="#aaa" />
|
||||
</span>
|
||||
{application.forks || "-"}
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
<FontAwesomeIcon icon={faEye} /> {application.watchers}
|
||||
<span className={styles.watchers}>
|
||||
<span className={styles.label}>Watchers</span>
|
||||
<span className={styles.data}>
|
||||
<span className={styles.icon}>
|
||||
<FontAwesomeIcon icon={faEye} color="#007eff" />
|
||||
</span>
|
||||
{application.watchers || "-"}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,8 +1,8 @@
|
||||
import Category, { RawCategory } from "./category";
|
||||
import Language, { RawLanguage } from "./language";
|
||||
import Screenshot, { RawScreenshot } from "./screenshot";
|
||||
import Category from "./category";
|
||||
import Language from "./language";
|
||||
import Screenshot from "./screenshot";
|
||||
|
||||
export type RawApplication = {
|
||||
export type Application = {
|
||||
id: string;
|
||||
forks: number;
|
||||
watchers: number;
|
||||
@@ -11,71 +11,9 @@ export type RawApplication = {
|
||||
title: string;
|
||||
description: string;
|
||||
icon_url: string;
|
||||
languages: RawLanguage[];
|
||||
categories: RawCategory[];
|
||||
screenshots: RawScreenshot[];
|
||||
languages: Language[];
|
||||
categories: Category[];
|
||||
screenshots: Screenshot[];
|
||||
};
|
||||
|
||||
class Application {
|
||||
public id: string | undefined;
|
||||
|
||||
public forks: number = 0;
|
||||
|
||||
public watchers: number = 0;
|
||||
|
||||
public repo_url: string = "";
|
||||
|
||||
public stars: number = 0;
|
||||
|
||||
public title: string = "";
|
||||
|
||||
public description: string = "";
|
||||
|
||||
public icon_url: string = "";
|
||||
|
||||
public languages: Language[] = [];
|
||||
|
||||
public categories: Category[] = [];
|
||||
|
||||
public screenshots: Screenshot[] = [];
|
||||
|
||||
constructor(init?: RawApplication & Application) {
|
||||
if (init) {
|
||||
this.id = init.id;
|
||||
this.forks = init.forks;
|
||||
this.watchers = init.watchers;
|
||||
this.repo_url = init.repo_url;
|
||||
this.stars = init.stars;
|
||||
this.title = init.title;
|
||||
this.description = init.description;
|
||||
this.icon_url = init.icon_url;
|
||||
this.languages = init.languages.map(
|
||||
(language: Language | RawLanguage) => new Language(language),
|
||||
);
|
||||
this.categories = init.categories.map(
|
||||
(rawCategory: RawCategory) => new Category(rawCategory),
|
||||
);
|
||||
this.screenshots = init.screenshots.map(
|
||||
(rawScreenshot: RawScreenshot) => new Screenshot(rawScreenshot),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public toJson = () => {
|
||||
return {
|
||||
id: this.id,
|
||||
forks: this.forks,
|
||||
watchers: this.watchers,
|
||||
repo_url: this.repo_url,
|
||||
stars: this.stars,
|
||||
title: this.title,
|
||||
description: this.description,
|
||||
icon_url: this.icon_url,
|
||||
languages: this.languages.map((language: Language) => language.toJson()),
|
||||
categories: this.categories.map((category: Category) => category.toJson()),
|
||||
screenshots: this.screenshots.map((screenshot: Screenshot) => screenshot.toJson()),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default Application;
|
||||
|
||||
@@ -1,27 +1,7 @@
|
||||
export type RawCategory = {
|
||||
id?: string;
|
||||
type Category = {
|
||||
id: string;
|
||||
name: string;
|
||||
shortName: string;
|
||||
};
|
||||
|
||||
class Category {
|
||||
public id: string | undefined;
|
||||
|
||||
public name: string = "";
|
||||
|
||||
public shortName: string = "";
|
||||
|
||||
constructor(init?: RawCategory | Category) {
|
||||
if (init) {
|
||||
this.id = init.id;
|
||||
this.name = init.name;
|
||||
this.shortName = init.shortName;
|
||||
}
|
||||
}
|
||||
|
||||
public toJson = () => {
|
||||
return { id: this.id, name: this.name, shortName: this.shortName };
|
||||
};
|
||||
}
|
||||
|
||||
export default Category;
|
||||
|
||||
@@ -1,23 +1,6 @@
|
||||
export type RawLanguage = {
|
||||
id?: string;
|
||||
export type Language = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
class Language {
|
||||
public id: string | undefined;
|
||||
|
||||
public name: string = "";
|
||||
|
||||
constructor(init?: RawLanguage | Language) {
|
||||
if (init) {
|
||||
this.id = init.id;
|
||||
this.name = init.name;
|
||||
}
|
||||
}
|
||||
|
||||
public toJson = () => {
|
||||
return { id: this.id, name: this.name };
|
||||
};
|
||||
}
|
||||
|
||||
export default Language;
|
||||
|
||||
@@ -1,23 +1,6 @@
|
||||
export type RawScreenshot = {
|
||||
id?: string;
|
||||
export type Screenshot = {
|
||||
id: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
class Screenshot {
|
||||
public id: string | undefined;
|
||||
|
||||
public url: string = "";
|
||||
|
||||
constructor(init?: Screenshot | RawScreenshot) {
|
||||
if (init) {
|
||||
this.id = init.id;
|
||||
this.url = init.url;
|
||||
}
|
||||
}
|
||||
|
||||
public toJson = () => {
|
||||
return { id: this.id, url: this.url };
|
||||
};
|
||||
}
|
||||
|
||||
export default Screenshot;
|
||||
|
||||
@@ -10,7 +10,7 @@ import Application from "../models/application";
|
||||
|
||||
export type ApplicationsState = {
|
||||
loading: boolean;
|
||||
error: any;
|
||||
error: unknown;
|
||||
data: Application[];
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ export const applications = (
|
||||
| FetchAllApplicationsAction
|
||||
| FetchAllApplicationsSucceedAction
|
||||
| FetchAllApplicationsFailedAction,
|
||||
) => {
|
||||
): ApplicationsState => {
|
||||
switch (action.type) {
|
||||
case ApplicationActions.FETCH_ALL:
|
||||
return FetchAllApplications(state);
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
FetchAllCategoriesSucceedPayload,
|
||||
FetchAllCategoriesFailedAction,
|
||||
} from "../actions/category";
|
||||
import Category, { RawCategory } from "../models/category";
|
||||
import Category from "../models/category";
|
||||
|
||||
export type CategoriesState = {
|
||||
loading: boolean;
|
||||
@@ -26,7 +26,7 @@ const FetchAllCategoriesSucceed = (
|
||||
state: CategoriesState,
|
||||
payload: FetchAllCategoriesSucceedPayload,
|
||||
) => {
|
||||
const categories = payload.map((rawCategory: RawCategory) => new Category(rawCategory));
|
||||
const categories = payload;
|
||||
|
||||
return { ...state, data: categories, error: null, loading: false };
|
||||
};
|
||||
@@ -47,7 +47,7 @@ export const categories = (
|
||||
| FetchAllCategoriesAction
|
||||
| FetchAllCategoriesSucceedAction
|
||||
| FetchAllCategoriesFailedAction,
|
||||
) => {
|
||||
): CategoriesState => {
|
||||
switch (action.type) {
|
||||
case CategoryActions.FETCH_ALL:
|
||||
return FetchAllCategories(state);
|
||||
|
||||
@@ -5,19 +5,19 @@ import {
|
||||
fetchAllApplicationsFailedAction,
|
||||
fetchAllApplicationsSucceedAction,
|
||||
} from "../../actions/application";
|
||||
import { RawApplication } from "../../models/application";
|
||||
import Application from "../../models/application";
|
||||
|
||||
import { FetchAllApplications } from "../../services/Api";
|
||||
|
||||
function* fetchAll() {
|
||||
try {
|
||||
const applications: RawApplication[] = yield call(FetchAllApplications);
|
||||
const applications: Application[] = yield call(FetchAllApplications);
|
||||
yield put(fetchAllApplicationsSucceedAction(applications));
|
||||
} catch (error) {
|
||||
yield put(fetchAllApplicationsFailedAction(error));
|
||||
}
|
||||
}
|
||||
|
||||
export default function* applicationWatchers() {
|
||||
export default function* applicationWatchers(): Generator {
|
||||
yield takeLatest(ApplicationActions.FETCH_ALL, fetchAll);
|
||||
}
|
||||
|
||||
@@ -5,19 +5,19 @@ import {
|
||||
fetchAllCategoriesSucceedAction,
|
||||
fetchCategoryFailedAction,
|
||||
} from "../../actions/category";
|
||||
import { RawCategory } from "../../models/category";
|
||||
import Category from "../../models/category";
|
||||
|
||||
import { FetchAllCategories } from "../../services/Api";
|
||||
|
||||
function* fetchAll() {
|
||||
try {
|
||||
const categoies: RawCategory[] = yield call(FetchAllCategories);
|
||||
yield put(fetchAllCategoriesSucceedAction(categoies));
|
||||
const categories: Category[] = yield call(FetchAllCategories);
|
||||
yield put(fetchAllCategoriesSucceedAction(categories));
|
||||
} catch (error) {
|
||||
yield put(fetchCategoryFailedAction(error));
|
||||
}
|
||||
}
|
||||
|
||||
export default function* categoryFetchAllWatcher() {
|
||||
export default function* categoryFetchAllWatcher(): Generator {
|
||||
yield takeLatest(CategoryActions.FETCH_ALL, fetchAll);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ import { all } from "redux-saga/effects";
|
||||
import categoryWatchers from "./category";
|
||||
import applicationWatchers from "./application";
|
||||
|
||||
export default function* rootSaga() {
|
||||
export default function* rootSaga(): Generator {
|
||||
yield all([...categoryWatchers, ...applicationWatchers]);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import config from "../../../config";
|
||||
|
||||
export const FetchAllCategories = () =>
|
||||
export const FetchAllCategories = (): Promise<any> =>
|
||||
fetch(`http://${config.api.url}:${config.api.port}/categories`).then((response: Response) =>
|
||||
response.json(),
|
||||
);
|
||||
|
||||
export const FetchAllApplications = () =>
|
||||
export const FetchAllApplications = (): Promise<any> =>
|
||||
fetch(`http://${config.api.url}:${config.api.port}/apps`).then((response: Response) =>
|
||||
response.json(),
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import config from "../../config";
|
||||
|
||||
class PathBuilder {
|
||||
static build(path: string = "/") {
|
||||
static build(path = "/"): string {
|
||||
return `/${config.repository_name}${path}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 930 B After Width: | Height: | Size: 930 B |
@@ -19,4 +19,5 @@ body {
|
||||
#root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 1100px;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable */
|
||||
|
||||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createStore, applyMiddleware, StoreEnhancer, compose } from "redux";
|
||||
import { createStore, applyMiddleware, compose } from "redux";
|
||||
|
||||
import createSagaMiddleware from "redux-saga";
|
||||
|
||||
@@ -15,9 +15,7 @@ const middlewareEnhancer = composeWithDevTools(applyMiddleware(...middlewares));
|
||||
|
||||
const enhancers = [middlewareEnhancer];
|
||||
|
||||
const composedEnhancers: StoreEnhancer<{}> = compose(...enhancers);
|
||||
|
||||
const store = createStore(reducers, composedEnhancers);
|
||||
const store = createStore(reducers, compose(...enhancers));
|
||||
|
||||
sagaMiddleware.run(rootSaga);
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
@@ -13,7 +17,10 @@
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react"
|
||||
"jsx": "react-jsx",
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
|
||||
8579
page-src/yarn.lock
8579
page-src/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user