Skip to content

almost done #2

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

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
07b0977
fix package.json
mjkatgithub Jun 25, 2018
7c7adef
add material-ui
mjkatgithub Jun 25, 2018
290d42a
add App-Header
mjkatgithub Jun 25, 2018
7aeaaf6
add shortcut to package.json
mjkatgithub Jun 26, 2018
80e2533
add downshift
mjkatgithub Jun 28, 2018
973e8f9
add messy adder to AppHeader
mjkatgithub Jun 28, 2018
ae087fb
use actionTypes
mjkatgithub Jul 13, 2018
3931422
update adderActions
mjkatgithub Jul 13, 2018
73a5d0c
install some dependencies
mjkatgithub Jul 13, 2018
5ec4f49
set global background-color
mjkatgithub Jul 13, 2018
fed6f34
add delButton
mjkatgithub Jul 13, 2018
d941d95
add bookmarks
mjkatgithub Jul 13, 2018
b98d23b
add ItemTypes
mjkatgithub Jul 13, 2018
336dbf2
add BookmarkList
mjkatgithub Jul 13, 2018
22f9f88
add BookmarkActions
mjkatgithub Jul 13, 2018
cab5e38
add BookmarkReducer
mjkatgithub Jul 14, 2018
5fc5470
add MetaList
mjkatgithub Jul 14, 2018
5cd67f9
add route to bookmarks
mjkatgithub Jul 14, 2018
657fec1
redirect to bookmarks
mjkatgithub Jul 14, 2018
8bab9d7
save lodash in package.json
mjkatgithub Jul 14, 2018
72f0141
persist state in local storage
mjkatgithub Jul 14, 2018
d3be188
user star.svg for stargazers_count
mjkatgithub Jul 14, 2018
ab713a9
fix icon colors
mjkatgithub Jul 14, 2018
6a824bd
fix adder design
mjkatgithub Jul 14, 2018
91b02e9
reduce api calls
mjkatgithub Jul 14, 2018
b7617e2
set AppBar position static
mjkatgithub Jul 14, 2018
aa5adac
add margin-bottom to BookmarkLists
mjkatgithub Jul 14, 2018
01c124d
add check.svg
mjkatgithub Jul 16, 2018
45ff7fe
final adder design
mjkatgithub Jul 16, 2018
1b11726
add SnackBar
mjkatgithub Jul 17, 2018
7d7ffce
only save unsaved bookmarks
mjkatgithub Jul 17, 2018
714ced1
Merge branch 'develop'
mjkatgithub Jul 18, 2018
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
699 changes: 647 additions & 52 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,35 @@
"private": true,
"main": "index.js",
"scripts": {
"start": "npm run development",
"test": "jest",
"development": "webpack-dev-server --env=development",
"production": "webpack --env=production",
"test:watch": "jest --watch",
"flow:start": "flow start",
"flo:stop": "flow stop",
"flow:stop": "flow stop",
"flow:status": "flow status",
"flow:coverage": "flow coverage"
},
"lint-staged": {},
"author": "",
"license": "ISC",
"dependencies": {
"@material-ui/core": "^1.2.3",
"axios": "^0.18.0",
"babel-polyfill": "^6.26.0",
"downshift": "^2.0.10",
"immutable": "^3.8.2",
"lodash": "^4.17.10",
"number-abbreviate": "^2.0.0",
"polished": "^1.9.2",
"react": "^16.3.2",
"react-dnd": "^5.0.0",
"react-dnd-html5-backend": "^5.0.1",
"react-dom": "^16.3.2",
"react-dropzone": "^4.2.9",
"react-router-dom": "^4.3.1",
"react-timeago": "^4.1.9",
"redux": "^4.0.0",
"redux-actions": "^2.3.0",
"redux-immutable": "^4.0.0",
Expand Down
12 changes: 12 additions & 0 deletions src/app/ActionTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const ActionTypes = {
SEARCH_REPOS: 'SEARCH_REPOS',
ADD_BOOKMARK: 'ADD_BOOKMARK',
MOVE_BOOKMARK: 'MOVE_BOOKMARK',
DELETE_BOOKMARK: 'DELETE_BOOKMARK',
ADD_BOOKMARK_LIST: 'ADD_BOOKMARK_LIST',
DELETE_BOOKMARK_LIST: 'DELETE_BOOKMARK_LIST',
SHOW_MESSAGE: 'SHOW_MESSAGE',
CLEAR_MESSAGE: 'CLEAR_MESSAGE',
}

export default ActionTypes;
23 changes: 23 additions & 0 deletions src/app/AppHeader/Adder/Adder.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.AdderSubContainer{
position: relative;
}

.SuggestionSelector{
z-index: 1;
position: absolute;
margin-top: 16px;
padding-bottom: 24px;
left: 0;
right: 0;
}

.AdderInputRoot{
flex-wrap: wrap;
padding-left: 60px;
}

@media only screen and (max-width: 600px) {
.SuggestionSelector{
margin-top: 8px;
}
}
206 changes: 206 additions & 0 deletions src/app/AppHeader/Adder/Adder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import React from 'react';
import Downshift from 'downshift';
import TextField from '@material-ui/core/TextField';
import Paper from '@material-ui/core/Paper';
import './Adder.css'
import styled from 'styled-components'
import TimeAgo from 'react-timeago';
var abbreviate = require('number-abbreviate');

export default class Adder extends React.Component {
render() {
return (
<AdderRoot>
<SearchIcon dangerouslySetInnerHTML={{ __html: require('../../../assets/search.svg') }} />
<Downshift onChange={(item) => this.handleSelect(item)} itemToString={() => {return '';}}>
{({
getInputProps,
getItemProps,
isOpen,
inputValue,
highlightedIndex,
selectedItem,
clearSelection
}) => (
<div className="AdderSubContainer">
{this.renderInput({
fullWidth: true,
InputProps: getInputProps({
placeholder: 'Search...',
id: 'AdderInput',
}),
})}
{isOpen ? (
<Paper className="SuggestionSelector" square>
{this.getSuggestions(inputValue).map((suggestion, index) =>
this.renderSuggestion({
suggestion,
index,
itemProps: getItemProps({ item: suggestion }),
getItemProps,
highlightedIndex,
selectedItem,
}),
)}
</Paper>
) : null}
</div>
)}
</Downshift>
</AdderRoot>
);
}

handleSelect(Bookmark){
if (Bookmark.saved) {
this.props.showMessage('This Repo is already bookmarked.');
} else {
this.props.addRepo(Bookmark.url);
}
}

renderInput(inputProps){
const { InputProps, ref, ...other } = inputProps;
return (
<TextField
InputProps={{
disableUnderline: true,
inputRef: ref,
classes: {
root: "AdderInputRoot",
},
...InputProps,
}}
{...other}
/>
);
}

getSuggestions(inputValue) {
if (inputValue !== '' && inputValue !== this.props.lastQuery) {
this.props.searchRepos(inputValue);
}
return this.props.suggestions;
}

renderSuggestion({ suggestion, index, itemProps, getItemProps, highlightedIndex, selectedItem }) {
return (
<div
{...getItemProps({
key: suggestion.full_name,
index,
item: suggestion,
style: {
backgroundColor: highlightedIndex === index
? 'lightgray'
: 'white',
},
})}
>
<Suggestion>
<Header>
{suggestion.full_name}
</Header>
<Description>
{suggestion.description ? suggestion.description : null}
</Description>
<Footer>
{suggestion.license
? <span>{suggestion.license.name}</span>
: null}
<span>{'Updated '}<TimeAgo date={suggestion.updated_at} /></span>
<span>{suggestion.open_issues_count + ' issues need help '}</span>
<span>
<StarIcon dangerouslySetInnerHTML={{ __html: require('../../../assets/star.svg') }} />
{abbreviate(suggestion.stargazers_count, 2) + ' '}
</span>
</Footer>
{suggestion.saved
? <SavedIcon dangerouslySetInnerHTML={{ __html: require('../../../assets/check.svg') }} />
: <AddIcon dangerouslySetInnerHTML={{ __html: require('../../../assets/plus.svg') }} />}
</Suggestion>
</div>
);
}
}

const AdderRoot = styled.div`
margin-left: 56px;
flex: 3 0 auto;
max-width: 800px;
`

const SearchIcon = styled.span`
svg {
position: absolute;
margin-right: 200px;
height: 32px;
path {
fill: #999;
}
}
`

const Suggestion = styled.div`
position: relative;
padding-top: 24px;
padding-bottom: 16px;
padding-left: 60px;
height: 130px;
border-bottom: 1px solid #ccc;
overflow-x: hidden;
`

const Header = styled.div`
font-size: 0.9em;
letter-spacing: 0.5px;
color: #2b7be8;
font-weight: bold;
`

const Description = styled.div`
padding-top 5px;
font-size: 0.8em;
`

const Footer = styled.div`
font-size: 0.65em;
position: absolute;
bottom: 10px;
span {
margin-right: 16px;
}
`

const StarIcon = styled.span`
margin-right: 0px !important;
svg {
height: 9px;
path {
fill: #999;
}​
}
`

const SavedIcon = styled.span`
position absolute;
top: 55px;
right: 38px;
svg {
height: 20px;
width: 20px;
}
`

const AddIcon = styled.span`
position absolute;
top: 55px;
right: 38px;
svg {
height: 20px;
width: 20px;
path {
fill: #999;
}​
}
`
36 changes: 36 additions & 0 deletions src/app/AppHeader/Adder/AdderActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import ActionTypes from '../../ActionTypes'
import axios from 'axios';

//this token has a public scope and is used to test this app
//and just increse the Rate limit for github-api-calls
let AccessToken = 'c436cd365f5e323ceb2ba307751e6ec878d2c3f8';

export function searchRepos(query) {
return function(dispatch) {
axios.get(`https://api.github.com/search/repositories?q=${query}&access_token=${AccessToken}`)
.then(response => {
dispatch({
type: ActionTypes.SEARCH_REPOS,
payload: {query:query, searchResults:response.data.items}
});
})
.catch((error) => {
console.error(error);
})
}
}

export function addRepo(repoLink) {
return function(dispatch) {
axios.get(repoLink)
.then(response => {
dispatch({
type: ActionTypes.ADD_BOOKMARK,
payload: response.data
});
})
.catch((error) => {
console.error(error);
})
}
}
55 changes: 55 additions & 0 deletions src/app/AppHeader/Adder/AdderContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { connect } from 'react-redux'
import { searchRepos, addRepo } from './AdderActions'
import Adder from './Adder'
import { showMessage } from '../../SnackBar/SnackBarActions'

const getSuggestions = (state) => {
let inputValue = state.AdderReducer.query;
let suggestions = state.AdderReducer.searchResults;
let bookmarkLists = state.BookmarkReducer;
let selection = [];
let count = 0;
let preSelection = suggestions.filter(suggestion => {
const keep =
(!inputValue || suggestion.full_name.toLowerCase().indexOf(inputValue.toLowerCase()) !== -1) &&
count < 3;
if (keep) {
count += 1;
}
return keep;
});
preSelection.forEach((suggestion) => {
let saved = false;
for (var index in bookmarkLists) {
if (bookmarkLists.hasOwnProperty(index)) {
bookmarkLists[index].bookmarks.forEach(bookmark => {
if (bookmark.id === suggestion.id) {
saved = true;
}
});
}
}
selection.push(
{...suggestion, saved}
);
});
return selection;
}

const mapStateToProps = state => ({
suggestions: getSuggestions(state),
lastQuery: state.AdderReducer.query
})

const mapDispatchToProps = dispatch => {
return{
searchRepos: (query) => { dispatch(searchRepos(query)); },
addRepo: (url) => { dispatch(addRepo(url)); },
showMessage: (message) => { dispatch(showMessage(message)); }
};
};

export default connect(
mapStateToProps,
mapDispatchToProps
)(Adder)
23 changes: 23 additions & 0 deletions src/app/AppHeader/Adder/AdderReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import ActionTypes from '../../ActionTypes'

const InitialState = {
query: "",
searchResults: []
};

const AdderReducer = (state = InitialState, action) => {
switch (action.type) {
case ActionTypes.SEARCH_REPOS:
state = {
...state,
query: action.payload.query,
searchResults: action.payload.searchResults
};
return state;
break;
default:
return state;
}
}

export default AdderReducer;
Loading