New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(tabbar): show notifications count #360
Conversation
src/config/colors.js
Outdated
@@ -17,6 +17,7 @@ export const colors = { | |||
darkGreen: '#27ae60', | |||
red: '#ee0701', | |||
darkRed: '#e74c3c', | |||
redDarkest: '#da0000', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
darkerRed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really nice addition!
src/api/index.js
Outdated
count = linkHeader.split('=').pop(); | ||
} | ||
|
||
return +count; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do +count
do ? Oo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This convert the value to a number, similarly does the call to parseInt()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, thanks
src/api/index.js
Outdated
const response = await fetch(ENDPOINT, accessTokenParameters(accessToken)); | ||
|
||
let linkHeader = response.headers.get('Link'); | ||
let count = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the Link header is not present, count should be the count of json objects returned in the response, not 1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See how this is handled in v3.count() I just added: 6ce8489#diff-284817f1b6aad47c0ac6d61bd72f474cR58
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the Link header is not present, count should be the count of json objects returned in the response, not 1
The fact is that when the notification in the repository is only one, the Link header will not be, and response.json().length
will return 0, which is not correct. Therefore, the default is 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See how this is handled in v3.count() I just added: 6ce8489#diff-284817f1b6aad47c0ac6d61bd72f474cR58
Can you add a third parameter, such as defaultCount
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lex111 as I understand, here's how the API respond :
- if you have 0 notifications, response.status is 404 with a json body that must be ignored
- if you have notifications :
- if only 1, no Link header and response.json() is an array of 1 element (so json().length = 1)
- if more, you parse the Link header to get the number
am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's right, I checked again, and found the error that you have now, and because of which I incorrectly put 1
response.json()
is a promise, so it should be like this:
number = response.json().then(data => {
return data.length;
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OMG, thanks! didn't know response.json() was a promise, I thought only response was.
I'll update count() to handle the 404, fix json().
Do you still think defaultValue is needed? I fail to see how
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you still think defaultValue is needed? I fail to see how
No, at least for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just fixed the count()
method and realized that json().length isn't needed at all as we request one element per page -_-
So, if 404 => 0 elements
If no Link header => 1 element
Else, count fetched from Link
I wonder if the 404 status code is sent for all empty request responses ..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(let's chat over gitter if you want)
I made changes according with the feedback, and now I thought, can show the total number notifications, instead 99+? I did it because I wanted the badge to be always round, and if there is a four-digit number, then it will be oval. |
I personally think +99 is perfect |
/> | ||
|
||
{notificationsCount | ||
? <View style={styles.badge}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @lex111, maybe we could use from react-native-elements which is already bundled :
https://react-native-training.github.io/react-native-elements/API/badge/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not round, I like that currenly it's just round in shape, well okay
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough, if you would rather have a circular badge and think it looks neater I don't mind :)
I think we can do one of two things however:
- Could we have a local badge component that contains the style for the badge and expose a background color and text color prop? Feels nice to have that exposed in it's own component. Would prefer if you call it
circular-badge
or something along those lines to not name-conflict with the react native elements badge component - OR we could use React Native Elements badge here and just apply
containerStyle
to make it circular.
I'm okay with either option 🙌
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strangely, I can not import this component:
import { Icon, Badge } from 'react-native-elements';
console.log(Badge); // undefined
I do not understand what's up, okay, I'll make a local component then.
src/auth/screens/events.screen.js
Outdated
@@ -86,8 +89,11 @@ class Events extends Component { | |||
} | |||
} | |||
|
|||
getUserEvents = (user = this.props.user) => { | |||
getUserEvents = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're passing a user into this function on lines 82 and 88, and this would be therefore ignoring it. Maybe something like this will work?
getUserEvents = ({ user, accessToken } = this.props) => {
// ...
}
While changing lines 82 and 88 to pass in this.props
and nextProps
respectively.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to do it, it did not work. In componentWillReceiveProps
this will work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with @andrewda. Line 82 shouldn't matter since it's already defaulting to this.props.user
(would prefer if you can remove the argument call in there (line 82) if you can too :)
But if you're removing the arguments entirely from the function declaration in line 92, line 88 doesn't look it would work at all. We're passing in nextProps.user
but in here it's not being used at all..
Curious why
getUserEvents = ({ user, accessToken } = this.props) => {
// ...
}
doesn't work? Destructuring and applying a default should work if I'm not mistaken
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Beautiful stuff @lex111. Can't tell you how much I appreciate you always finding things to be improved in the app. Thank you.
Left a few comments but nothing major 👍
src/auth/screens/events.screen.js
Outdated
@@ -86,8 +89,11 @@ class Events extends Component { | |||
} | |||
} | |||
|
|||
getUserEvents = (user = this.props.user) => { | |||
getUserEvents = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with @andrewda. Line 82 shouldn't matter since it's already defaulting to this.props.user
(would prefer if you can remove the argument call in there (line 82) if you can too :)
But if you're removing the arguments entirely from the function declaration in line 92, line 88 doesn't look it would work at all. We're passing in nextProps.user
but in here it's not being used at all..
Curious why
getUserEvents = ({ user, accessToken } = this.props) => {
// ...
}
doesn't work? Destructuring and applying a default should work if I'm not mistaken
/> | ||
|
||
{notificationsCount | ||
? <View style={styles.badge}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough, if you would rather have a circular badge and think it looks neater I don't mind :)
I think we can do one of two things however:
- Could we have a local badge component that contains the style for the badge and expose a background color and text color prop? Feels nice to have that exposed in it's own component. Would prefer if you call it
circular-badge
or something along those lines to not name-conflict with the react native elements badge component - OR we could use React Native Elements badge here and just apply
containerStyle
to make it circular.
I'm okay with either option 🙌
@@ -163,6 +167,8 @@ class Notifications extends Component { | |||
} = this.props; | |||
const { type } = this.state; | |||
|
|||
this.props.getNotificationsCount(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we have this as part of our destructured object up above:
const {
getUnreadNotifications,
getParticipatingNotifications,
getAllNotifications,
getNotificationsCount
} = this.props;
So we can just call getNotificationsCount()
here
@@ -145,6 +148,7 @@ class Notifications extends Component { | |||
this.props.getUnreadNotifications(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would prefer is we destructure here as well (I know this is slightly out of your scope however so don't worry if you would rather not):
const {
getUnreadNotifications,
getParticipatingNotifications,
getAllNotifications,
getNotificationsCount
} = this.props;
And then Promise.all
all the functions instead of calling them one by one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done except promises, how can they help?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries, ideally Promise.all
is used to make sure a collection of promises are complete before submitting an event after them but since we're not really doing anything after each of these specifically - I think it's okay if we don't include it here :)
Ready for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works absolutely lovely mate 👏 Thank you!!!!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just noticed one tiny thing to change, otherwise this baby works wonderfully :)
src/auth/screens/events.screen.js
Outdated
componentDidMount() { | ||
if (this.props.user.login) { | ||
this.getUserEvents(this.props.user); | ||
componentDidMount({ user } = this.props) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should be including props
as an argument to componentDidMount
. We never call this method as a function anywhere in the component so there's no need to set an argument and define it's default (seems redundant).
Ideally, we can just destructure within the method itself:
componentDidMount() {
const { user } = this.props;
// ....
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, that's right, I fixed it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cheers, thanks so much for being on the ball and responding to change requests so quickly 🙌
So now the lazy loading of tabs works, then probably it makes sense to display the number of notifications in the tabbar.
This is the early version, you still need to deduct the notifications that were read by the repository (you need to determine how many they need to be deducted, and do it by analogy here).
Quick demo:
If there are more than 99 notifications, 99+ will be displayed.
Also I had an idea with auto-updates of the number of notifications? Is it necessary?
We can also think about how to make this component differently, more precisely how to connect
redux
andreact-navigation
? I had an idea to make a component of theTabBarIcon
type with thebadgeText
property and pass the count there, but it's not clear how to connectredux
then to update the badge text.Feel free to suggest changes, if you want.