From 1d73c9fc10f8fe0c76f1f7dcf7412b9e5fdc10f2 Mon Sep 17 00:00:00 2001 From: Nicholas Zuber Date: Sun, 31 Mar 2019 22:13:30 -0400 Subject: [PATCH] Add a lot of the functionality --- src/components/Logo/icon-black.png | Bin 0 -> 29125 bytes src/components/Logo/index.js | 2 +- src/pages/Notifications/Scene.js | 1208 ++++++++++----------- src/pages/Notifications/index.js | 31 +- src/pages/Notifications/redesign/Scene.js | 425 ++++++-- src/providers/Notifications.js | 26 +- src/styles/index.css | 3 +- 7 files changed, 988 insertions(+), 707 deletions(-) create mode 100644 src/components/Logo/icon-black.png diff --git a/src/components/Logo/icon-black.png b/src/components/Logo/icon-black.png new file mode 100644 index 0000000000000000000000000000000000000000..f0c89fe079ccb9271f5543e842b4b0e952acb18c GIT binary patch literal 29125 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 10) { - // probably prompt to mark all as read to start out since they prob don't use notifs - } + // if (isFirstTimeUser && notifications.length > 10) { + // // probably prompt to mark all as read to start out since they prob don't use notifs + // } - stagedStatistics = stagedStatistics.map(n => parseInt(n, 10)); + // stagedStatistics = stagedStatistics.map(n => parseInt(n, 10)); - const highestStagedCount = stagedStatistics.reduce((n, m) => Math.max(n, m), 0); - let lastWeekStats = stagedStatistics.slice(0, 7); - let thisWeekStats = stagedStatistics.slice(7); + // const highestStagedCount = stagedStatistics.reduce((n, m) => Math.max(n, m), 0); + // let lastWeekStats = stagedStatistics.slice(0, 7); + // let thisWeekStats = stagedStatistics.slice(7); - // Trim off the weekends. - lastWeekStats = lastWeekStats.slice(1, -1); - thisWeekStats = thisWeekStats.slice(1, -1); + // // Trim off the weekends. + // lastWeekStats = lastWeekStats.slice(1, -1); + // thisWeekStats = thisWeekStats.slice(1, -1); return ( ); - return ( -
- - - -
-
- - -
-

- - {currentTime.format('h:mma')} -

- {currentTime.format('dddd, MMMM Do')} - You've triaged {stagedTodayCount} notifications today -
- onSetActiveFilter(Filters.PARTICIPATING)}> - {activeFilter === Filters.PARTICIPATING ? ( - - ) : ( - - )} - all your updates - - onSetActiveFilter(Filters.REVIEW_REQUESTED)}> - {activeFilter === Filters.REVIEW_REQUESTED ? ( - - ) : ( - - )} - review requested - - onSetActiveFilter(Filters.ASSIGNED)}> - {activeFilter === Filters.ASSIGNED ? ( - - ) : ( - - )} - assigned - - onSetActiveFilter(Filters.COMMENT)}> - {activeFilter === Filters.COMMENT ? ( - - ) : ( - - )} - commented - - -
- - {/* Last week's statistics */} - - {lastWeekStats.map((dayStats, i) => ( - - ))} - - {/* This week's ongoing statistics */} - - {thisWeekStats.map((dayStats, i) => ( - = i + 1} - /> - ))} - - {/* Wrapper for tooltips */} - {/* - {thisWeekStats.map((dayStats, i) => ( - - ))} - */} - -
- -
- Report bugs - Submit feedback - See source code -
-
-
-
-
- - - onFetchNotifications()) : undefined} - /> - - - { - const response = window.confirm('Are you sure you want to mark all your notifications as read?'); - if (response) { - onMarkAllAsStaged(); - } - }) : undefined} - /> - - - { - const response = window.confirm('Are you sure you want to clear the cache?'); - if (response) { - onClearCache(); - } - }) : undefined} - /> - - - { - switch(notificationsPermission) { - case 'granted': - return setNotificationsPermission('denied'); - case 'denied': - case 'default': - default: - Notification.requestPermission().then(result => { - return setNotificationsPermission(result); - }); - } - }) : undefined} - /> - - {query ? ( - - - - onClearQuery()) : undefined} - /> - - - ) : null} -
- - - onChangePage(page - 1)) : undefined} - /> - - - onChangePage(page + 1)) : undefined} - /> - -
-
- - onSetActiveStatus(Status.QUEUED)} - href="javascript:void(0);"> - Unread - - onSetActiveStatus(Status.STAGED)} - href="javascript:void(0);"> - Read - - onSetActiveStatus(Status.CLOSED)} - href="javascript:void(0);"> - Archived - - - - - {isFetchingNotifications ? ( - - - - ) : fetchingNotificationsError ? ( - - - An error occurred when fetching notifications.
- onFetchNotifications()} href="#">Try again? -
-
- ) : notifications.length <= 0 ? ( - -

- No - {activeStatus === Status.QUEUED ? ( - ' unread ' - ) : activeStatus === Status.STAGED ? ( - ' read ' - ) : ( - ' archived ' - )} - notifications -

-

- 🎉 You're all set here for the moment

-
- ) : ( - - - - {notifications.map(n => ( - - -
- {getPRIssueIcon(n.type, n.reasons)} -
-
- { - window.open(n.url); - onStageThread(n.id, n.repository) - }}> - - {n.name} - - - {getRelativeTime(n.updated_at)} - {n.isAuthor && ( - - )} - - - - {getMessageFromReasons(n.reasons, n.type)} - - - - - {activeStatus === Status.QUEUED && n.badges.map(badge => { - switch (badge) { - case Badges.HOT: - // lots of `reasons` within short time frame - return ( - - ); - case Badges.OLD: - // old - return ( - - ); - case Badges.COMMENTS: - // lots of `reasons` - return ( - - ); - default: - return null; - } - })} - - - - window.open(n.repositoryUrl)} - style={{cursor: 'pointer', userSelect: 'none'}}> - {n.repository} - - - - +{n.score} - - {activeStatus === Status.QUEUED ? ( - - onStageThread(n.id, n.repository)) : undefined} - /> - - ) : ( - - onRestoreThread(n.id)) : undefined} - /> - - )} - {activeStatus === Status.CLOSED ? ( - -   - - ) : ( - - onMarkAsRead(n.id, n.repository)) : undefined} - /> - - )} - -
- ))} - -
- {!loading && Page {page} out of {lastPage}} -
- )} -
-
-
-
-
- ); + // return ( + //
+ // + //
+ // { + // onSetActiveStatus(Status.QUEUED); + // onSetActiveFilter(Filters.PARTICIPATING); + // }} + // /> + // beta + // + // + // event.target.select()} + // type="text" + // placeholder="Search for notifications" + // onEnter={onSearch} + // /> + // {isSearching && } + // + //
+ // home + //
+ //
+ // sign out + //
+ //
+ //
+ //
+ //
+ // + // + //
+ //

+ // + // {currentTime.format('h:mma')} + //

+ // {currentTime.format('dddd, MMMM Do')} + // You've triaged {stagedTodayCount} notifications today + //
+ // onSetActiveFilter(Filters.PARTICIPATING)}> + // {activeFilter === Filters.PARTICIPATING ? ( + // + // ) : ( + // + // )} + // all your updates + // + // onSetActiveFilter(Filters.REVIEW_REQUESTED)}> + // {activeFilter === Filters.REVIEW_REQUESTED ? ( + // + // ) : ( + // + // )} + // review requested + // + // onSetActiveFilter(Filters.ASSIGNED)}> + // {activeFilter === Filters.ASSIGNED ? ( + // + // ) : ( + // + // )} + // assigned + // + // onSetActiveFilter(Filters.COMMENT)}> + // {activeFilter === Filters.COMMENT ? ( + // + // ) : ( + // + // )} + // commented + // + // + //
+ // + // {/* Last week's statistics */} + // + // {lastWeekStats.map((dayStats, i) => ( + // + // ))} + // + // {/* This week's ongoing statistics */} + // + // {thisWeekStats.map((dayStats, i) => ( + // = i + 1} + // /> + // ))} + // + // {/* Wrapper for tooltips */} + // {/* + // {thisWeekStats.map((dayStats, i) => ( + // + // ))} + // */} + // + //
+ // + //
+ // Report bugs + // Submit feedback + // See source code + //
+ //
+ //
+ //
+ //
+ // + // + // onFetchNotifications()) : undefined} + // /> + // + // + // { + // const response = window.confirm('Are you sure you want to mark all your notifications as read?'); + // if (response) { + // onMarkAllAsStaged(); + // } + // }) : undefined} + // /> + // + // + // { + // const response = window.confirm('Are you sure you want to clear the cache?'); + // if (response) { + // onClearCache(); + // } + // }) : undefined} + // /> + // + // + // { + // switch(notificationsPermission) { + // case 'granted': + // return setNotificationsPermission('denied'); + // case 'denied': + // case 'default': + // default: + // Notification.requestPermission().then(result => { + // return setNotificationsPermission(result); + // }); + // } + // }) : undefined} + // /> + // + // {query ? ( + // + // + // + // onClearQuery()) : undefined} + // /> + // + // + // ) : null} + //
+ // + // + // onChangePage(page - 1)) : undefined} + // /> + // + // + // onChangePage(page + 1)) : undefined} + // /> + // + //
+ //
+ // + // onSetActiveStatus(Status.QUEUED)} + // href="javascript:void(0);"> + // Unread + // + // onSetActiveStatus(Status.STAGED)} + // href="javascript:void(0);"> + // Read + // + // onSetActiveStatus(Status.CLOSED)} + // href="javascript:void(0);"> + // Archived + // + // + // + // + // {isFetchingNotifications ? ( + // + // + // + // ) : fetchingNotificationsError ? ( + // + // + // An error occurred when fetching notifications.
+ // onFetchNotifications()} href="#">Try again? + //
+ //
+ // ) : notifications.length <= 0 ? ( + // + //

+ // No + // {activeStatus === Status.QUEUED ? ( + // ' unread ' + // ) : activeStatus === Status.STAGED ? ( + // ' read ' + // ) : ( + // ' archived ' + // )} + // notifications + //

+ //

+ // 🎉 You're all set here for the moment

+ //
+ // ) : ( + // + // + // + // {notifications.map(n => ( + // + // + //
+ // {getPRIssueIcon(n.type, n.reasons)} + //
+ //
+ // { + // window.open(n.url); + // onStageThread(n.id, n.repository) + // }}> + // + // {n.name} + // + // + // {getRelativeTime(n.updated_at)} + // {n.isAuthor && ( + // + // )} + // + // + // + // {getMessageFromReasons(n.reasons, n.type)} + // + // + // + // + // {activeStatus === Status.QUEUED && n.badges.map(badge => { + // switch (badge) { + // case Badges.HOT: + // // lots of `reasons` within short time frame + // return ( + // + // ); + // case Badges.OLD: + // // old + // return ( + // + // ); + // case Badges.COMMENTS: + // // lots of `reasons` + // return ( + // + // ); + // default: + // return null; + // } + // })} + // + // + // + // window.open(n.repositoryUrl)} + // style={{cursor: 'pointer', userSelect: 'none'}}> + // {n.repository} + // + // + // + // +{n.score} + // + // {activeStatus === Status.QUEUED ? ( + // + // onStageThread(n.id, n.repository)) : undefined} + // /> + // + // ) : ( + // + // onRestoreThread(n.id)) : undefined} + // /> + // + // )} + // {activeStatus === Status.CLOSED ? ( + // + //   + // + // ) : ( + // + // onMarkAsRead(n.id, n.repository)) : undefined} + // /> + // + // )} + // + //
+ // ))} + // + //
+ // {!loading && Page {page} out of {lastPage}} + //
+ // )} + //
+ //
+ //
+ //
+ //
+ // ); } diff --git a/src/pages/Notifications/index.js b/src/pages/Notifications/index.js index 460de44..28b9062 100644 --- a/src/pages/Notifications/index.js +++ b/src/pages/Notifications/index.js @@ -148,7 +148,8 @@ class NotificationsPage extends React.Component { activeStatus: View.UNREAD, currentPage: 1, sort: Sort.SCORE, - descending: false + descending: false, + user: null } componentDidMount () { @@ -160,6 +161,9 @@ class NotificationsPage extends React.Component { } this.props.notificationsApi.fetchNotifications(); + this.props.notificationsApi.requestUser().then(user => { + this.setState({user}); + }); this.tabSyncer = setInterval(() => { if (!document.hidden && this.isUnreadTab) { @@ -216,6 +220,7 @@ class NotificationsPage extends React.Component { // Ignore empty queries. if (text.length <= 0) { + this.onClearQuery(); return; } @@ -335,9 +340,9 @@ class NotificationsPage extends React.Component { const filteredNotifications = notifications.filter(filterMethod); - const notificationsQueued = filteredNotifications.filter(n => n.status === Status.QUEUED); - const notificationsStaged = filteredNotifications.filter(n => n.status === Status.STAGED); - const notificationsClosed = filteredNotifications.filter(n => n.status === Status.CLOSED); + let notificationsQueued = filteredNotifications.filter(n => n.status === Status.QUEUED); + let notificationsStaged = filteredNotifications.filter(n => n.status === Status.STAGED); + let notificationsClosed = filteredNotifications.filter(n => n.status === Status.CLOSED); let notificationsToRender = []; switch (this.state.activeStatus) { @@ -389,7 +394,16 @@ class NotificationsPage extends React.Component { if (this.state.query) { scoredAndSortedNotifications = scoredAndSortedNotifications.filter(n => ( n.name.toLowerCase().indexOf(this.state.query.toLowerCase()) > -1) - ) + ); + notificationsQueued = notificationsQueued.filter(n => ( + n.name.toLowerCase().indexOf(this.state.query.toLowerCase()) > -1) + ); + notificationsStaged = notificationsStaged.filter(n => ( + n.name.toLowerCase().indexOf(this.state.query.toLowerCase()) > -1) + ); + notificationsClosed = notificationsClosed.filter(n => ( + n.name.toLowerCase().indexOf(this.state.query.toLowerCase()) > -1) + ); } if (this.props.notificationsApi.newChanges) { @@ -465,9 +479,9 @@ class NotificationsPage extends React.Component { isFirstTimeUser={this.state.isFirstTimeUser} setNotificationsPermission={this.setNotificationsPermission} notificationsPermission={notificationsPermission} - queuedCount={queuedCount} - stagedCount={stagedCount} - closedCount={closedCount} + unreadCount={queuedCount} + readCount={stagedCount} + archivedCount={closedCount} stagedTodayCount={stagedTodayCount || 0} first={firstNumbered} last={lastNumbered} @@ -503,6 +517,7 @@ class NotificationsPage extends React.Component { setDescending={descending => this.setState({descending})} view={this.state.activeStatus} setView={this.onSetActiveStatus} + user={this.state.user} /> ); } diff --git a/src/pages/Notifications/redesign/Scene.js b/src/pages/Notifications/redesign/Scene.js index 8b9c101..ca6a6b4 100644 --- a/src/pages/Notifications/redesign/Scene.js +++ b/src/pages/Notifications/redesign/Scene.js @@ -3,10 +3,12 @@ import React from 'react'; import moment from 'moment'; import styled from '@emotion/styled'; -import {css, jsx} from '@emotion/core'; +import {css, jsx, keyframes} from '@emotion/core'; import {useSpring, useTransition, animated} from 'react-spring' import Icon from '../../../components/Icon'; +import Logo from '../../../components/Logo'; import LoadingIcon from '../../../components/LoadingIcon'; +import {Reasons, Badges} from '../../../constants/reasons'; import {withOnEnter} from '../../../enhance'; import {Sort, View} from '../index'; @@ -19,8 +21,21 @@ const Mode = { COMMENTED: 2 }; -// @TODO if GitHub ever fixes their API, we can use `reasons` to know when -// a PR/Issue merges/closes/etc. +// ======================================================================== +// START OF 'MOVE TO A UTILS FILE' +// ======================================================================== + +function stringOfType (type) { + switch (type) { + case 'PullRequest': + return 'pull request'; + case 'Issue': + return 'issue'; + default: + return 'task'; + } +} + function getPRIssueIcon (type, _reasons) { const scale = 1.5; switch (type) { @@ -43,21 +58,73 @@ function getPRIssueIcon (type, _reasons) { return null; } } +function getRelativeTime (time) { + const currentTime = moment(); + const targetTime = moment(time); + const diffMinutes = currentTime.diff(targetTime, 'minutes'); + if (diffMinutes < 1) + return 'Just now'; + if (diffMinutes < 5) + return 'Few minutes ago'; + if (diffMinutes < 60) + return diffMinutes + ' minutes ago'; + if (diffMinutes < 60 * 24) + return Math.floor(diffMinutes / 60) + ' hours ago'; -function colorOfScore (score, min, max) { - const ratio = (score - min) / (max - min); - if (ratio > .9) return '#ec1461'; - if (ratio > .8) return '#ec5314'; - if (ratio > .7) return '#ec5314'; - if (ratio > .6) return '#ec7b14'; - if (ratio > .5) return '#ec7b14'; - if (ratio > .4) return '#ec9914'; - if (ratio > .3) return '#ec9914'; - if (ratio > .2) return '#ecad14'; - if (ratio > .1) return '#ecad14'; - return '#ecc114'; + const diffDays = currentTime.diff(targetTime, 'days'); + if (diffDays === 1) + return 'Yesterday'; + if (diffDays <= 7) + return 'Last ' + targetTime.format('dddd'); + // @TODO implement longer diffs + return 'Over a week ago'; } +function getMessageFromReasons (reasons, type) { + switch (reasons[reasons.length - 1].reason) { + case Reasons.ASSIGN: + return 'You were assigned'; + case Reasons.AUTHOR: + return 'There was activity on this thread you created'; + case Reasons.COMMENT: + return 'Somebody left a comment'; + case Reasons.MENTION: + return 'You were mentioned'; + case Reasons.REVIEW_REQUESTED: + return 'Your review was requested'; + case Reasons.SUBSCRIBED: + return 'There was an update and you\'re subscribed'; + case Reasons.OTHER: + default: + return 'Something was updated'; + } +} +// ======================================================================== +// END OF 'MOVE TO A UTILS FILE' +// ======================================================================== + +function createColorOfScore (min, max) { + return function (score) { + const ratio = (score - min) / (max - min); + if (ratio > .9) return '#ec1461'; + if (ratio > .8) return '#ec5314'; + if (ratio > .7) return '#ec5314'; + if (ratio > .6) return '#ec7b14'; + if (ratio > .5) return '#ec7b14'; + if (ratio > .4) return '#ec9914'; + if (ratio > .3) return '#ec9914'; + if (ratio > .2) return '#ecad14'; + if (ratio > .1) return '#ecad14'; + return '#ecc114'; + } +} + +const loadingKeyframe = keyframes` + 100% { + transform: translateX(100%); + } +`; + const Container = styled('div')` position: relative; display: block; @@ -219,7 +286,6 @@ const SearchField = styled('div')` position: relative; float: left; text-align: left; - // box-shadow: rgba(84,70,35,0.01) 0px 2px 19px 8px, rgba(84, 70, 35, 0.11) 0px 2px 12px; align-items: center; height: 36px; font-size: 13px; @@ -236,6 +302,7 @@ const SearchField = styled('div')` } &:focus-within { border: 1px solid #457cff; + box-shadow: rgba(84,70,35,0.01) 0px 2px 19px 8px, rgba(84, 70, 35, 0.11) 0px 2px 12px; } i { color: #bfc5d1; @@ -271,7 +338,7 @@ const SearchInput = styled('input')` &:focus { opacity: 1; color: rgb(55, 53, 47); - width: 500px; + width: 300px; } `; const EnhancedSearchInput = withOnEnter(SearchInput); @@ -415,7 +482,7 @@ const NotificationRow = styled(NotificationRowHeader)` position: relative; background: ${WHITE}; border-radius: 4px; - padding: 2px 18px; + padding: 6px 18px; font-size: 14px; margin-bottom: 12px; border-radius: 6px; @@ -424,14 +491,42 @@ const NotificationRow = styled(NotificationRowHeader)` transition: all 200ms ease; &:hover { box-shadow: rgba(84,70,35,0.01) 0px 2px 19px 8px, rgba(84, 70, 35, 0.11) 0px 2px 12px; - // background: rgb(252, 250, 248); + }; + &:active { + background: rgb(252, 250, 248); }; `; -const NotificationBlock = styled('tbody')` - +const LoadingNotificationRow = styled(NotificationRowHeader)` + position: relative; + background: ${WHITE}; + height: 62px; + overflow: hidden; + border-radius: 4px; + padding: 6px 18px; + font-size: 14px; + margin-bottom: 12px; + border-radius: 6px; + cursor: pointer; + user-select: none; + transition: all 200ms ease; + opacity: 0.75; + &:after { + background: linear-gradient(90deg, transparent, #f9f8f5, transparent); + display: block; + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + transform: translateX(-100%); + animation: ${loadingKeyframe} 2.0s infinite; + } `; +const NotificationBlock = styled('tbody')``; + const AnimatedNotificationRow = animated(NotificationRow); const AnimatedNotificationsBlock = animated(NotificationBlock); @@ -448,6 +543,65 @@ const NotificationCell = styled('td')` }}; `; +const NotificationTitle = styled('span')``; +const NotificationByline = styled('span')` + display: block; + margin-top: 4px; + font-size: 12px; + color: #8893a7cc; + i { + margin-right: 4px; + font-size: 10px; + color: #8893a7cc; + } + span { + margin-left: 12px; + font-size: 12px; + font-weight: 500; + color: #8893a7cc; + i { + margin-right: 4px; + font-size: 9px; + color: #8893a7cc; + } + } +`; + +const ProfileContainer = styled('div')` + display: flex; + justify-content: center; + align-items: center; + border-left: 1px solid #edeef0; + padding: 0 22px; + position: absolute; + right: 0; + transition: all 200ms ease; + user-select: none; + cursor: pointer; + i { + transition: all 200ms ease; + color: #bfc5d1a3 + } + &:hover { + background: rgba(233, 233, 233, .25); + i { + color: #bfc5d1 + } + } +`; + +const ProfileName = styled('span')` + font-size: 14px; + font-weight: 500; + margin: 0 12px; +`; + +const ProfilePicture = styled('img')` + height: 36px; + width: 36px; + border-radius: 4px; +`; + const NotificationIconWrapper = styled('div')` background: #DBE7FF; width: 48px; @@ -535,7 +689,7 @@ function SortingItem ({children, selected, onChange, descending, setDescending, if (selected) { setDescending(!descending); } else { - setDescending(true); + setDescending(false); } onChange(props.sort) }} @@ -576,7 +730,14 @@ export default function Scene ({ onClearQuery, onSearch, isSearching, + user, + onFetchNotifications, + onMarkAllAsStaged, + onClearCache, + setNotificationsPermission, + onStageThread, }) { + console.warn('unreadCount', unreadCount) const [menuOpen, setMenuOpen] = React.useState(false); const [dropdownOpen, setDropdownOpen] = React.useState(false); const [mode, setMode] = React.useState(Mode.ALL); @@ -603,15 +764,6 @@ export default function Scene ({ // } // }); - const props = useSpring({ - from: {opacity: 0, transform: 'translate3d(50px, 0, 0)'}, - to: {opacity: 1, transform: 'translate3d(0, 0, 0)'}, - config: { - tension: 300, - duration: 200, - } - }); - React.useEffect(() => { const body = window.document.querySelector('body'); const hideDropdownMenu = () => setDropdownOpen(false); @@ -621,7 +773,14 @@ export default function Scene ({ return ( - + } + { + window.scrollTo(0, 0); + }} + size={36} + /> + {user && ( + + + {user.name} + + + )} - + -
+
{ + event.stopPropagation(); + onFetchNotifications(); + setDropdownOpen(false); + }}>

Reload notifications

Manually fetch new notifications instead of waiting for the sync

-
+
{ + event.stopPropagation(); + const response = window.confirm('Are you sure you want to mark all your notifications as read?'); + void (response && onMarkAllAsStaged()); + setDropdownOpen(false); + }}>

Mark all as read

Move all your unread notifications to the read tab

-
+
{ + event.stopPropagation(); + const response = window.confirm('Are you sure you want to clear the cache?'); + void (response && onClearCache()); + setDropdownOpen(false); + }}>

Empty cache

Clear all the notifications that are being tracked in your local storage

-
+
{ + event.stopPropagation(); + switch(notificationsPermission) { + case 'granted': + return setNotificationsPermission('denied'); + case 'denied': + case 'default': + default: + Notification.requestPermission().then(result => { + return setNotificationsPermission(result); + }); + } + setDropdownOpen(false); + }}>

Turn {hasNotificationsOn ? 'off' : 'on'} notifications

{hasNotificationsOn @@ -740,6 +943,7 @@ export default function Scene ({ {'Unread'} {unreadCount > 0 && ( 0 && ( 0 && ( - - {notifications.map(item => ( - - {/* Type */} - - {getPRIssueIcon(item.type, item.reasons)} - - {/* Title */} - - {item.name} - - {/* Repository */} - - {'@' + item.repository} - - {/* Score */} - - {'+' + item.score} - - - - - - - - - - - ))} - + {loading ? ( + + + + + + + + + + ) : ( + + )} @@ -953,3 +1131,82 @@ export default function Scene ({ ); } + +function NotificationCollection ({ + notifications, + colorOfScore, + onTitleClick, + page +}) { + const props = useSpring({ + from: {opacity: 0}, + to: {opacity: 1}, + config: { + duration: 200, + } + }); + + return ( + + {notifications.map(item => ( + + {/* Type */} + + {getPRIssueIcon(item.type, item.reasons)} + + {/* Title */} + { + window.open(item.url); + onTitleClick(item.id, item.repository); + }} + css={css` + font-weight: 500; + `}> + + {item.name} + + + {getMessageFromReasons(item.reasons, item.type)} + {` ${getRelativeTime(item.updated_at).toLowerCase()}`} + + + {/* Repository */} + window.open(item.repositoryUrl)} + css={css` + font-weight: 500; + color: #8994A6; + `}> + {'@' + item.repository} + + {/* Score */} + + {'+' + item.score} + + + + + + + + + + + ))} + + ); +} diff --git a/src/providers/Notifications.js b/src/providers/Notifications.js index eea5bcb..3ea4e93 100644 --- a/src/providers/Notifications.js +++ b/src/providers/Notifications.js @@ -86,7 +86,9 @@ class NotificationsProvider extends React.Component { } // Only update if our notifications prop changes. // All other props "changing" should NOT trigger a rerender. - return this.props.notifications !== nextProps.notifications; + return ( + this.props.notifications !== nextProps.notifications + ); } // The web notificaitons API doesn't let users revoke notifications permission @@ -99,6 +101,27 @@ class NotificationsProvider extends React.Component { this.forceUpdate(); } + requestUser = () => { + const headers = { + 'Authorization': `token ${this.props.token}`, + 'Content-Type': 'application/json', + }; + + // @TODO probably add timestamp + const cachedUser = this.props.getUserItem('user-model'); + if (cachedUser) { + return Promise.resolve(cachedUser); + } + + return fetch(`${BASE_GITHUB_API_URL}/user`, { + method: 'GET', + headers: headers + }).then(processHeadersAndBodyJson) + .then(({json}) => { + this.props.setUserItem('user-model', json); + }); + } + requestPage = (page = 1, optimizePolling = true) => { // Fetch all notifications from a month ago, including ones that have been read. // We can tell in the response if a notification has been read or not, so we can @@ -395,6 +418,7 @@ class NotificationsProvider extends React.Component { render () { return this.props.children({ ...this.state, + requestUser: this.requestUser, notifications: this.props.notifications, fetchNotifications: this.fetchNotifications, fetchNotificationsSync: this.requestFetchNotifications, diff --git a/src/styles/index.css b/src/styles/index.css index 6c26c35..e9a8b05 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -18,12 +18,13 @@ ::selection { color: #fff; - background: #24292e; + background: #4880ff; } html, body { /* height: 100%; */ width: 100%; + scroll-behavior: smooth; } html, body, * {