From 2d98079896e405396c61f1d8031ab6f49ffe00e9 Mon Sep 17 00:00:00 2001 From: Nicholas Zuber Date: Fri, 9 Nov 2018 00:37:04 -0500 Subject: [PATCH] Many tickets and new readme --- .github/icon.png | Bin 0 -> 29125 bytes .github/template.png | Bin 0 -> 58242 bytes README.md | 6 +- package.json | 2 +- src/components/Icon/index.js | 18 + src/components/Icon/svg/bolt-alt.svg | 4 + src/components/Icon/svg/bubbles.svg | 1 + src/components/Icon/svg/cloudoff.svg | 1 + src/components/Icon/svg/eye-white.svg | 1 + src/components/Icon/svg/eye.svg | 1 + src/components/Icon/svg/sync.svg | 1 + src/components/Icon/svg/tag-white.svg | 1 + src/components/Icon/svg/tag.svg | 1 + src/components/Icon/svg/warn.svg | 1 + src/constants/filters.js | 1 + src/enhance/index.js | 2 +- src/pages/Home/Scene.js | 145 ++-- src/pages/Notifications/Scene.js | 992 ++++++++++++++++++-------- src/pages/Notifications/SceneAlt.js | 898 ----------------------- src/pages/Notifications/index.js | 20 +- src/providers/Notifications.js | 39 +- src/providers/Storage.js | 4 +- src/styles/gradient.css | 9 + 23 files changed, 905 insertions(+), 1243 deletions(-) create mode 100644 .github/icon.png create mode 100644 .github/template.png create mode 100644 src/components/Icon/svg/bolt-alt.svg create mode 100644 src/components/Icon/svg/bubbles.svg create mode 100644 src/components/Icon/svg/cloudoff.svg create mode 100644 src/components/Icon/svg/eye-white.svg create mode 100644 src/components/Icon/svg/eye.svg create mode 100644 src/components/Icon/svg/sync.svg create mode 100644 src/components/Icon/svg/tag-white.svg create mode 100644 src/components/Icon/svg/tag.svg create mode 100644 src/components/Icon/svg/warn.svg delete mode 100644 src/pages/Notifications/SceneAlt.js diff --git a/.github/icon.png b/.github/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f0c89fe079ccb9271f5543e842b4b0e952acb18c GIT binary patch literal 29125 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%0DLhd;5m^{mi&N;xun;^Lv~SNDSdL{ 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 U9{f-hSjDn~`^@*|?s2&KU)-8Wga7~l literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 9ec7549..a54ac7c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ -# meteorite +# meteorite > Smarter GitHub notifications. +
+ +
+ ## License This software is free to use under the MIT License. See [this reference](https://opensource.org/licenses/MIT) for license text and copyright information. diff --git a/package.json b/package.json index fdf803b..0d2e3e9 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "start": "node scripts/start.js", "build": "node scripts/build.js && cp build/index.html build/200.html", "test": "node scripts/test.js", - "deploy": "surge" + "deploy": "npm run build && surge ./build" }, "eslintConfig": { "extends": "react-app" diff --git a/src/components/Icon/index.js b/src/components/Icon/index.js index 668729c..3699edd 100644 --- a/src/components/Icon/index.js +++ b/src/components/Icon/index.js @@ -5,6 +5,7 @@ import alarm from './svg/alarm.svg'; import allInbox from './svg/all_inbox.svg'; import back from './svg/back.svg'; import bolt from './svg/bolt.svg'; +import boltAlt from './svg/bolt-alt.svg'; import boltWhite from './svg/bolt-white.svg'; import bookmarkAlt from './svg/bookmark-alt.svg'; import bookmarkAltWhite from './svg/bookmark-alt-white.svg'; @@ -43,6 +44,14 @@ import x from './svg/x.svg'; import zap from './svg/zap.svg'; import ear from './svg/ear.svg'; import ring from './svg/ring.svg'; +import eye from './svg/eye.svg'; +import eyeWhite from './svg/eye-white.svg'; +import warn from './svg/warn.svg'; +import bubbles from './svg/bubbles.svg'; +import cloudOff from './svg/cloudoff.svg'; +import tag from './svg/tag.svg'; +import tagWhite from './svg/tag-white.svg'; +import sync from './svg/sync.svg'; import issue_closed from './svg/github/issue-closed.svg'; import issue_open from './svg/github/issue-open.svg'; @@ -76,6 +85,7 @@ Icon.AllInbox = createIcon(allInbox); Icon.Back = createIcon(back); Icon.Bolt = createIcon(bolt); Icon.BoltWhite = createIcon(boltWhite); +Icon.BoltAlt = createIcon(boltAlt); Icon.BookmarkAlt = createIcon(bookmarkAlt); Icon.BookmarkAltWhite = createIcon(bookmarkAltWhite); Icon.Bookmark = createIcon(bookmark); @@ -113,6 +123,14 @@ Icon.X = createIcon(x); Icon.Zap = createIcon(zap); Icon.Ear = createIcon(ear); Icon.Ring = createIcon(ring); +Icon.Eye = createIcon(eye); +Icon.EyeWhite = createIcon(eyeWhite); +Icon.Warn = createIcon(warn); +Icon.Bubbles = createIcon(bubbles); +Icon.CloudOffWhite = createIcon(cloudOff); +Icon.Tag = createIcon(tag); +Icon.TagWhite = createIcon(tagWhite); +Icon.Sync = createIcon(sync); Icon.IssueClosed = createIcon(issue_closed); Icon.IssueOpen = createIcon(issue_open); diff --git a/src/components/Icon/svg/bolt-alt.svg b/src/components/Icon/svg/bolt-alt.svg new file mode 100644 index 0000000..6bbf5fb --- /dev/null +++ b/src/components/Icon/svg/bolt-alt.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/Icon/svg/bubbles.svg b/src/components/Icon/svg/bubbles.svg new file mode 100644 index 0000000..150b3f5 --- /dev/null +++ b/src/components/Icon/svg/bubbles.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Icon/svg/cloudoff.svg b/src/components/Icon/svg/cloudoff.svg new file mode 100644 index 0000000..87b080e --- /dev/null +++ b/src/components/Icon/svg/cloudoff.svg @@ -0,0 +1 @@ + diff --git a/src/components/Icon/svg/eye-white.svg b/src/components/Icon/svg/eye-white.svg new file mode 100644 index 0000000..b416030 --- /dev/null +++ b/src/components/Icon/svg/eye-white.svg @@ -0,0 +1 @@ + diff --git a/src/components/Icon/svg/eye.svg b/src/components/Icon/svg/eye.svg new file mode 100644 index 0000000..4beebcf --- /dev/null +++ b/src/components/Icon/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Icon/svg/sync.svg b/src/components/Icon/svg/sync.svg new file mode 100644 index 0000000..38795a1 --- /dev/null +++ b/src/components/Icon/svg/sync.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Icon/svg/tag-white.svg b/src/components/Icon/svg/tag-white.svg new file mode 100644 index 0000000..6edc0d8 --- /dev/null +++ b/src/components/Icon/svg/tag-white.svg @@ -0,0 +1 @@ + diff --git a/src/components/Icon/svg/tag.svg b/src/components/Icon/svg/tag.svg new file mode 100644 index 0000000..07d9a06 --- /dev/null +++ b/src/components/Icon/svg/tag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Icon/svg/warn.svg b/src/components/Icon/svg/warn.svg new file mode 100644 index 0000000..8dcb028 --- /dev/null +++ b/src/components/Icon/svg/warn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/constants/filters.js b/src/constants/filters.js index f8af81f..0e1ef05 100644 --- a/src/constants/filters.js +++ b/src/constants/filters.js @@ -1,5 +1,6 @@ export const Filters = { ALL: 'all', + ASSIGNED: 'assign', REVIEW_REQUESTED: 'review_requested', PARTICIPATING: 'participating', SUBSCRIBED: 'subscribed', diff --git a/src/enhance/index.js b/src/enhance/index.js index d933954..a719beb 100644 --- a/src/enhance/index.js +++ b/src/enhance/index.js @@ -31,7 +31,7 @@ class Tooltip extends React.Component { } const {tooltipOffsetX, tooltipOffsetY} = this.props; - const {x, y, height} = event.target.getBoundingClientRect(); + const {x, y} = event.target.getBoundingClientRect(); const text = document.createTextNode(this.props.message); const tooltipElement = document.createElement('div'); diff --git a/src/pages/Home/Scene.js b/src/pages/Home/Scene.js index 90cfe23..f8d6358 100644 --- a/src/pages/Home/Scene.js +++ b/src/pages/Home/Scene.js @@ -7,7 +7,7 @@ import Icon from '../../components/Icon'; import Logo from '../../components/Logo'; import '../../styles/gradient.css'; -function createImagePlaceholder () { +function createImagePlaceholder (highlight) { return ( {/* navigation backdrop */} @@ -136,19 +136,21 @@ function createImagePlaceholder () {
@@ -156,7 +158,7 @@ function createImagePlaceholder () { position: 'absolute', background: '#dee1e6', top: 30, - left: 30, + left: highlight === 'badges' ? 30 : 100, width: 50, height: 7, borderRadius: 50 @@ -164,20 +166,44 @@ function createImagePlaceholder () {
- + {highlight === 'badges' ? ( + + ) : ( +
+ )}
- + {highlight === 'badges' ? ( + + ) : ( +
+ )}
@@ -231,7 +259,7 @@ function createImagePlaceholder () { position: 'absolute', background: '#dee1e6', top: 30, - left: 30, + left: highlight === 'badges' ? 30 : 100, width: 30, height: 7, borderRadius: 50 @@ -240,7 +268,7 @@ function createImagePlaceholder () { position: 'absolute', background: '#dee1e6', top: 30, - left: 64, + left: highlight === 'badges' ? 64 : 134, width: 7, height: 7, borderRadius: '100%' @@ -248,11 +276,23 @@ function createImagePlaceholder () {
- + {highlight === 'badges' ? ( + + ) : ( +
+ )}
alt && ({ background: '#24292e', 'p': { @@ -303,6 +343,9 @@ const Section = styled('div')({ 'h2': { color: '#fff' }, + '@media (max-width: 1000px)': { + flexDirection: 'column' + } })); const Item = styled('div')({ @@ -311,9 +354,10 @@ const Item = styled('div')({ padding: '24px 72px', 'h2': { marginTop: 0, - fontSize: 36, + marginLeft: 15, + fontSize: 42, textAlign: 'left', - fontWeight: 700 + fontWeight: 600 }, 'p': { fontSize: 18 @@ -339,6 +383,7 @@ const ImagePlaceholder = styled('div')({ position: 'relative', display: 'block', height: 400, + width: 600, background: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(179, 179, 179, 0.25)' @@ -348,7 +393,7 @@ const ImagePlaceholder = styled('div')({ const Header = styled('h1')({ color: '#fff', padding: '0 20px', - margin: '0 auto 20px', + margin: '0 auto 48px', letterSpacing: '-1.0px' }); @@ -364,7 +409,7 @@ const SubHeader = styled(Header)({ const LandingHeader = styled('div')({ position: 'relative', width: '100%', - margin: '54px 20px', + margin: '54px 20px 78px', maxWidth: 1000, display: 'flex', justifyContent: 'space-between', @@ -421,7 +466,7 @@ export default function Scene ({loggedIn, onLogout, ...props}) {
-
Control your notifications
+
Control your GitHub notifications
Prioritize the tasks that keep you and your team most productive
let's get started @@ -465,50 +510,54 @@ export default function Scene ({loggedIn, onLogout, ...props}) {
-
+
+ - {createImagePlaceholder()} + {createImagePlaceholder('badges')} - -

Surface the most important tasks to tackle as they happen

+ +

Surface the things that matter the most.

-

The issues and pull requests that require your attention the most are called out for you.

+

The most important issues and pull requests that require your presence are called out and brought to your attention.

-

We listen for updates with your notifications and let you know as soon as things change.

+

We listen for updates with your notifications and let you know why and when things change.

-

Super charge your day by focusing on getting things done, rather than sifting through notifications.

+

Super charge your day by focusing on getting things done, rather than sifting through notifications or emails.

+
-
+
- -

Surface the most important tasks to tackle as they happen

+ +

Your privacy matters, so
we keep things offline.

- -

The issues and pull requests that require your attention the most are called out for you.

+ +

a

- -

We listen for updates with your notifications and let you know as soon as things change.

+ +

a

- -

Super charge your day by focusing on getting things done, rather than sifting through notifications.

+ +

a

- {createImagePlaceholder()} + {createImagePlaceholder('reason')}
diff --git a/src/pages/Notifications/Scene.js b/src/pages/Notifications/Scene.js index 3576beb..27811ed 100644 --- a/src/pages/Notifications/Scene.js +++ b/src/pages/Notifications/Scene.js @@ -1,4 +1,6 @@ import React from 'react'; +import moment from 'moment'; +// import {VictoryPie, VictoryChart} from "victory"; import {Link} from "@reach/router"; import styled from 'react-emotion'; import Icon from '../../components/Icon'; @@ -6,49 +8,73 @@ import Logo from '../../components/Logo'; import LoadingIcon from '../../components/LoadingIcon'; import {routes} from '../../constants'; import {Filters} from '../../constants/filters'; -import {withOnEnter} from '../../enhance'; +import {withOnEnter, withTooltip} from '../../enhance'; import {Status} from '../../constants/status'; +import {Reasons, Badges} from '../../constants/reasons'; import '../../styles/gradient.css'; -/** - * Given a notification, give it a score based on its importance. - * - * There are some interesting workarounds that go into this algorithm to account - * for GitHub's broken notifications API -- but we will get to that later. First, - * let's start off with the basics of scoring. - * - * There are a few "reasons" that we can be getting a notification, each having - * an initial weight of importance: - * - * - MENTION -> 8 - * - ASSIGN -> 14 - * - REVIEW_REQUESTED -> 20 - * - SUBSCRIBED -> 3 - * - AUTHOR -> 8 - * - OTHER -> 2 - * - * There are some rules that go to giving out these scores, primarily being the - * first time we see one of these unique reasons, we award the notification with - * the respective score, but a reason that transitions into itself will be awarded - * a degraded score of min(ceil(n/3), 2). For example: - * - * - null, MENTION, MENTION -> 0, 8, 3 - * - null, ASSIGN, ASSIGN, REVIEW_REQUESTED, -> 0, 14, 5, 20 - * - null, SUBSCRIBED, SUBSCRIBED, SUBSCRIBED -> 0, 3, 2, 2 - * - * @param {Object} notification Some notification to score. - * @return {number} The score. - */ -function scoreOf (notification) { - return notification.reasons.length +/* eslint-disable jsx-a11y/anchor-is-valid */ +/* eslint-disable no-script-url */ + +function getMessageFromReasons (reasons) { + switch (reasons[reasons.length - 1].reason) { + case Reasons.ASSIGN: + return 'You were just assigned a task'; + case Reasons.AUTHOR: + return 'There was activity on a thread you created'; + case Reasons.COMMENT: + return 'Somebody just left a comment'; + case Reasons.MENTION: + return 'You were just @mentioned'; + case Reasons.REVIEW_REQUESTED: + return 'Your review was just requested'; + case Reasons.SUBSCRIBED: + return 'There was an update and you\'re subscribed'; + case Reasons.OTHER: + default: + return 'Something was updated'; + } } -const decorateWithScore = notification => ({ - ...notification, - score: scoreOf(notification) +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'; + + 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'; +} + +const UnofficialReleaseTag = styled('span')({ + color: 'white', + position: 'absolute', + left: '21px', + bottom: '0px', + fontSize: '9px', + background: '#f42839', + fontWeight: '800', + padding: '2px 4px', + borderRadius: '4px', + textTransform: 'uppercase', }); const FixedContainer = styled('div')({ + height: '80%', + maxWidth: 270, + display: 'block', position: 'fixed' }); @@ -64,7 +90,7 @@ const NotificationsContainer = styled('div')({ margin: '0 auto', padding: 0, width: '100%', - height: '100vh', + height: '100%', display: 'flex', flexDirection: 'row', overflowX: 'hidden', @@ -72,19 +98,28 @@ const NotificationsContainer = styled('div')({ }); const NavigationContainer = styled('div')({ - position: 'relative', + position: 'fixed', + top: 0, boxSizing: 'border-box', margin: '0 auto', - padding: '24px 48px', width: '100%', - background: 'none', - height: 'initial' + height: 60, + color: 'hsla(0,0%,100%,.75)', + paddingBottom: '12px', + paddingTop: '12px', + zIndex: '100', }); const GeneralOptionsContainer = styled(NavigationContainer)({ + position: 'relative', + zIndex: '1', + height: 'initial', + minHeight: 60, + width: '95%', + margin: 0, background: '#fff', padding: '8px 16px', - paddingLeft: 260, + paddingTop: 18, flex: '0 0 50px', 'button': { display: 'inline-flex', @@ -93,30 +128,51 @@ const GeneralOptionsContainer = styled(NavigationContainer)({ }); const Sidebar = styled('div')({ - flex: '0 0 200px', - padding: '0 20px 20px', - marginTop: 15 + flex: '0 0 300px', + padding: '32px 20px', + paddingRight: 0, + display: 'flex', + justifyContent: 'center', }); const SidebarLink = styled('a')({}, ({active, color}) => ({ textAlign: 'left', - margin: '0 auto', + userSelect: 'none', + margin: '0 auto 5px', + position: 'relative', cursor: 'pointer', - borderRadius: '8px', + borderRadius: 4, alignItems: 'center', padding: '0 14px', height: 40, + width: 200, fontSize: '12px', fontWeight: 600, letterSpacing: 0.5, - textTransform: 'uppercase', + textTransform: 'capitalize', textDecoration: 'none', transition: 'background 0.12s ease-in-out', display: 'flex', background: active ? color : 'none', color: active ? '#fff' : '#202124', - ':hover': { - background: active ? color: 'rgba(200, 200, 200, .25)' + ':before': { + content: '""', + transition: 'all 150ms ease', + background: 'rgba(190, 197, 208, 0.25)', + borderRadius: 4, + display: 'block', + top: 0, + bottom: 0, + right: 0, + left: 0, + position: 'absolute', + transform: 'scale(0)' + }, + ':hover:before': { + transform: active ? 'scale(0)' : 'scale(1)', + }, + ':active:before': { + background: 'rgba(190, 197, 208, 0.5)' }, 'div': { marginRight: 5 @@ -127,8 +183,67 @@ const Notifications = styled('div')({ flex: 1, }); +const NavTab = styled('a')({ + position: 'relative', + textTransform: 'capitalize', + userSelect: 'none', + borderRadius: 4, + textDecoration: 'none', + fontWeight: '500', + fontSize: '14px', + textAlign: 'left', + opacity: 0.6, + padding: '20px 32px', + paddingLeft: '16px', + width: '150px', + display: 'inline-block', + margin: 0, + transition: 'all 150ms ease', + ':hover': { + background: 'rgba(190, 197, 208, 0.25)', + }, +}, ({ number }) => ({ + ':after': number > 0 && { + content: `"${number}"`, + color: '#ffffff', + background: '#a8a8a9', + fontSize: '10px', + verticalAlign: 'text-top', + padding: '1px 8px', + borderRadius: '4px', + marginLeft: '6px', + display: 'inline-block', + } +}), ({ active, color, number }) => active && ({ + color, + opacity: 1, + ':before': { + content: '""', + position: 'absolute', + background: color, + height: '3px', + width: '90%', + bottom: '0', + left: '5%', + borderTopLeftRadius: '4px', + borderTopRightRadius: '4px', + }, + ':after': number > 0 && { + content: `"${number}"`, + color: '#ffffff', + background: color, + fontSize: '10px', + verticalAlign: 'text-top', + padding: '1px 8px', + borderRadius: '4px', + marginLeft: '6px', + display: 'inline-block', + } +})); + const Tab = styled('button')({ position: 'relative', + userSelect: 'none', cursor: 'pointer', border: 0, outline: 'none', @@ -159,8 +274,14 @@ const Tab = styled('button')({ } }, ({disabled}) => disabled && ({ background: 'none !important', - opacity: 0.5, + opacity: 0.35, cursor: 'default', + ':hover:before': { + transform: 'scale(0) !important', + }, + ':active:before': { + background: 'none !important' + } })); const SearchField = styled('div')({ @@ -169,17 +290,29 @@ const SearchField = styled('div')({ width: '50%', boxShadow: '0 4px 6px rgba(50,50,93,.11), 0 1px 3px rgba(0,0,0,.08)', margin: '0 auto', - background: '#fff', + background: 'hsla(0,0%,100%,.125)', borderRadius: '4px', alignItems: 'center', padding: 0, - height: '48px', - fontSize: '14px', + height: '36px', + fontSize: '13px', textDecoration: 'none', transition: 'all 0.06s ease-in-out', display: 'inline-flex', ':focus-within': { - boxShadow: '0 3px 9px #4a4a4a5c', + background: '#fff' + } +}); + +const Message = styled('div')({ + display: 'block', + textAlign: 'center', + marginTop: 24 * 5, + 'p': { + paddingTop: 24, + userSelect: 'none', + display: 'block', + margin: 0 } }); @@ -196,23 +329,26 @@ const SearchInput = styled('input')({ margin: '0 auto', background: 'none', padding: 0, - height: '48px', - fontSize: '14px', + height: '36px', + color: '#fff', + fontSize: '13px', textDecoration: 'none', - transition: 'all 0.12s ease-in-out', display: 'inline-flex', border: '0', - outline: 'none' + outline: 'none', + ':focus': { + color: '#202124' + } }); const EnhancedSearchInput = withOnEnter(SearchInput); const NotificationRow = styled('tr')({ position: 'relative', - cursor: 'pointer', - borderBottom: '1px solid #f2f2f2', - display: 'block', + display: 'flex', + alignItems: 'center', textAlign: 'left', width: '100%', + borderRadius: 4, margin: '0 auto', background: '#fff', padding: '8px 16px', @@ -230,12 +366,25 @@ const NotificationTab = styled(Tab)({ margin: 0, }); +const Timestamp = styled('span')({ + position: 'relative', + margin: 0, + marginLeft: 10, + fontSize: 11, + opacity: 0.5, +}); + +const ReasonMessage = styled(Timestamp)({ + +}); + const NotificationTitle = styled('span')({ position: 'relative', + display: 'block' }, ({img}) => img && ({ paddingLeft: 20, '::before': { - content: "''", + content: '""', position: 'absolute', display: 'block', background: `url(${img}) center center no-repeat`, @@ -254,34 +403,56 @@ const Repository = styled('span')({ const PRIssue = styled(Repository)({ fontWeight: 400, -}); +}, ({after}) => ({ + ':after': { + content: `"#${after}"`, + fontSize: 13, + opacity: .3, + marginLeft: 5 + } +})); const Table = styled('table')({ - display: 'block', + width: '96%', + minWidth: 970, 'td': { display: 'inline-block' } }); -const TableHeader = styled('h2')({ - fontWeight: 500, - fontSize: 34, - color: 'rgba(0, 0, 0, 0.86)', - letterSpacing: '-0.05px', - margin: '20px 15px 0', -}); - const TableItem = styled('td')({ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', -}, ({width}) => ({ - width +}, ({width, flex}) => ({ + width, + flex })); +const SmallLink = styled('a')({ + display: 'block', + marginRight: 10, + cursor: 'pointer', + fontSize: 10, + lineHeight: '20px', + fontWeight: 400, + textDecoration: 'underline', + transition: 'all 0.12s ease-in-out', + ':hover': { + opacity: 0.75 + } +}); + +const EnhancedTab = withTooltip(Tab); +const EnhancedNavTab = withTooltip(NavTab); +const EnhancedNotificationTab = withTooltip(NotificationTab); +const EnhancedSidebarLink = withTooltip(SidebarLink); +const EnhancedIconHot = withTooltip(Icon.Hot); +const EnhancedIconTimer = withTooltip(Icon.Timer); +const EnhancedIconConvo = withTooltip(Icon.Convo); + function getPRIssueIcon (type, reasons) { const grow = 1.0; - switch (type) { case 'PullRequest': return ( @@ -297,13 +468,28 @@ function getPRIssueIcon (type, reasons) { } export default function Scene ({ + queuedCount, + stagedCount, + closedCount, + first, + last, + lastPage, + page, notifications, + query, + activeStatus, + allNotificationsCount, + stagedTodayCount, + onChangePage, + onSetActiveStatus, + onClearQuery, onLogout, onSearch, onMarkAsRead, + onMarkAllAsStaged, onFetchNotifications, - onRefreshNotifications, onStageThread, + onRestoreThread, isSearching, isFetchingNotifications, onClearCache, @@ -311,223 +497,471 @@ export default function Scene ({ activeFilter, onSetActiveFilter, }) { - const isLoading = isSearching || isFetchingNotifications; + const loading = isSearching || isFetchingNotifications; + const isFirstPage = page === 1; + const isLastPage = page === lastPage; - let filterMethod = () => true; - switch (activeFilter) { - case Filters.REVIEW_REQUESTED: - filterMethod = n => n.reasons[0].reason === 'review_requested'; - break; - case Filters.PARTICIPATING: - filterMethod = n => ( - n.reasons.some(({ reason }) => ( - reason === 'review_requested' || - reason === 'assign' || - reason === 'mention' || - reason === 'author' - )) - ); - break; - case Filters.SUBSCRIBED: - filterMethod = n => n.reasons[0].reason === 'subscribed'; - break; - case Filters.ALL: - default: - filterMethod = () => true; - } - - notifications = notifications - .filter(filterMethod) - .sort((a, b) => a.repository.localeCompare(b.repository)) - .map(decorateWithScore) - .sort((a, b) => b.score - a.score); - - console.warn(notifications) - - const notificationsQueued = notifications.filter(n => n.status === Status.QUEUED); - const notificationsStaged = notifications.filter(n => n.status === Status.STAGED); + console.log('notifications', notifications) return ( -
- -
- +
+ + - - - onFetchNotifications()) : undefined} - /> - - - onClearCache()) : undefined} - /> - - -
- {notifications.length} -
-
-
- - - - onSetActiveFilter(Filters.ALL)} - > - {activeFilter === Filters.ALL ? ( - - ) : ( - - )} - all notifications - - onSetActiveFilter(Filters.PARTICIPATING)} - > - {activeFilter === Filters.PARTICIPATING ? ( - - ) : ( - - )} - participating - - - - - {isFetchingNotifications ? ( - - - - ) : notifications.length <= 0 ? ( -
-

no notifications

+
+
+ + +
+

+ + {moment().format('h:mma')} +

+ {moment().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 + +
+ + statistics coming soonโ„ข +
+
+ 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} + /> + + {query ? ( + + + + onClearQuery()) : undefined} + /> + + + ) : null} +
+ + + onChangePage(page - 1)) : undefined} + /> + + + onChangePage(page + 1)) : undefined} + /> +
- ) : ( - - - Queued - {notificationsQueued.map(n => ( - - -
- {getPRIssueIcon(n.type, n.reasons)} -
-
- { - window.open(n.url); - onStageThread(n.id) - }}> - - {n.name} - - - {/* - {n.reasons.map(r => r.reason).join(', ')} - */} - - - - - - - - {n.repository} - - - - onStageThread(n.id)) : undefined} - /> - - - onMarkAsRead(n.id)) : undefined} - /> - - - {/*

Last read at {n.last_read_at ? moment(n.last_read_at).format('dddd h:mma') : 'never'}

-

Last updated at {moment(n.last_updated).format('dddd h:mma')}

*/} -
- ))} - Staged - {notificationsStaged.map(n => ( - - -
- {getPRIssueIcon(n.type, n.reasons)} -
-
- window.open(n.url)}> - - {n.name} - - - - {n.reasons.map(r => r.reason).join(', ')} - - - {n.repository} - - - - onMarkAsRead(n.id)) : undefined} - /> - - - {/*

Last read at {n.last_read_at ? moment(n.last_read_at).format('dddd h:mma') : 'never'}

-

Last updated at {moment(n.last_updated).format('dddd h:mma')}

*/} -
- ))} - -
- )} - - + + + onSetActiveStatus(Status.QUEUED)} + href="javascript:void(0);"> + Unread + + onSetActiveStatus(Status.STAGED)} + href="javascript:void(0);"> + Read + + onSetActiveStatus(Status.CLOSED)} + href="javascript:void(0);"> + Resolved + + + + + {isFetchingNotifications ? ( + + + + ) : fetchingNotificationsError ? ( + +

OOPSIE WOOPSIE!! Uwu An error occurred when fetching notifications.

+

onFetchNotifications()} href="#">Try again

+
+ ) : notifications.length <= 0 ? ( + +

+ No {activeStatus.toLowerCase()} 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.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)) : undefined} + /> + + )} + +
+ ))} + +
+ )} +
+
+
+
); } diff --git a/src/pages/Notifications/SceneAlt.js b/src/pages/Notifications/SceneAlt.js deleted file mode 100644 index 6d2eeaa..0000000 --- a/src/pages/Notifications/SceneAlt.js +++ /dev/null @@ -1,898 +0,0 @@ -import React from 'react'; -import moment from 'moment'; -import {VictoryPie, VictoryChart} from "victory"; -import {Link} from "@reach/router"; -import styled from 'react-emotion'; -import Icon from '../../components/Icon'; -import Logo from '../../components/Logo'; -import LoadingIcon from '../../components/LoadingIcon'; -import {routes} from '../../constants'; -import {Filters} from '../../constants/filters'; -import {withOnEnter, withTooltip} from '../../enhance'; -import {Status} from '../../constants/status'; -import {Badges} from '../../constants/reasons'; -import '../../styles/gradient.css'; - -/* eslint-disable jsx-a11y/anchor-is-valid */ - -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'; - - const diffDays = currentTime.diff(targetTime, 'days'); - if (diffDays === 1) - return 'Yesterday'; - if (diffDays <= 7) - return 'Last ' + targetTime.format('dddd'); - // @TODO implement longer diffs - return 'Long time ago'; -} - -const UnofficialReleaseTag = styled('span')({ - color: 'white', - position: 'absolute', - left: '21px', - bottom: '0px', - fontSize: '9px', - background: '#f42839', - fontWeight: '800', - padding: '2px 4px', - borderRadius: '4px', - textTransform: 'uppercase', -}); - -const FixedContainer = styled('div')({ - height: '80%', - maxWidth: 270, - display: 'block', - position: 'fixed' -}); - -const InlineBlockContainer = styled('div')({ - 'div': { - display: 'inline-block' - } -}); - -const NotificationsContainer = styled('div')({ - position: 'relative', - background: '#fff', - margin: '0 auto', - padding: 0, - width: '100%', - height: '100%', - display: 'flex', - flexDirection: 'row', - overflowX: 'hidden', - boxSizing: 'border-box' -}); - -const NavigationContainer = styled('div')({ - position: 'fixed', - top: 0, - boxSizing: 'border-box', - margin: '0 auto', - width: '100%', - height: 60, - color: 'hsla(0,0%,100%,.75)', - paddingBottom: '12px', - paddingTop: '12px', - zIndex: '100', -}); - -const GeneralOptionsContainer = styled(NavigationContainer)({ - position: 'relative', - zIndex: '1', - height: 'initial', - minHeight: 60, - width: '95%', - margin: 0, - background: '#fff', - padding: '8px 16px', - paddingTop: 18, - flex: '0 0 50px', - 'button': { - display: 'inline-flex', - margin: 0 - } -}); - -const Sidebar = styled('div')({ - flex: '0 0 300px', - padding: '32px 20px', - paddingRight: 0, - display: 'flex', - justifyContent: 'center', -}); - -const SidebarLink = styled('a')({}, ({active, color}) => ({ - textAlign: 'left', - userSelect: 'none', - margin: '0 auto 5px', - position: 'relative', - cursor: 'pointer', - borderRadius: 4, - alignItems: 'center', - padding: '0 14px', - height: 40, - width: 200, - fontSize: '12px', - fontWeight: 600, - letterSpacing: 0.5, - textTransform: 'capitalize', - textDecoration: 'none', - transition: 'background 0.12s ease-in-out', - display: 'flex', - background: active ? color : 'none', - color: active ? '#fff' : '#202124', - ':before': { - content: '""', - transition: 'all 150ms ease', - background: 'rgba(190, 197, 208, 0.25)', - borderRadius: 4, - display: 'block', - top: 0, - bottom: 0, - right: 0, - left: 0, - position: 'absolute', - transform: 'scale(0)' - }, - ':hover:before': { - transform: active ? 'scale(0)' : 'scale(1)', - }, - ':active:before': { - background: 'rgba(190, 197, 208, 0.5)' - }, - 'div': { - marginRight: 5 - } -})); - -const Notifications = styled('div')({ - flex: 1, -}); - -const NavTab = styled('a')({ - position: 'relative', - textTransform: 'capitalize', - userSelect: 'none', - borderRadius: 4, - textDecoration: 'none', - fontWeight: '500', - fontSize: '14px', - textAlign: 'left', - opacity: 0.6, - padding: '20px 32px', - paddingLeft: '16px', - width: '150px', - display: 'inline-block', - margin: 0, - transition: 'all 150ms ease', - ':hover': { - background: 'rgba(190, 197, 208, 0.25)', - }, -}, ({ number }) => ({ - ':after': number > 0 && { - content: `"${number}"`, - color: '#ffffff', - background: '#a8a8a9', - fontSize: '10px', - verticalAlign: 'text-top', - padding: '1px 8px', - borderRadius: '4px', - marginLeft: '6px', - display: 'inline-block', - } -}), ({ active, color, number }) => active && ({ - color, - opacity: 1, - ':before': { - content: '""', - position: 'absolute', - background: color, - height: '3px', - width: '90%', - bottom: '0', - left: '5%', - borderTopLeftRadius: '4px', - borderTopRightRadius: '4px', - }, - ':after': number > 0 && { - content: `"${number}"`, - color: '#ffffff', - background: color, - fontSize: '10px', - verticalAlign: 'text-top', - padding: '1px 8px', - borderRadius: '4px', - marginLeft: '6px', - display: 'inline-block', - } -})); - -const Tab = styled('button')({ - position: 'relative', - userSelect: 'none', - cursor: 'pointer', - border: 0, - outline: 'none', - background: 'none', - height: 40, - width: 40, - borderRadius: '100%', - margin: '0 auto', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - ':before': { - content: "''", - transition: 'all 150ms ease', - background: 'rgba(190, 197, 208, 0.25)', - borderRadius: '100%', - display: 'block', - height: 40, - width: 40, - position: 'absolute', - transform: 'scale(0)' - }, - ':hover:before': { - transform: 'scale(1)', - }, - ':active:before': { - background: 'rgba(190, 197, 208, 0.5)' - } -}, ({disabled}) => disabled && ({ - background: 'none !important', - opacity: 0.35, - cursor: 'default', - ':hover:before': { - transform: 'scale(0) !important', - }, - ':active:before': { - background: 'none !important' - } -})); - -const SearchField = styled('div')({ - float: 'left', - textAlign: 'left', - width: '50%', - boxShadow: '0 4px 6px rgba(50,50,93,.11), 0 1px 3px rgba(0,0,0,.08)', - margin: '0 auto', - background: 'hsla(0,0%,100%,.125)', - borderRadius: '4px', - alignItems: 'center', - padding: 0, - height: '36px', - fontSize: '13px', - textDecoration: 'none', - transition: 'all 0.06s ease-in-out', - display: 'inline-flex', - ':focus-within': { - background: '#fff' - } -}); - -const Message = styled('div')({ - display: 'block', - textAlign: 'center', - marginTop: 24 * 5, - 'p': { - paddingTop: 24, - userSelect: 'none', - display: 'block', - margin: 0 - } -}); - -const LoaderContainer = styled('div')({ - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - height: '100%' -}); - -const SearchInput = styled('input')({ - flex: 1, - textAlign: 'left', - margin: '0 auto', - background: 'none', - padding: 0, - height: '36px', - color: '#fff', - fontSize: '13px', - textDecoration: 'none', - display: 'inline-flex', - border: '0', - outline: 'none', - ':focus': { - color: '#202124' - } -}); -const EnhancedSearchInput = withOnEnter(SearchInput); - -const NotificationRow = styled('tr')({ - position: 'relative', - display: 'flex', - alignItems: 'center', - textAlign: 'left', - width: '100%', - borderRadius: 4, - margin: '0 auto', - background: '#fff', - padding: '8px 16px', - transition: 'all 0.1s ease-in-out', - boxSizing: 'border-box', - ':hover': { - background: '#f9f9f9', - // boxShadow: '0 4px 6px rgba(50,50,93,.11), 0 1px 3px rgba(0,0,0,.08)', - zIndex: 10 - } -}); - -const NotificationTab = styled(Tab)({ - display: 'inline-flex', - margin: 0, -}); - -const Timestamp = styled('span')({ - position: 'relative', - margin: 0, - marginLeft: 10, - fontSize: 11, - opacity: 0.5, -}); - -const NotificationTitle = styled('span')({ - position: 'relative', - display: 'block' -}, ({img}) => img && ({ - paddingLeft: 20, - '::before': { - content: '""', - position: 'absolute', - display: 'block', - background: `url(${img}) center center no-repeat`, - backgroundSize: 'cover', - left: 0, - height: 20, - width: 20, - } -})); - -const Repository = styled('span')({ - fontWeight: 500, - marginLeft: 10, - fontSize: 14 -}); - -const PRIssue = styled(Repository)({ - fontWeight: 400, -}, ({after}) => ({ - ':after': { - content: `"#${after}"`, - fontSize: 13, - opacity: .3, - marginLeft: 5 - } -})); - -const Table = styled('table')({ - width: '96%', - minWidth: 970, - 'td': { - display: 'inline-block' - } -}); - -const TableItem = styled('td')({ - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', -}, ({width, flex}) => ({ - width, - flex -})); - -const SmallLink = styled('a')({ - display: 'block', - marginRight: 10, - cursor: 'pointer', - fontSize: 10, - lineHeight: '20px', - fontWeight: 400, - textDecoration: 'underline', - transition: 'all 0.12s ease-in-out', - ':hover': { - opacity: 0.75 - } -}); - -const EnhancedTab = withTooltip(Tab); -const EnhancedNavTab = withTooltip(NavTab); -const EnhancedNotificationTab = withTooltip(NotificationTab); -const EnhancedSidebarLink = withTooltip(SidebarLink); -const EnhancedIconHot = withTooltip(Icon.Hot); -const EnhancedIconTimer = withTooltip(Icon.Timer); -const EnhancedIconConvo = withTooltip(Icon.Convo); - -function getPRIssueIcon (type, reasons) { - const grow = 1.0; - switch (type) { - case 'PullRequest': - return ( - - ); - case 'Issue': - return ( - - ); - default: - return null; - } -} - -export default function Scene ({ - queuedCount, - stagedCount, - closedCount, - first, - last, - lastPage, - page, - notifications, - query, - activeStatus, - allNotificationsCount, - stagedTodayCount, - onChangePage, - onSetActiveStatus, - onClearQuery, - onLogout, - onSearch, - onMarkAsRead, - onFetchNotifications, - onRefreshNotifications, - onStageThread, - onRestoreThread, - isSearching, - isFetchingNotifications, - onClearCache, - fetchingNotificationsError, - activeFilter, - onSetActiveFilter, -}) { - const loading = isSearching || isFetchingNotifications; - const isFirstPage = page === 1; - const isLastPage = page === lastPage; - - console.warn('before render in scene', notifications) - - return ( -
- -
- { - onSetActiveStatus(Status.QUEUED); - onSetActiveFilter(Filters.PARTICIPATING); - }} - /> - beta - - - - {isSearching && } - -
- home -
-
- sign out -
-
-
-
-
- - -
-

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

- {moment().format('dddd, MMMM Do')} - You've triaged {stagedTodayCount} notifications today -
- {/* - We shouldn't show all the notificaitons. Pointless and creates more noise. - - onSetActiveFilter(Filters.ALL)}> - {activeFilter === Filters.ALL ? ( - - ) : ( - - )} - all notifications - - */} - onSetActiveFilter(Filters.PARTICIPATING)}> - {activeFilter === Filters.PARTICIPATING ? ( - - ) : ( - - )} - your updates - - onSetActiveFilter(Filters.COMMENT)}> - {activeFilter === Filters.COMMENT ? ( - - ) : ( - - )} - participating - -
- bar chart, statistics, etc. will be here eventually -
-
- Report bugs - Submit feedback - See source code -
-
-
-
-
- - - onFetchNotifications()) : undefined} - /> - - - { - const response = window.confirm('Are you sure you want to clear the cache?'); - if (response) { - onClearCache(); - } - }) : 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);"> - Resolved - - - - - {isFetchingNotifications ? ( - - - - ) : notifications.length <= 0 ? ( - -

- No {activeStatus.toLowerCase()} 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 && ( - - )} - - - - - {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 ? ( - - {}) : undefined} - /> - - ) : ( - - onMarkAsRead(n.id)) : undefined} - /> - - )} - -
- ))} - -
- )} -
-
-
-
-
- ); -} diff --git a/src/pages/Notifications/index.js b/src/pages/Notifications/index.js index fd0784d..3e60932 100644 --- a/src/pages/Notifications/index.js +++ b/src/pages/Notifications/index.js @@ -11,7 +11,7 @@ import { routes } from '../../constants'; import { Filters } from '../../constants/filters'; import { Status } from '../../constants/status'; import { Reasons, Badges } from '../../constants/reasons'; -import Scene from './SceneAlt'; +import Scene from './Scene'; const PER_PAGE = 15; @@ -53,7 +53,6 @@ function scoreOf (notification) { let prevReason = null; for (let i = 0; i < reasons.length; i++) { const reason = reasons[i].reason; - console.log(reason) if (prevReason && reason === prevReason) { const degradedScore = Math.ceil(scoreOfReason[reason] / 3); score += Math.max(degradedScore, 2); @@ -122,8 +121,9 @@ class NotificationsPage extends React.Component { } componentDidMount () { + this.props.notificationsApi.fetchNotifications(); + this.syncer = setInterval(() => { - console.warn('sync'); this.props.notificationsApi.fetchNotificationsSync(); }, 8 * 1000); } @@ -172,14 +172,12 @@ class NotificationsPage extends React.Component { } enhancedOnStageThread = (thread_id, repository) => { - console.warn('staging thread', thread_id, 'in repo', repository); this.props.storageApi.incrStat('stagedCount'); this.props.storageApi.incrStat(repository + '-stagedCount'); this.props.notificationsApi.stageThread(thread_id); } restoreThread = thread_id => { - console.warn('restoring thread'); this.props.notificationsApi.restoreThread(thread_id); } @@ -191,6 +189,7 @@ class NotificationsPage extends React.Component { const { fetchNotifications, markAsRead, + markAllAsStaged, clearCache, notifications, loading: isFetchingNotifications, @@ -210,6 +209,16 @@ class NotificationsPage extends React.Component { )) ); break; + case Filters.ASSIGNED: + filterMethod = n => ( + n.reasons.some(({ reason }) => reason === 'assign') + ); + break; + case Filters.REVIEW_REQUESTED: + filterMethod = n => ( + n.reasons.some(({ reason }) => reason === 'review_requested') + ); + break; case Filters.COMMENT: filterMethod = n => ( n.reasons.some(({ reason }) => reason === 'comment') @@ -290,6 +299,7 @@ class NotificationsPage extends React.Component { onClearQuery={this.onClearQuery} onFetchNotifications={fetchNotifications} onMarkAsRead={markAsRead} + onMarkAllAsStaged={markAllAsStaged} onClearCache={clearCache} onStageThread={this.enhancedOnStageThread} onRestoreThread={this.restoreThread} diff --git a/src/providers/Notifications.js b/src/providers/Notifications.js index 3bdcf06..abdf8e1 100644 --- a/src/providers/Notifications.js +++ b/src/providers/Notifications.js @@ -1,6 +1,6 @@ import React from 'react'; import {AuthConsumer} from './Auth'; -import {StorageProvider} from './Storage'; +import {StorageProvider, LOCAL_STORAGE_PREFIX} from './Storage'; import {Status} from '../constants/status'; const BASE_GITHUB_API_URL = 'https://api.github.com'; @@ -121,7 +121,7 @@ class NotificationsProvider extends React.Component { fetchNotifications = (page = 1, optimizePolling = true) => { if (!this.props.token) { - console.error('Unauthenitcated, aborting request.') + console.error('Unauthenitcated, aborting request.'); return false; } @@ -133,7 +133,7 @@ class NotificationsProvider extends React.Component { processNotificationsChunk = (nextPage, notificationsChunk) => { return new Promise((resolve, reject) => { - console.log('chunk', notificationsChunk) + console.log('chunk', notificationsChunk); let everythingUpdated = true; if (notificationsChunk.length === 0) { @@ -187,7 +187,6 @@ class NotificationsProvider extends React.Component { : Promise.reject(); }) .then(() => { - console.warn('removing', thread_id); this.props.removeItemFromStorage(thread_id); this.props.refreshNotifications(); return Promise.resolve(); @@ -208,7 +207,6 @@ class NotificationsProvider extends React.Component { requestClearCache = () => { return new Promise((resolve, reject) => { - console.warn('clearing cache'); this.props.clearStorageCache(); this.props.refreshNotifications(); this.last_modified = null; @@ -225,7 +223,6 @@ class NotificationsProvider extends React.Component { requestStageThread = thread_id => { return new Promise((resolve, reject) => { - console.warn('staging thread', thread_id); const cached_n = this.props.getItemFromStorage(thread_id); if (cached_n) { const newValue = { @@ -241,9 +238,28 @@ class NotificationsProvider extends React.Component { }); } + requestStageAll = () => { + return new Promise((resolve, reject) => { + Object.keys(localStorage).forEach(nKey => { + // Only update the notification items in the cache. + // Don't get the statistics or anything else caught in there. + if (nKey.includes(LOCAL_STORAGE_PREFIX)) { + let cached_n = null; + cached_n = JSON.parse(window.localStorage.getItem(nKey)); + const newValue = { + ...cached_n, + status: Status.STAGED + }; + window.localStorage.setItem(nKey, JSON.stringify(newValue)); + } + }); + this.props.refreshNotifications(); + return resolve(); + }); + } + requestRestoreThread = thread_id => { return new Promise((resolve, reject) => { - console.warn('restoring thread', thread_id); const cached_n = this.props.getItemFromStorage(thread_id); if (cached_n) { const newValue = { @@ -259,6 +275,13 @@ class NotificationsProvider extends React.Component { }); } + markAllAsStaged = () => { + this.setState({ loading: true }); + return this.requestStageAll() + .catch(error => this.setState({ error })) + .finally(() => this.setState({ loading: false })); + } + stageThread = thread_id => { this.setState({ loading: true }); return this.requestStageThread(thread_id) @@ -282,7 +305,6 @@ class NotificationsProvider extends React.Component { if (prevReason) { reasons = prevReason.concat(newReason); - console.warn('MULTIPLE REASONS', reasons) } else { reasons = [newReason]; } @@ -311,6 +333,7 @@ class NotificationsProvider extends React.Component { fetchNotifications: this.fetchNotifications, fetchNotificationsSync: this.requestFetchNotifications, markAsRead: this.markAsRead, + markAllAsStaged: this.markAllAsStaged, clearCache: this.clearCache, stageThread: this.stageThread, restoreThread: this.restoreThread, diff --git a/src/providers/Storage.js b/src/providers/Storage.js index 937e977..f5165a0 100644 --- a/src/providers/Storage.js +++ b/src/providers/Storage.js @@ -3,8 +3,8 @@ import moment from 'moment'; import {Status} from '../constants/status'; import {Reasons} from '../constants/reasons'; -const LOCAL_STORAGE_PREFIX = '__meteorite_noti_cache__'; -const LOCAL_STORAGE_STATISTIC_PREFIX = '__meteorite_statistic_cache__'; +export const LOCAL_STORAGE_PREFIX = '__meteorite_noti_cache__'; +export const LOCAL_STORAGE_STATISTIC_PREFIX = '__meteorite_statistic_cache__'; const getMockReasons = n => { const reasons = Object.values(Reasons); diff --git a/src/styles/gradient.css b/src/styles/gradient.css index b29581d..857a75e 100644 --- a/src/styles/gradient.css +++ b/src/styles/gradient.css @@ -112,3 +112,12 @@ 50% {background-position: 60% 50%} 100% {background-position: 0% 50%} } + +@media only screen and (max-width: 1400px) { + #section { + flex-direction: column !important; + } + #item-text { + width: 600px; + } +}