From ccbcc727330c1302c3ec28d8c3753eeef4c1815b Mon Sep 17 00:00:00 2001
From: Oleksandr Trypolskyi <37958052+trypolski@users.noreply.github.com>
Date: Tue, 12 Jun 2018 17:38:34 +0300
Subject: [PATCH] because the task
it was interesting
---
src/app/Bookmarks/scenes/Overview/Overview.js | 25 +--
src/app/actions/index.js | 98 +++++++++++
src/app/additional/additionalFunc.js | 81 ++++++++++
src/app/components/BookmarkApp.js | 15 ++
.../components/Columns/BookmarkAddColumn.js | 71 ++++++++
src/app/components/Columns/BookmarkCard.js | 153 ++++++++++++++++++
src/app/components/Columns/BookmarkColumn.js | 22 +++
.../components/Columns/BookmarkColumnHead.js | 50 ++++++
.../components/Columns/BookmarkContainer.js | 26 +++
src/app/components/DropDown/DropDown.js | 23 +++
src/app/components/DropDown/DropDownButton.js | 44 +++++
src/app/components/DropDown/InfoString.js | 40 +++++
src/app/components/Search/SearchInput.js | 73 +++++++++
src/app/components/Search/SearchItem.js | 58 +++++++
src/app/components/Search/SearchItemsList.js | 23 +++
.../containers/Columns/DndBookmarkColumn.js | 54 +++++++
.../Columns/DndBookmarkColumnContainer.js | 15 ++
.../Columns/DndBookmarkColumnCover.js | 55 +++++++
.../Columns/ShowBookmarkAddColumn.js | 64 ++++++++
.../containers/Columns/ShowBookmarkCard.js | 96 +++++++++++
.../Columns/ShowBookmarkColumnHead.js | 60 +++++++
.../Columns/ShowBookmarkContainer.js | 14 ++
.../containers/DropDown/DropDownInfoString.js | 19 +++
src/app/containers/DropDown/ShowDropDown.js | 56 +++++++
.../containers/DropDown/ShowDropDownButton.js | 46 ++++++
src/app/containers/Search/ShowSearchInput.js | 39 +++++
src/app/global.js | 17 +-
src/app/index.js | 7 +-
src/app/reducer.js | 7 +-
src/app/reducers/bookmarks.js | 44 +++++
src/app/reducers/search.js | 26 +++
src/app/store.js | 8 +
32 files changed, 1386 insertions(+), 43 deletions(-)
create mode 100644 src/app/actions/index.js
create mode 100644 src/app/additional/additionalFunc.js
create mode 100644 src/app/components/BookmarkApp.js
create mode 100644 src/app/components/Columns/BookmarkAddColumn.js
create mode 100644 src/app/components/Columns/BookmarkCard.js
create mode 100644 src/app/components/Columns/BookmarkColumn.js
create mode 100644 src/app/components/Columns/BookmarkColumnHead.js
create mode 100644 src/app/components/Columns/BookmarkContainer.js
create mode 100644 src/app/components/DropDown/DropDown.js
create mode 100644 src/app/components/DropDown/DropDownButton.js
create mode 100644 src/app/components/DropDown/InfoString.js
create mode 100644 src/app/components/Search/SearchInput.js
create mode 100644 src/app/components/Search/SearchItem.js
create mode 100644 src/app/components/Search/SearchItemsList.js
create mode 100644 src/app/containers/Columns/DndBookmarkColumn.js
create mode 100644 src/app/containers/Columns/DndBookmarkColumnContainer.js
create mode 100644 src/app/containers/Columns/DndBookmarkColumnCover.js
create mode 100644 src/app/containers/Columns/ShowBookmarkAddColumn.js
create mode 100644 src/app/containers/Columns/ShowBookmarkCard.js
create mode 100644 src/app/containers/Columns/ShowBookmarkColumnHead.js
create mode 100644 src/app/containers/Columns/ShowBookmarkContainer.js
create mode 100644 src/app/containers/DropDown/DropDownInfoString.js
create mode 100644 src/app/containers/DropDown/ShowDropDown.js
create mode 100644 src/app/containers/DropDown/ShowDropDownButton.js
create mode 100644 src/app/containers/Search/ShowSearchInput.js
create mode 100644 src/app/reducers/bookmarks.js
create mode 100644 src/app/reducers/search.js
diff --git a/src/app/Bookmarks/scenes/Overview/Overview.js b/src/app/Bookmarks/scenes/Overview/Overview.js
index 548f079..324980b 100644
--- a/src/app/Bookmarks/scenes/Overview/Overview.js
+++ b/src/app/Bookmarks/scenes/Overview/Overview.js
@@ -1,29 +1,10 @@
import React from 'react'
-import styled from 'styled-components'
+import BookmarkApp from '../../../components/BookmarkApp'
+
export default class Home extends React.PureComponent {
render () {
return (
-
-
- Happy Coding!
-
-
-
+
)
}
}
-
-const Wrapper = styled.div`
- display: flex;
- height: 100%;
- align-items: center;
- justify-content: center;
-`
-
-const Message = styled.div`
- font-size: 62px;
-`
-
-const Icon = styled.div`
-
-`
diff --git a/src/app/actions/index.js b/src/app/actions/index.js
new file mode 100644
index 0000000..9afd5d8
--- /dev/null
+++ b/src/app/actions/index.js
@@ -0,0 +1,98 @@
+// import fetch from 'cross-fetch'
+import axios from 'axios'
+
+export const SEARCH_ONCHANGE = 'SEARCH_ONCHANGE'
+export const SEARCH_RECEIVE_DATA = 'SEARCH_RECEIVE_DATA'
+export const DROPDOWN_SHOW = 'DROPDOWN_SHOW'
+export const DROPDOWN_HIDE = 'DROPDOWN_HIDE'
+
+export const ADD_BOOKMARK = 'ADD_BOOKMARK'
+export const DELETE_BOOKMARK = 'DELETE_BOOKMARK'
+export const MOVE_BOOKMARK = 'MOVE_BOOKMARK'
+
+export const COLUMN_NAME_ONCHANGE = 'COLUMN_NAME_ONCHANGE'
+export const ADD_COLUMN = 'ADD_COLUMN'
+export const DELETE_COLUMN = 'DELETE_COLUMN'
+
+// Changing value of search input
+export const searchOnchange = text => ({
+ type: SEARCH_ONCHANGE,
+ data: text
+})
+
+// Received data of 5 first results
+export const searchReaceiveData = json => ({
+ type: SEARCH_RECEIVE_DATA,
+ items: json
+})
+
+// Also we can get data from GitHUB API using fetch
+// export const searchFetchQuery = text => dispatch => {
+// return fetch(`https://api.github.com/search/repositories?q=${text}+in:name&sort=stars`)
+// .then(response => response.json())
+// .then(json => dispatch(searchReaceiveData(json.items.slice(0, 5))))
+// }
+
+// Fetching data from GitHUB API
+export const searchFetchQuery = text => dispatch => {
+ return axios.get(`https://api.github.com/search/repositories?q=${text}+in:name&sort=stars`)
+ .then(function (response) {
+ // here we can change results amount
+ dispatch(searchReaceiveData(response.data.items.slice(0, 5)))
+ })
+ .catch(function (error) {
+ console.log(error)
+ })
+}
+
+// Showing dropdown box
+export const dropdownShow = () => ({
+ type: DROPDOWN_SHOW
+})
+
+// Hiding dropdown box
+export const dropdownHide = () => ({
+ type: DROPDOWN_HIDE
+})
+
+// Adding new bookmark
+export const addBookmark = item => ({
+ type: ADD_BOOKMARK,
+ item: item
+})
+
+// Deleting bookmark
+export const deleteBookmark = (id, columnName) => ({
+ type: DELETE_BOOKMARK,
+ id: id,
+ column: columnName
+})
+
+// Moving bookmark
+export const moveBookmark = (id, idColumnName, indexOfId, atIndexColumnName, indexOfatIndex) => ({
+ type: MOVE_BOOKMARK,
+ id: id,
+ idColumnName: idColumnName,
+ indexOfId: indexOfId,
+ atIndexColumnName: atIndexColumnName,
+ indexOfatIndex: indexOfatIndex
+})
+
+// Changing value of add column input
+export const columnNameOnchange = text => ({
+ type: COLUMN_NAME_ONCHANGE,
+ text: text
+})
+
+// Adding new column
+export const addColumn = name => ({
+ type: ADD_COLUMN,
+ name: name
+})
+
+// Deleting column
+export const deleteColumn = (name, cards) => ({
+ type: DELETE_COLUMN,
+ name: name,
+ cards: cards
+})
diff --git a/src/app/additional/additionalFunc.js b/src/app/additional/additionalFunc.js
new file mode 100644
index 0000000..97e354f
--- /dev/null
+++ b/src/app/additional/additionalFunc.js
@@ -0,0 +1,81 @@
+// Creating update info
+export function wasUpdated (dateString) {
+ const updatedAtMilisec = Date.parse(dateString)
+ const todayMilisec = Date.now()
+ let dayDiffirence = parseInt((todayMilisec - updatedAtMilisec) / (1000 * 60 * 60 * 24))
+ let result = ''
+ if (dayDiffirence > 1) {
+ result = 'Updated ' + dayDiffirence.toString() + ' days ago'
+ return result
+ } else if (dayDiffirence === 1) {
+ result = 'Updated a day ago'
+ return result
+ }
+ result = 'Updated today'
+ return result
+}
+
+// Creating stars info
+export function showStars (stars) {
+ let result = ''
+ const starsAbsolute = stars % 1000
+ if (stars < 1000) {
+ result = stars.toString()
+ return result
+ } else if (starsAbsolute > 50 && starsAbsolute < 951) {
+ let starsKilo = (stars / 1000).toFixed(1)
+ result = starsKilo + 'k'
+ return result
+ } else if (starsAbsolute < 50 || starsAbsolute > 950) {
+ let starsKilo = (stars / 1000).toFixed()
+ result = starsKilo + 'k'
+ return result
+ }
+}
+
+// Creating issues info
+export function showIssues (issues) {
+ let result = 'No issues'
+ if (issues > 1) {
+ result = issues.toString() + ' issues need help'
+ return result
+ } else if (issues === 1) {
+ result = '1 issue need help'
+ return result
+ }
+ return result
+}
+
+// Creating license info
+export function showLicense (license) {
+ let result = ''
+ if (!license) {
+ result = 'No info'
+ return result
+ }
+ result = license.name
+ return result
+}
+
+// Loading the state from LocalStorage
+export const loadState = () => {
+ try {
+ const serializedState = localStorage.getItem('bookmark')
+ if (serializedState === null) {
+ return { 'bookmarks': { name: '', list: {}, columns: [{ name: 'Collection', cards: [] }] } }
+ }
+ return JSON.parse(serializedState)
+ } catch (err) {
+ return undefined
+ }
+}
+
+// Saving the state to LocalStorage
+export const saveState = (state) => {
+ try {
+ const serializedState = JSON.stringify(state)
+ localStorage.setItem('bookmark', serializedState)
+ } catch (err) {
+ // Ignore errors
+ }
+}
diff --git a/src/app/components/BookmarkApp.js b/src/app/components/BookmarkApp.js
new file mode 100644
index 0000000..3d2253b
--- /dev/null
+++ b/src/app/components/BookmarkApp.js
@@ -0,0 +1,15 @@
+import React from 'react'
+
+import ShowSearchInput from '../containers/Search/ShowSearchInput'
+import ShowDropDown from '../containers/DropDown/ShowDropDown'
+import ShowBookmarkContainer from '../containers/Columns/ShowBookmarkContainer'
+
+const BookmarkApp = () => (
+
+
+
+
+
+)
+
+export default BookmarkApp
diff --git a/src/app/components/Columns/BookmarkAddColumn.js b/src/app/components/Columns/BookmarkAddColumn.js
new file mode 100644
index 0000000..c01bab2
--- /dev/null
+++ b/src/app/components/Columns/BookmarkAddColumn.js
@@ -0,0 +1,71 @@
+import React from 'react'
+import styled from 'styled-components'
+
+// Visualisation of add column block
+const BookmarkAddColumn = ({ className, value, onChange, onClick, onKeyPress }) => (
+
+)
+
+export default styled(BookmarkAddColumn)`
+ position: relative;
+ display: inline-block;
+ width: 196px;
+ height: 50px;
+ background-color: rgb(245, 247, 250);
+ border: 2px dashed rgb(153, 153, 153);
+ font-family: 'Open Sans', sans-serif;
+ font-weight: bold;
+ font-size: 17px;
+ margin: 7px 15px;
+
+ input[type="text"] {
+ color: rgb(73, 73, 73);
+ border-radius: 5px;
+ border: 1px solid transparent;
+ width: 70%;
+ height: 27px;
+ font-size: 17px;
+ background-color: rgb(245, 247, 250);
+ font-family: 'Open Sans', sans-serif;
+ font-weight: bold;
+ margin: 12px 0px 12px 15px;
+ }
+
+ input[type="text"]::placeholder {
+ font-size: 17px;
+ font-family: 'Open Sans', sans-serif;
+ font-weight: bold;
+ }
+
+ button {
+ position: absolute;
+ right: 0;
+ top: 0;
+ border: 0;
+ background: transparent;
+ cursor: pointer;
+ border-radius: 50%;
+ width: 30px;
+ height: 30px;
+ padding: 0;
+ margin: 6%;
+ }
+
+ button svg {
+ fill: #999999;
+ width: 30px;
+ height: 30px;
+ padding: 0;
+ }
+
+`
diff --git a/src/app/components/Columns/BookmarkCard.js b/src/app/components/Columns/BookmarkCard.js
new file mode 100644
index 0000000..a482604
--- /dev/null
+++ b/src/app/components/Columns/BookmarkCard.js
@@ -0,0 +1,153 @@
+import React from 'react'
+import styled from 'styled-components'
+
+// Visualisation of the bookmark card
+const BookmarkCard = ({ className,
+ onClick,
+ imgUrl,
+ htmlUrl,
+ fullName,
+ description,
+ updatedAt,
+ stars,
+ issues }) => (
+
+
+
+

+
+
+
{fullName}
+
+
+
+
{stars}
+
{issues}
+
{updatedAt}
+
+
+
+)
+
+export default styled(BookmarkCard)`
+ display: ${props => props.dragging ? '0.5' : '1'};
+ opacity: ${props => props.dragging ? '0' : '1'};
+ position: relative;
+ width: 255px;
+ min-height: 110px;
+ background-color: white;
+ box-shadow: 1px 2px 5px 0px rgba(0,0,0,0.5);
+ -moz-box-shadow: 1px 2px 5px 0px rgba(0,0,0,0.5);
+ -webkit-box-shadow: 1px 2px 5px 0px rgba(0,0,0,0.5);
+ font-family: 'Open Sans', sans-serif;
+ font-weight: bold;
+ font-size: 17px;
+ text-align: left;
+ margin-bottom: 1px;
+ cursor: move;
+
+ .delete-bookmark {
+ position: absolute;
+ right: 0;
+ top: 0;
+ border: 0;
+ background: transparent;
+ cursor: pointer;
+ border-radius: 50%;
+ width: 15px;
+ height: 15px;
+ padding: 0;
+ margin: 4px;
+ }
+
+ .delete-bookmark svg {
+ width: 15px;
+ height: 15px;
+ padding: 0;
+ }
+
+ .card-image {
+ position: relative;
+ display: inline-block;
+ height: auto;
+ width: 20%;
+ vertical-align: top;
+ }
+
+ .card-image img {
+ position: absolute;
+ top: 20px;
+ left: 5px;
+ width: 80%;
+ border-radius: 50%;
+ }
+
+ .card-info {
+ display: inline-block;
+ height: auto;
+ width: 80%;
+ line-height: 10px;
+ }
+
+ .card-info p {
+ margin: 15px 15px 0 0;
+ }
+
+ .card-info a {
+ color: rgb(43, 123, 232);
+ text-decoration: none;
+ font-family: 'Open Sans', sans-serif;
+ font-size: 11px;
+ font-weight: 800;
+ white-space: pre-line;
+ word-wrap: break-word;
+ }
+
+ .card-description {
+ min-height: 40px;
+ }
+
+ .card-description p {
+ color: rgb(158, 158, 158);
+ font-family: 'Open Sans', sans-serif;
+ font-size: 10px;
+ white-space: pre-line;
+ word-wrap: break-word;
+ }
+
+ .card-info-string {
+ margin: 10px 15px 5px 0;
+ }
+
+ .card-info-string::before {
+ content: "";
+ display: inline-block;
+ vertical-align: middle;
+ height: 100%;
+ }
+
+ .card-info-string div {
+ color: rgb(158, 158, 158);
+ font-family: 'Open Sans', sans-serif;
+ font-size: 7px;
+ display: inline-block;
+ vertical-align: middle;
+ padding-right: 7px;
+ }
+
+ .card-info-string .star {
+ padding-right: 1px;
+ padding-bottom: 3px;
+ }
+
+ .card-info-string svg {
+ padding-top: 1px;
+ width: 10px;
+ height: 10px;
+ fill: #9e9e9e;
+ }
+`
diff --git a/src/app/components/Columns/BookmarkColumn.js b/src/app/components/Columns/BookmarkColumn.js
new file mode 100644
index 0000000..ffacc27
--- /dev/null
+++ b/src/app/components/Columns/BookmarkColumn.js
@@ -0,0 +1,22 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import ShowBookmarkColumnHead from '../../containers/Columns/ShowBookmarkColumnHead'
+import DndBookmarkColumnCover from '../../containers/Columns/DndBookmarkColumnCover'
+
+// Visualisation of the column for bookmark cards
+const BookmarkColumn = ({ className, id, name, cards }) => (
+
+
+
+
+)
+
+export default styled(BookmarkColumn)`
+ display: inline-block;
+ width: 250px;
+ height: auto;
+ margin: 5px 15px;
+ vertical-align: top;
+`
diff --git a/src/app/components/Columns/BookmarkColumnHead.js b/src/app/components/Columns/BookmarkColumnHead.js
new file mode 100644
index 0000000..76c7d81
--- /dev/null
+++ b/src/app/components/Columns/BookmarkColumnHead.js
@@ -0,0 +1,50 @@
+import React from 'react'
+import styled from 'styled-components'
+
+// Visualisation of columns head block
+const BookmarkColumnHead = ({className, name, onClick}) => (
+
+)
+
+export default styled(BookmarkColumnHead)`
+ position: ${props => props.sticky ? 'fixed' : 'relative'};
+ top: ${props => props.sticky ? '0' : '0'};
+ z-index: 1;
+ width: 240px;
+ height: 25px;
+ background-color: white;
+ box-shadow: ${props => props.sticky ? '1px 3px 5px 0px rgba(0, 0, 0, 0.5)' : '1px 1px 5px 0px rgba(0, 0, 0, 0.5)'};
+ -moz-box-shadow: ${props => props.sticky ? '1px 3px 5px 0px rgba(0, 0, 0, 0.5)' : '1px 1px 5px 0px rgba(0, 0, 0, 0.5)'};
+ -webkit-box-shadow: ${props => props.sticky ? '1px 3px 5px 0px rgba(0, 0, 0, 0.5)' : '1px 1px 5px 0px rgba(0, 0, 0, 0.5)'};
+ font-family: 'Open Sans', sans-serif;
+ font-weight: bold;
+ font-size: 17px;
+ text-align: left;
+ padding: 17px 0 14px 15px;
+ margin-bottom: 1px;
+
+ .delete-column {
+ position: absolute;
+ right: 0;
+ top: 0;
+ border: 0;
+ background: transparent;
+ cursor: pointer;
+ border-radius: 50%;
+ width: 20px;
+ height: 20px;
+ padding: 0;
+ margin: 4px;
+ }
+
+ .delete-column svg {
+ width: 20px;
+ height: 20px;
+ padding: 0;
+ }
+`
diff --git a/src/app/components/Columns/BookmarkContainer.js b/src/app/components/Columns/BookmarkContainer.js
new file mode 100644
index 0000000..7987e2a
--- /dev/null
+++ b/src/app/components/Columns/BookmarkContainer.js
@@ -0,0 +1,26 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import BookmarkColumn from './BookmarkColumn'
+import ShowBookmarkAddColumn from '../../containers/Columns/ShowBookmarkAddColumn'
+import DndBookmarkColumnContainer from '../../containers/Columns/DndBookmarkColumnContainer'
+
+// Visualisation of the main container for columns
+const BookmarkContainer = ({ className, columns }) => (
+
+
+ {columns.map(column =>
+
+ )}
+
+
+
+)
+
+export default styled(BookmarkContainer)`
+ position: absolute;
+ left 50px;
+ top: 100px;
+ overflow-x: auto;
+ white-space: nowrap;
+`
diff --git a/src/app/components/DropDown/DropDown.js b/src/app/components/DropDown/DropDown.js
new file mode 100644
index 0000000..3180b18
--- /dev/null
+++ b/src/app/components/DropDown/DropDown.js
@@ -0,0 +1,23 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import SearchItemsList from '../../components/Search/SearchItemsList'
+
+// Visualisation of dropdown block
+const DropDown = (props) => (
+
+
+
+)
+
+export default styled(DropDown)`
+ display: block;
+ position: relative;
+ left: 10%;
+ top: 40px;
+ background-color: #f9f9f9;
+ width: 40%;
+ min-width: 350px;
+ box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
+ z-index: 2;
+`
diff --git a/src/app/components/DropDown/DropDownButton.js b/src/app/components/DropDown/DropDownButton.js
new file mode 100644
index 0000000..f6feeeb
--- /dev/null
+++ b/src/app/components/DropDown/DropDownButton.js
@@ -0,0 +1,44 @@
+import React from 'react'
+import styled from 'styled-components'
+
+// Visualisation of dropdown add button
+const DropDownButton = ({ className, added, onClick }) => (
+
+)
+
+export default styled(DropDownButton)`
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ border: 0;
+ background: transparent;
+ cursor: pointer;
+ border-radius: 50%;
+ width: 35px;
+ height: 35px;
+ padding: 0;
+ margin-top: auto;
+ margin-bottom: auto;
+ margin-right: 6%;
+
+ svg {
+ width: 35px;
+ height: 35px;
+ padding: 0;
+ }
+
+ .checked {
+ fill: #31cc73;
+ }
+
+ .add {
+ fill: #999999;
+ }
+`
diff --git a/src/app/components/DropDown/InfoString.js b/src/app/components/DropDown/InfoString.js
new file mode 100644
index 0000000..ee2ad7d
--- /dev/null
+++ b/src/app/components/DropDown/InfoString.js
@@ -0,0 +1,40 @@
+import React from 'react'
+import styled from 'styled-components'
+
+// Visualisation of info string for dropdown search blocks
+const InfoString = ({ updatedAt, stars, issues, license, className }) => (
+
+
{license}
+
{updatedAt}
+
{issues}
+
+
{stars}
+
+)
+
+export default styled(InfoString)`
+ :before {
+ content: "";
+ display: inline-block;
+ vertical-align: middle;
+ }
+
+ div {
+ color: rgb(158, 158, 158);
+ font-family: 'Open Sans', sans-serif;
+ font-size: 10px;
+ display: inline-block;
+ vertical-align: middle;
+ padding: 0 10px 0 0;
+ }
+
+ .star {
+ padding-right: 3px;
+ }
+
+ svg {
+ width: 10px;
+ height: 10px;
+ fill: #9e9e9e;
+ }
+`
diff --git a/src/app/components/Search/SearchInput.js b/src/app/components/Search/SearchInput.js
new file mode 100644
index 0000000..1679586
--- /dev/null
+++ b/src/app/components/Search/SearchInput.js
@@ -0,0 +1,73 @@
+import React from 'react'
+import styled from 'styled-components'
+
+// Visualisation of search input
+const SearchInput = ({ value, onChange, className }) => (
+
+)
+
+export default styled(SearchInput)`
+ .dropdown {
+ display: block;
+ }
+
+ .logo {
+ color: rgb(55, 55, 55);
+ float: right;
+ font-family: 'Open Sans', sans-serif;
+ font-weight: bold;
+ margin: 20px 5% 0 0;
+ }
+
+ div {
+ position: absolute;
+ height: 60px;
+ width: 100%;
+ top: 0;
+ left: 0;
+ z-index: 1;
+ background-color: white;
+ -webkit-box-shadow: 0px 8px 10px -7px rgba(0,0,0,0.5);
+ -moz-box-shadow: 0px 8px 10px -7px rgba(0,0,0,0.5);
+ box-shadow: 0px 8px 10px -7px rgba(0,0,0,0.5);
+ }
+
+ svg {
+ fill: #9e9e9e;
+ float: left;
+ width: 30px;
+ height: 30px;
+ margin: 14px 0 0 10%;
+ text-align: center;
+ }
+
+ input[type="text"] {
+ color: rgb(73, 73, 73);
+ border-radius: 5px;
+ border: 3px solid transparent;
+ width: 25%;
+ height: 28px;
+ margin: 15px 0 0 10px;
+ font-size: 17px;
+ background-color: white;
+ font-family: 'Open Sans', sans-serif;
+ font-weight: 300;
+ }
+
+ input[type="text"]::placeholder {
+ font-style: italic;
+ font-size: 17px;
+ font-family: 'Open Sans', sans-serif;
+ font-weight: 300;
+ }
+`
diff --git a/src/app/components/Search/SearchItem.js b/src/app/components/Search/SearchItem.js
new file mode 100644
index 0000000..cff0723
--- /dev/null
+++ b/src/app/components/Search/SearchItem.js
@@ -0,0 +1,58 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import DropDownInfoString from '../../containers/DropDown/DropDownInfoString'
+import ShowDropDownButton from '../../containers/DropDown/ShowDropDownButton'
+
+// Visualisation of the search item block
+const SearchItem = ({ id,
+ full_name,
+ html_url,
+ description,
+ updated_at,
+ stargazers_count,
+ open_issues,
+ license,
+ className }) => (
+
+
+
+)
+
+export default styled(SearchItem)`
+ list-style-type: none;
+
+ .item-box {
+ position: relative;
+ padding: 15px 0px 5px 35px;
+ border-bottom: 3px solid rgb(230, 230, 230);
+ }
+
+ .description {
+ width: 80%;
+ padding: 0 0 15px 0;
+ font-size: 12px;
+ }
+
+ a {
+ color: rgb(43, 123, 232);
+ text-decoration: none;
+ font-family: 'Open Sans', sans-serif;
+ font-size: 14px;
+ font-weight: bold;
+ }
+
+ p, .description {
+ color: rgb(158, 158, 158);
+ font-family: 'Open Sans', sans-serif;
+ }
+`
diff --git a/src/app/components/Search/SearchItemsList.js b/src/app/components/Search/SearchItemsList.js
new file mode 100644
index 0000000..be851cc
--- /dev/null
+++ b/src/app/components/Search/SearchItemsList.js
@@ -0,0 +1,23 @@
+import React from 'react'
+
+import styled from 'styled-components'
+import SearchItem from './SearchItem'
+
+// Visualisation of the search items list
+const SearchItemsList = ({ items, className }) => (
+
+
+ {items.map(item =>
+
+ )}
+
+
+)
+
+export default styled(SearchItemsList)`
+ ul {
+ padding-left: 0;
+ }
+`
diff --git a/src/app/containers/Columns/DndBookmarkColumn.js b/src/app/containers/Columns/DndBookmarkColumn.js
new file mode 100644
index 0000000..d740a4f
--- /dev/null
+++ b/src/app/containers/Columns/DndBookmarkColumn.js
@@ -0,0 +1,54 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+import { DropTarget } from 'react-dnd'
+
+import ShowBookmarkCard from '../../containers/Columns/ShowBookmarkCard'
+
+const mapStateToProps = state => {
+ return { columns: state.bookmarks.columns }
+}
+
+const cardTarget = {
+ drop () {
+ //
+ },
+ hover (props, monitor, component) {
+ const isOver = monitor.isOver({ shallow: true })
+ const currentColumn = props.findBookmark(monitor.getItem().id).findedColumnName
+ const isDifferentColumn = currentColumn !== props.columnName
+ // when the mouse pointer is over different column
+ if (isOver && isDifferentColumn) {
+ props.moveCard(monitor.getItem().id, props.columnName)
+ }
+ }
+}
+
+@DropTarget('bookmark', cardTarget, connect => ({
+ connectDropTarget: connect.dropTarget()
+}))
+@connect(mapStateToProps)
+export default class DndBookmarkColumn extends Component {
+ constructor (props) {
+ super(props)
+ }
+
+ render () {
+ const { connectDropTarget } = this.props
+ const cards = this.props.cards
+
+ return (
+ connectDropTarget &&
+ connectDropTarget(
+
+ {cards.map(card =>
+
+ )}
+
+ )
+ )
+ }
+}
diff --git a/src/app/containers/Columns/DndBookmarkColumnContainer.js b/src/app/containers/Columns/DndBookmarkColumnContainer.js
new file mode 100644
index 0000000..fb7a4f4
--- /dev/null
+++ b/src/app/containers/Columns/DndBookmarkColumnContainer.js
@@ -0,0 +1,15 @@
+import React, { Component } from 'react'
+import { DragDropContext } from 'react-dnd'
+import HTML5Backend from 'react-dnd-html5-backend'
+
+// Cover all drag-and-drop elements by html5 backend
+@DragDropContext(HTML5Backend)
+export default class DndBookmarkColumnContainer extends Component {
+ render () {
+ return (
+
+ {this.props.children}
+
+ )
+ }
+}
diff --git a/src/app/containers/Columns/DndBookmarkColumnCover.js b/src/app/containers/Columns/DndBookmarkColumnCover.js
new file mode 100644
index 0000000..d995f73
--- /dev/null
+++ b/src/app/containers/Columns/DndBookmarkColumnCover.js
@@ -0,0 +1,55 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+
+import DndBookmarkColumn from '../../containers/Columns/DndBookmarkColumn'
+import { moveBookmark } from '../../actions'
+
+const mapStateToProps = state => {
+ return { columns: state.bookmarks.columns }
+}
+
+@connect(mapStateToProps)
+export default class DndBookmarkColumnCover extends Component {
+ constructor (props) {
+ super(props)
+
+ this.moveCard = this.moveCard.bind(this)
+ this.findBookmark = this.findBookmark.bind(this)
+ }
+
+ // Changing bookmark position
+ moveCard (id, atIndex) {
+ const { dispatch } = this.props
+ const { findedColumnName: idColumnName, index: indexOfId } = this.findBookmark(id)
+ const { findedColumnName: atIndexColumnName, index: indexOfatIndex } = this.findBookmark(atIndex)
+
+ dispatch(moveBookmark(id, idColumnName, indexOfId, atIndexColumnName, indexOfatIndex))
+ }
+
+ // Finding a column name and an index of bookmark card
+ findBookmark (id) {
+ const columns = this.props.columns
+ if (!Number.isInteger(id)) {
+ return {
+ findedColumnName: id,
+ index: 0
+ }
+ }
+ const column = columns.filter(col => col.cards.includes(id))[0]
+ const findedColumnName = column.name
+ const findedCard = column.cards.indexOf(id)
+ return {
+ findedColumnName: findedColumnName,
+ index: findedCard
+ }
+ }
+
+ render () {
+ return (
+
+ )
+ }
+}
diff --git a/src/app/containers/Columns/ShowBookmarkAddColumn.js b/src/app/containers/Columns/ShowBookmarkAddColumn.js
new file mode 100644
index 0000000..02ed981
--- /dev/null
+++ b/src/app/containers/Columns/ShowBookmarkAddColumn.js
@@ -0,0 +1,64 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+
+import { columnNameOnchange, addColumn } from '../../actions/'
+import BookmarkAddColumn from '../../components/Columns/BookmarkAddColumn'
+
+const mapStateToProps = state => {
+ return { value: state.bookmarks.name,
+ columns: state.bookmarks.columns }
+}
+
+@connect(mapStateToProps)
+export default class ShowBookmarkAddColumn extends Component {
+ constructor (props) {
+ super(props)
+
+ this.handleInputChange = this.handleInputChange.bind(this)
+ this.handleAddClick = this.handleAddClick.bind(this)
+ this.handlePressEnter = this.handlePressEnter.bind(this)
+ }
+
+ // Changing value of add column block input
+ handleInputChange (e) {
+ const { dispatch } = this.props
+ dispatch(columnNameOnchange(e.target.value))
+ }
+
+ // Adding new column by button click
+ handleAddClick (e) {
+ const { dispatch } = this.props
+ if (this.props.value && this.props.value !== '') {
+ // if there is no columns with current value
+ if (this.props.columns.every((el) => el.name !== this.props.value)) {
+ dispatch(addColumn(this.props.value))
+ } else {
+ // show message if column with current value already exists
+ alert('The column already exists')
+ }
+ }
+ }
+
+ // Adding new column by Enter press
+ handlePressEnter (e) {
+ const { dispatch } = this.props
+ if (e.key === 'Enter') {
+ if (this.props.value && this.props.value !== '') {
+ if (this.props.columns.every((el) => el.name !== this.props.value)) {
+ dispatch(addColumn(this.props.value))
+ } else {
+ alert('The column already exists')
+ }
+ }
+ }
+ }
+
+ render () {
+ return (
+ this.handleAddClick(e)}
+ onKeyPress={(e) => this.handlePressEnter(e)} />
+ )
+ }
+}
diff --git a/src/app/containers/Columns/ShowBookmarkCard.js b/src/app/containers/Columns/ShowBookmarkCard.js
new file mode 100644
index 0000000..4b23065
--- /dev/null
+++ b/src/app/containers/Columns/ShowBookmarkCard.js
@@ -0,0 +1,96 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+import { DragSource, DropTarget } from 'react-dnd'
+
+import { wasUpdated, showStars, showIssues } from '../../additional/additionalFunc'
+import BookmarkCard from '../../components/Columns/BookmarkCard'
+
+import { deleteBookmark } from '../../actions/'
+
+const mapStateToProps = state => {
+ return { list: state.bookmarks.list }
+}
+
+const cardSource = {
+ beginDrag (props) {
+ return {
+ id: props.card,
+ itemList: props.list
+ }
+ },
+ endDrag (props, monitor) {
+
+ },
+ isDragging (props, monitor) {
+ return props.card === monitor.getItem().id
+ }
+}
+
+const cardTarget = {
+ canDrop () {
+ return false
+ },
+
+ hover (props, monitor, component) {
+ if (monitor.isOver({ shallow: true })) {
+ const draggedId = monitor.getItem().id
+ const overId = props.card
+ // move a bookmark if the current one isn't over itself
+ if (draggedId !== overId) {
+ props.moveCard(draggedId, overId)
+ }
+ }
+ }
+}
+
+@DropTarget('bookmark', cardTarget, (connect, monitor) => ({
+ connectDropTarget: connect.dropTarget(),
+ isOverCurrent: monitor.isOver({ shallow: true })
+}))
+@DragSource('bookmark', cardSource, (connect, monitor) => ({
+ connectDragSource: connect.dragSource(),
+ isDragging: monitor.isDragging()
+}))
+@connect(mapStateToProps)
+export default class ShowBookmarkCard extends Component {
+ constructor (props) {
+ super(props)
+
+ this.handleDeleteBookmark = this.handleDeleteBookmark.bind(this)
+ }
+
+ // Deleting a bookmark by click
+ handleDeleteBookmark (e) {
+ const { dispatch } = this.props
+ dispatch(deleteBookmark(this.props.card, this.props.columnName))
+ }
+
+ render () {
+ const itemList = this.props.list
+ const card = this.props.card
+ const { connectDragSource, connectDropTarget, isDragging } = this.props
+
+ return (
+ connectDragSource &&
+ connectDropTarget &&
+ connectDragSource(
+ connectDropTarget(
+
+ this.handleDeleteBookmark(e)}
+ dragging={isDragging}
+ id={card.toString()}
+ key={card}
+ />
+
+ )
+ )
+ )
+ }
+}
diff --git a/src/app/containers/Columns/ShowBookmarkColumnHead.js b/src/app/containers/Columns/ShowBookmarkColumnHead.js
new file mode 100644
index 0000000..8281657
--- /dev/null
+++ b/src/app/containers/Columns/ShowBookmarkColumnHead.js
@@ -0,0 +1,60 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+
+import BookmarkColumnHead from '../../components/Columns/BookmarkColumnHead'
+import { deleteColumn } from '../../actions'
+
+const mapStateToProps = state => {
+ return { columns: state.bookmarks.columns }
+}
+
+@connect(mapStateToProps)
+export default class ShowBookmarkColumnHead extends Component {
+ constructor (props) {
+ super(props)
+ this.state = { sticky: false }
+
+ this.handleDeleteColumn = this.handleDeleteColumn.bind(this)
+ this.onScroll = this.onScroll.bind(this)
+ }
+
+ componentDidMount () {
+ // Creating the event listener for a scroll event
+ window.addEventListener('scroll', this.onScroll, false)
+ }
+ componentWillUnmount () {
+ // Deleting the event listener for a scroll event
+ window.removeEventListener('scroll', this.onScroll, false)
+ }
+
+ // Changing column head style between position relative and fixed
+ onScroll () {
+ if (window.pageYOffset >= 106 && !this.state.sticky) {
+ this.setState({ sticky: true })
+ } else if (window.pageYOffset < 106 && this.state.sticky) {
+ this.setState({ sticky: false })
+ }
+ }
+
+ // Handling a click on column delete button
+ handleDeleteColumn (e) {
+ const { dispatch } = this.props
+ // delete any column except the Collection column
+ if (this.props.name !== 'Collection') {
+ const props = this.props
+ const columnCards = props.columns.filter(el => el.name === props.name)[0].cards
+ dispatch(deleteColumn(this.props.name, columnCards))
+ } else {
+ // message if trying delete the Collection column
+ alert("You can't delete the collection column")
+ }
+ }
+
+ render () {
+ return (
+ this.handleDeleteColumn(e)}
+ sticky={this.state.sticky} />
+ )
+ }
+}
diff --git a/src/app/containers/Columns/ShowBookmarkContainer.js b/src/app/containers/Columns/ShowBookmarkContainer.js
new file mode 100644
index 0000000..8ec35c2
--- /dev/null
+++ b/src/app/containers/Columns/ShowBookmarkContainer.js
@@ -0,0 +1,14 @@
+import React from 'react'
+import { connect } from 'react-redux'
+
+import BookmarkContainer from '../../components/Columns/BookmarkContainer'
+
+const ShowBookmarkContainer = ({ columns }) => (
+
+)
+
+const mapStateToProps = state => {
+ return { columns: state.bookmarks.columns }
+}
+
+export default connect(mapStateToProps)(ShowBookmarkContainer)
diff --git a/src/app/containers/DropDown/DropDownInfoString.js b/src/app/containers/DropDown/DropDownInfoString.js
new file mode 100644
index 0000000..8e2fcfa
--- /dev/null
+++ b/src/app/containers/DropDown/DropDownInfoString.js
@@ -0,0 +1,19 @@
+import React, { Component } from 'react'
+
+import InfoString from '../../components/DropDown/InfoString'
+import { wasUpdated, showStars, showIssues, showLicense } from '../../additional/additionalFunc'
+
+export default class DropDownInfoString extends Component {
+ constructor (props) {
+ super(props)
+ }
+
+ render () {
+ return (
+
+ )
+ }
+}
diff --git a/src/app/containers/DropDown/ShowDropDown.js b/src/app/containers/DropDown/ShowDropDown.js
new file mode 100644
index 0000000..ce65f6d
--- /dev/null
+++ b/src/app/containers/DropDown/ShowDropDown.js
@@ -0,0 +1,56 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+
+import { dropdownHide } from '../../actions/'
+import DropDown from '../../components/DropDown/DropDown'
+
+const mapStateToProps = state => {
+ return {
+ items: state.search.items,
+ dropdown: state.search.dropdown
+ }
+}
+
+@connect(mapStateToProps)
+export default class ShowDropDown extends Component {
+ constructor (props) {
+ super(props)
+
+ this.handleClick = this.handleClick.bind(this)
+ }
+
+ componentWillMount () {
+ // Creating the event listener for mousedown event
+ document.addEventListener('mousedown', this.handleClick, false)
+ }
+
+ componentWillUnmount () {
+ // Deleting the event listener for mousedown event
+ document.removeEventListener('mousedown', this.handleClick, false)
+ }
+
+ // Getting ref of the dropdown block
+ ddBox = React.createRef();
+
+ // Handlick a click outside the dropdown block
+ handleClick = (e) => {
+ const { dispatch } = this.props
+ // Hide the dropdown if the click was outside the block
+ if (this.props.dropdown && !this.ddBox.current.contains(e.target)) {
+ dispatch(dropdownHide())
+ }
+ }
+
+ render () {
+ return (
+
+ {
+ (this.props.dropdown)
+ ?
+ : null
+ }
+
+ )
+ }
+}
diff --git a/src/app/containers/DropDown/ShowDropDownButton.js b/src/app/containers/DropDown/ShowDropDownButton.js
new file mode 100644
index 0000000..b4bfa05
--- /dev/null
+++ b/src/app/containers/DropDown/ShowDropDownButton.js
@@ -0,0 +1,46 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+import { addBookmark } from '../../actions'
+
+import DropDownButton from '../../components/DropDown/DropDownButton'
+
+const mapStateToProps = state => {
+ return { columns: state.bookmarks.columns,
+ items: state.search.items }
+}
+
+@connect(mapStateToProps)
+export default class ShowDropDownButton extends Component {
+ constructor (props) {
+ super(props)
+
+ this.handleClickButton = this.handleClickButton.bind(this)
+ }
+
+ // Adding a bookmark by click on the button
+ handleClickButton (e) {
+ const { dispatch } = this.props
+ if (!this.hasBookmark(this.props.itemId, this.props.columns)) {
+ let bookmarkItem = {}
+ this.props.items.forEach((el, i) => {
+ if (el.id === this.props.itemId) {
+ bookmarkItem = this.props.items[i]
+ }
+ })
+ dispatch(addBookmark(bookmarkItem))
+ }
+ }
+
+ // Checking the current bookmark is added or not
+ hasBookmark (id, columns) {
+ const result = columns.every(el => el.cards.includes(id) === false)
+ return !result
+ }
+
+ render () {
+ return (
+ this.handleClickButton(e)}
+ added={this.hasBookmark(this.props.itemId, this.props.columns)} />
+ )
+ }
+}
diff --git a/src/app/containers/Search/ShowSearchInput.js b/src/app/containers/Search/ShowSearchInput.js
new file mode 100644
index 0000000..c1b4020
--- /dev/null
+++ b/src/app/containers/Search/ShowSearchInput.js
@@ -0,0 +1,39 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+import { searchOnchange, searchFetchQuery } from '../../actions'
+import SearchInput from '../../components/Search/SearchInput'
+
+const mapStateToProps = state => {
+ return { searchtext: state.search.searchtext }
+}
+
+@connect(mapStateToProps)
+export default class ShowSearchInput extends Component {
+ constructor (props) {
+ super(props)
+
+ this.handleOnChange = this.handleOnChange.bind(this)
+ }
+
+ // Handling changing of search input value
+ handleOnChange (e) {
+ const { dispatch } = this.props
+ // if the value has more than 2 symbols
+ if (e.target.value.length < 2) {
+ dispatch(searchOnchange(e.target.value))
+ } else {
+ dispatch(searchOnchange(e.target.value))
+ dispatch(searchFetchQuery(e.target.value))
+ }
+ }
+
+ render () {
+ return (
+
+
+
+ )
+ }
+}
diff --git a/src/app/global.js b/src/app/global.js
index 0a9a538..a1aef05 100644
--- a/src/app/global.js
+++ b/src/app/global.js
@@ -1,27 +1,12 @@
// @flow
+
import { injectGlobal } from 'styled-components'
injectGlobal`
@import url('https://fonts.googleapis.com/css?family=Open+Sans:300,400');
- * {
- box-sizing: border-box;
- }
-
html, body, #root {
- width: 100%;
- height: 100%;
- margin: 0;
- font-weight: 300;
font-family: 'Open Sans', sans-serif;
}
- form {
- margin: 0;
- }
-
- #modal-root {
- position: relative;
- z-index: 999;
- }
`
diff --git a/src/app/index.js b/src/app/index.js
index 288598b..45e4fea 100644
--- a/src/app/index.js
+++ b/src/app/index.js
@@ -1,5 +1,5 @@
// @flow
-import React from 'react'
+import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { HashRouter as Router, Switch, Redirect, Route } from 'react-router-dom'
@@ -9,7 +9,7 @@ import { routes } from './routes'
import './global'
-class Application extends React.PureComponent<{||}> {
+class Application extends Component {
render () {
return (
@@ -29,4 +29,5 @@ class Application extends React.PureComponent<{||}> {
}
ReactDOM.render(
- , (document.getElementById('root'): any))
+ , (document.getElementById('root'))
+)
diff --git a/src/app/reducer.js b/src/app/reducer.js
index 1933a8c..9dfd682 100644
--- a/src/app/reducer.js
+++ b/src/app/reducer.js
@@ -1,6 +1,9 @@
// @flow
-import { combineReducers } from 'redux-immutable'
+import { combineReducers } from 'redux'
+import search from './reducers/search'
+import bookmarks from './reducers/bookmarks'
export const reducer = combineReducers({
-
+ search,
+ bookmarks
})
diff --git a/src/app/reducers/bookmarks.js b/src/app/reducers/bookmarks.js
new file mode 100644
index 0000000..8590dc5
--- /dev/null
+++ b/src/app/reducers/bookmarks.js
@@ -0,0 +1,44 @@
+import update from 'immutability-helper'
+import { loadState } from '../additional/additionalFunc'
+
+import { ADD_BOOKMARK,
+ DELETE_BOOKMARK,
+ MOVE_BOOKMARK,
+ COLUMN_NAME_ONCHANGE,
+ ADD_COLUMN,
+ DELETE_COLUMN } from '../actions'
+
+// loading state from the Local Storage
+const persistedState = loadState()
+
+const bookmarks = (state = persistedState.bookmarks, action) => {
+ switch (action.type) {
+ case MOVE_BOOKMARK:
+ return update(state, {
+ columns: {
+ $set: state.columns.map(el => el.name === action.idColumnName ? update(el, { cards: { $splice: [[action.indexOfId, 1]] } }) : el)
+ .map(el => el.name === action.atIndexColumnName ? update(el, { cards: { $splice: [[action.indexOfatIndex, 0, action.id]] } }) : el)
+ }
+ })
+ case ADD_BOOKMARK:
+ return update(state, { list: { $set: { ...state.list, [action.item.id]: action.item } },
+ columns: { 0: { cards: { $push: [action.item.id] } } }
+ })
+ case DELETE_BOOKMARK:
+ return update(state, { list: { $unset: [action.id] },
+ columns: { $set: state.columns.map(el => (el.name === action.column) ? { name: el.name, cards: el.cards.filter(arrEl => arrEl !== action.id) } : el) }})
+ case COLUMN_NAME_ONCHANGE:
+ return update(state, { name: { $set: action.text } })
+ case ADD_COLUMN:
+ return update(state, { columns: { $push: [{ name: action.name, cards: [] }] },
+ name: { $set: '' }
+ })
+ case DELETE_COLUMN:
+ return update(state, { columns: { $set: state.columns.filter(el => el.name !== action.name)
+ .map((el, i) => i === 0 ? update(el, { cards: { $push: action.cards } }) : el) } })
+ default:
+ return state
+ }
+}
+
+export default bookmarks
diff --git a/src/app/reducers/search.js b/src/app/reducers/search.js
new file mode 100644
index 0000000..33c81b7
--- /dev/null
+++ b/src/app/reducers/search.js
@@ -0,0 +1,26 @@
+import update from 'immutability-helper'
+import { SEARCH_ONCHANGE,
+ SEARCH_RECEIVE_DATA,
+ DROPDOWN_SHOW,
+ DROPDOWN_HIDE } from '../actions'
+
+// The valuse of initial state
+const initialState = { searchtext: '', items: [], dropdown: false }
+
+const search = (state = initialState, action) => {
+ switch (action.type) {
+ case SEARCH_ONCHANGE:
+ return update(state, { searchtext: { $set: action.data } })
+ case SEARCH_RECEIVE_DATA:
+ return update(state, { items: { $set: action.items },
+ dropdown: { $set: true } })
+ case DROPDOWN_SHOW:
+ return update(state, { dropdown: { $set: true } })
+ case DROPDOWN_HIDE:
+ return update(state, { dropdown: { $set: false } })
+ default:
+ return state
+ }
+}
+
+export default search
diff --git a/src/app/store.js b/src/app/store.js
index d59f62e..fe7c698 100644
--- a/src/app/store.js
+++ b/src/app/store.js
@@ -3,7 +3,15 @@ import { createStore } from 'redux'
import { middleware } from './middleware'
import { reducer } from './reducer'
+import { saveState } from './additional/additionalFunc'
export const store = createStore(reducer, middleware)
+// Saving the store changing to the Local Storage
+store.subscribe(() => {
+ saveState({
+ bookmarks: store.getState().bookmarks
+ })
+})
+
window.store = store