-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Conversation
Depends on this server PR: mattermost/mattermost#8185 |
24067bb
to
72e0df0
Compare
3b816f6
to
ae2ac79
Compare
ae2ac79
to
121bf09
Compare
a64d683
to
e390a56
Compare
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.
Looks good for the most part, just a few comments
utils/timezone.jsx
Outdated
return dateTimeFormat.resolvedOptions().timeZone; | ||
} | ||
|
||
export function autoUpdateTimezone(userTimezone) { |
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.
Couple suggestions/comments here:
- Is any of this logic shared by the mobile app? Wonder if it would make sense to put it into mattermost-redux
- Since an action is getting called here, I'd prefer if we just turned this function into an action itself
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.
Doable, I currently have a PR for timezone support so adding selectors/actions for this won't be hard.
I agree this logic is shared with both webapp and mobile
stores/user_store.jsx
Outdated
@@ -459,6 +459,22 @@ class UserStoreClass extends EventEmitter { | |||
return this.getStatuses()[id] || UserStatuses.OFFLINE; | |||
} | |||
|
|||
getTimezone(id) { |
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 you turn this into a selector instead? Preferably in mattermost-redux since I figure the mobile apps are going to need this as well
Then the timezone can get passed in as a prop to the components that need it instead of importing UserStore (which we're trying to reduce usage of so we can remove 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.
It would definitely make sense as a selector. Something like this would be nice and simple.
const getCurrentTimezone = createSelector(
getCurrentUser,
(currentUser) => {
...
}
);
If we ever care about the timezone of another user, we could add one that's more detailed.
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.
👍
import React from 'react'; | ||
import {FormattedHTMLMessage, FormattedMessage} from 'react-intl'; | ||
|
||
import {updateUser} from 'actions/user_actions.jsx'; |
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.
Not a big deal, but it would be a bit nicer to pass in the mattermost-redux update user action through an index.js
container so we can eventually get rid of the wrapper actions like this 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.
@jwilander does the redux library support migrating away from these wrapper actions?
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.
Sorry, missed your comment. Yeah if the actions are already in mattermost-redux then you can just pass it in directly and if you need to get results right away from the action, just await
on it instead of passing callbacks like we do for the wrapper functions
useAutomaticTimezone: PropTypes.bool.isRequired, | ||
automaticTimezone: PropTypes.string.isRequired, | ||
manualTimezone: PropTypes.string.isRequired, | ||
}; |
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.
Mind defining the propTypes at the top of the class like so:
export default class ManageTimezones extends React.Component {
static propTypes = {
// props here
}
// other stuff
}
utils/timezone.jsx
Outdated
const dateTimeFormat = new Intl.DateTimeFormat(); | ||
|
||
export function getSupportedTimezones() { | ||
if (global.mm_config && global.mm_config.SupportedTimezones) { |
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 are removing all global.mm_config usage in the application, you should use something like const config = getConfig(store.getState())
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.
Although getConfig(store.getState())
is a correct workaround given that global.mm_config
is now gone, note that it won't connect a component to future changes in the SupportedTimezones
. We're aiming to eventually remove the required refresh when a configuration change occurs.
If possible, pass in the config.supportedTimezones
to this method and invoke it from the connector. If that's not straightforward, the approach @jespino describes will suffice 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.
This should be turned into a selector in the redux library
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 has been moved to a selector 👍
@@ -45,17 +52,35 @@ export default class PostTime extends React.PureComponent { | |||
}; | |||
} | |||
|
|||
getCurrentUserTimezone = () => { | |||
const userId = UserStore.getCurrentId(); |
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.
CurrentUserId is better passed through the redux connect, as a property.
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.
In particular, this may not trigger a re-render when the time zone changes, unless the PostTime
is re-rendered for another reason.
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.
Agreed. These should be passed through from the redux store. We shouldn't be adding new code that uses the old stores as long as we can help 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.
I'm noticing a pattern with classes that seem to be getting deprecated, examples:
user_store, user_actions.
Is there a document that describes which files to stay away from? that way contributors are able to understand which files are getting deprecated and can work with new best practices
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 kind of documented here but we should definitely do a better job of it. Would adding comments at the top of these files help?
let minute = date.getMinutes(); | ||
minute = minute >= 10 ? minute : ('0' + minute); | ||
let time = ''; | ||
// const militaryTime = this.props.useMilitaryTime; |
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 commented lines can be removed because the military time is already solved by the toLocaleString.
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 (this.props.enableTimezone) { | ||
const currentTimezone = getCurrentTimezone(this.props.user.timezone); | ||
if (currentTimezone) { | ||
const useMilitaryTime = PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time'); |
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.
Probably is better option to receive this from the redux connect as a property.
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.
👍
@@ -45,17 +52,35 @@ export default class PostTime extends React.PureComponent { | |||
}; | |||
} | |||
|
|||
getCurrentUserTimezone = () => { | |||
const userId = UserStore.getCurrentId(); |
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.
In particular, this may not trigger a re-render when the time zone changes, unless the PostTime
is re-rendered for another reason.
/* | ||
* Flag for enabling the timezone feature | ||
*/ | ||
enableTimezone: PropTypes.bool, |
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.
Mark .isRequired
?
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.
👍
const useMilitaryTime = PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time'); | ||
const date = new Date(); | ||
dataContent.push( | ||
<span> |
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 it make sense to factor out a <DateTime>
component that connects the useMilitaryTime
and timezone
preferences vs. having to pass them into every component that needs to render a time?
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.
that would be a nice refactor 👍
i18n/en.json
Outdated
@@ -2785,6 +2786,9 @@ | |||
"user.settings.import_theme.submitError": "Invalid format, please try copying and pasting in again.", | |||
"user.settings.languages.change": "Change interface language", | |||
"user.settings.languages.promote": "Select which language Mattermost displays in the user interface.<br /><br />Would like to help with translations? Join the <a href='https://translate.mattermost.com/' target='_blank'>Mattermost Translation Server</a> to contribute.", | |||
"user.settings.timezones.automatic": "Set automatically", | |||
"user.settings.timezones.change": "Change timezone", | |||
"user.settings.timezones.promote": "Select the time zone used for timestamps in the user interface and email notifications.", |
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.
time zone
vs. timezone
? I think they are both fungible, but perhaps we converge on one usage?
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 catch, I'll fix that. We can go with timezone to be consistent
utils/timezone.jsx
Outdated
const dateTimeFormat = new Intl.DateTimeFormat(); | ||
|
||
export function getSupportedTimezones() { | ||
if (global.mm_config && global.mm_config.SupportedTimezones) { |
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.
Although getConfig(store.getState())
is a correct workaround given that global.mm_config
is now gone, note that it won't connect a component to future changes in the SupportedTimezones
. We're aiming to eventually remove the required refresh when a configuration change occurs.
If possible, pass in the config.supportedTimezones
to this method and invoke it from the connector. If that's not straightforward, the approach @jespino describes will suffice for now :)
utils/timezone.jsx
Outdated
} | ||
|
||
export function getBrowserTimezone() { | ||
return dateTimeFormat.resolvedOptions().timeZone; |
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.
Did some quick tests with this JSFiddle and IE 11.15 displays "undefined" for this JS API so we'll need a fallback to support it.
@jasonblais Can you please confirm/disconfirm support for IE 11.15?
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.
Thanks Martin. I had missed your note until now. I'll queue for our PM meeting to discuss this.
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.
Discussed and we officially support IE11, hence IE 11.15 is included
import SuggestionList from 'components/suggestion/suggestion_list.jsx'; | ||
import TimezoneProvider from 'components/suggestion/timezone_provider.jsx'; | ||
|
||
export default class ManageTimezones extends React.Component { |
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 should be a PureComponent
} = this.state; | ||
|
||
const timezone = { | ||
useAutomaticTimezone: useAutomaticTimezone.toString(), |
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.
Why store this as a string version of the boolean? That seems like something that'll be easy to get mixed up
stores/user_store.jsx
Outdated
if (profile && profile.timezone) { | ||
return { | ||
...profile.timezone, | ||
useAutomaticTimezone: profile.timezone.useAutomaticTimezone === 'true', |
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 mentioned this above, but why switch types here?
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.
@hmhealey this is a good point to address the convention MM uses for storing booleans in postgres.
From my investigation, MM has been storing booleans as strings. See: https://github.com/mattermost/mattermost-webapp/blob/master/components/user_settings/notifications/user_settings_notifications.jsx#L138
Looking at the database this is what the notify_props looks like:
{
"channel":"true",
"comments":"never",
"desktop":"mention",
"desktop_duration":"5",
"desktop_sound":"true",
"email":"true",
"first_name":"false",
"mention_keys":"admin",
"push":"mention",
"push_status":"away"
}
I'm following the same convention MM has been using for storing booleans. If this is not the preferred convention, let us know what's the best way to store booleans.
stores/user_store.jsx
Outdated
@@ -459,6 +459,22 @@ class UserStoreClass extends EventEmitter { | |||
return this.getStatuses()[id] || UserStatuses.OFFLINE; | |||
} | |||
|
|||
getTimezone(id) { |
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 would definitely make sense as a selector. Something like this would be nice and simple.
const getCurrentTimezone = createSelector(
getCurrentUser,
(currentUser) => {
...
}
);
If we ever care about the timezone of another user, we could add one that's more detailed.
stores/user_store.jsx
Outdated
}; | ||
} | ||
|
||
return { |
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 we care whether the timezone is automatic or manual in most places? It might make sense to just have a function that returns the timezone string without the rest of the object.
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 is useful for the settings where we do need reference to the entire object, I agree that it would be great to only return a timezone string.
That's what the selector will be used for. Maybe it would be great to distinguish between settingsTimezone
object and userTimezone
string in the function name
utils/timezone.jsx
Outdated
const dateTimeFormat = new Intl.DateTimeFormat(); | ||
|
||
export function getSupportedTimezones() { | ||
if (global.mm_config && global.mm_config.SupportedTimezones) { |
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 should be turned into a selector in the redux library
utils/timezone.jsx
Outdated
} | ||
} | ||
|
||
export function getCurrentTimezone(userTimezone) { |
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.
As mentioned above, could this be turned into a selector, possibly combined with the one I was talking about before? Outside of the settings modal, I can't think of any code that will care about the automatic vs manual timezone, so I don't think that needs to be passed around
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.
yup, this will be turned into a selector 👍
@miguelespinoza You've got some style issues
|
@esethna here's the server PR for reference: mattermost/mattermost#8560 |
Cool, we will need a dev to switch to |
I've changed that ExperimentalTimezone setting for this spinmint and restarted it. |
Hitting an issue with the timezone setting disappearing after setting it. @miguelespinoza looking into it. |
Done, it was a mistake on my part. That change was made after the error that popped up yesterday. For fresh new users, their timezone was never getting automatically set, so I call autoUpdateTimezone one last time in the settings screen to ensure it gets another chance. @esethna tested with setting automatic and manual timezones. |
|
Thanks @miguelespinoza, testing now |
Spinmint test server created at: https://i-099c369abdaee52e7.spinmint.com Test Admin Account: Username: Test User Account: Username: Instance ID: i-099c369abdaee52e7 |
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.
Thanks @miguelespinoza!
- Minor authorship updates - `serialize-error` adopted a differently formatted version of the MIT license text
- Minor authorship updates - `serialize-error` adopted a differently formatted version of the MIT license text
Summary
mattermost/mattermost-mobile#1456
mattermost/mattermost#8185
Ticket Link
PLT-541 Add a "Time zone" field to account settings - Mattermost
Checklist
make check-style
to check for style errors (required for all pull requests)make test
to ensure unit and component tests passedScreenshots
manual timezone selection
automatic timezone selection
Feature in action
manual
automatic