Skip to content
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

Add local time to hover cards based on user location #1249

Closed
wants to merge 30 commits into from
Closed

Add local time to hover cards based on user location #1249

wants to merge 30 commits into from

Conversation

jorgegonzalez
Copy link

@jorgegonzalez jorgegonzalez commented Apr 6, 2018

Closes #1233.

Still need to add some padding as well as a clock icon like in the linked issue, any suggestions for that?

One of the issues with this solution is that nothing will render if too many API requests are made to get the user's location and timezone info, so I just made it silently fail. Any input is appreciated.

image

image

@fregante
Copy link
Member

fregante commented Apr 6, 2018

Oof, big PR :) Thanks!

I have 2 concerns though:

  1. moment-timezone weighs at least double our whole extension:

    ❯ package-size moment-timezone
    
      package                 size    minified  gzipped
      moment-timezone@0.5.14  840 kB  414 kB    91.2 kB
    
    
  2. Two sequential Google API requests for every timezone

@fregante
Copy link
Member

fregante commented Apr 6, 2018

  1. I wonder if modern Intl.DateTimeFormat browser APIs can let us skip moment-timezone

@fregante
Copy link
Member

fregante commented Apr 6, 2018

It looks pretty good though; we probably can't get anything better than Google's APIs. Perhaps we can just cache the locations and avoid the current user's

@fregante
Copy link
Member

fregante commented Apr 6, 2018

This is what I see, probably the +7 can be dropped, we already have the location name

screen shot 2018-04-06 at 11 04 53

@jorgegonzalez
Copy link
Author

Yeah, I'll find a better solution than moment-timezone. At least this is a nice proof of concept! I'll also implement a cache for the requests soon.

Would GMT+7 be better in those cases or should we not show the timezone at all?

@jorgegonzalez
Copy link
Author

Added the clock.

image

image

@fregante
Copy link
Member

fregante commented Apr 6, 2018

We can skip moment-timezone entirely by just reading the rawOffset and dstOffset from the timezone API call:

❯ curl 'https://maps.googleapis.com/maps/api/timezone/json?location=40.741895,-73.989308&timestamp=1331161200&sensor=false'
{
   "dstOffset" : 0,
   "rawOffset" : -18000,
   "status" : "OK",
   "timeZoneId" : "America/New_York",
   "timeZoneName" : "Eastern Standard Time"
}

The offset is in seconds and can be combined with new Date().getTimezoneOffset() (it always shows the offset of the system time), which is in minutes, in the opposite direction.

Once we have the correct Date it can be formatted via Intl.DateTimeFormat.


Side note: Those APIs give a crazy amount of info. Gotta love google for this stuff. I wish they included the simple timezone info in a single call though.

https://maps.googleapis.com/maps/api/geocode/json?address=40.741895,-73.989308

try {
const location = getLocationFromHovercard(
Array.from(hoverCardMutation.addedNodes)
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The past 10 lines can probably be replaced with a select('.Popover .octicon-location') + .nextSibling.textContent

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brilliant.

return timezoneResponse;
} catch (error) {
return null;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to catch here, let it bubble up


function createTimezoneRequestURL(lat, lng) {
const timezoneAPI = 'https://maps.googleapis.com/maps/api/timezone/json?location=';
return `${timezoneAPI}${lat},${lng}&timestamp=1331161200&sensor=false`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the timestamp should be Date.now() / 1000

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's just me, but this request seems to fail more often with Date.now() / 1000 🤔

/>
</svg>
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be moved to icons.js.

As per #1099:

  1. The extra classes should be removed. They can probably be added to a parent or via .classList.add()
  2. version and viewbox also can be removed

const time = moment.tz(moment(), timeZoneId).format('HH:mm');
const abbr = moment.tz(timeZoneId).zoneAbbr();
hoverCard.append(clockIcon());
hoverCard.append(domify(`${time} ${abbr}`));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

append accepts plain strings and multiple elements, so it can be:

hoverCard.append(clockIcon(), `${time} ${abbr}`);

}
} catch (e) {
// Silently fail when there are too many API requests
}
Copy link
Member

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 need suppress the errors. If it's not working we should probably show why.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will fail when either the timezoneAPI or the locationAPIrequest fails due to too many requests, but the requests often succeed on the next mouseover. How do you want to handle this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd let it go to the console for now. Perhaps it can use onetime to show the error in the console once per load at least, so it doesn't fail completely silently and doesn't spam the console with errors.

But @sindresorhus perhaps can give better advice on expected error handling.

observer.observe(document.querySelector('div.Popover-message'), {
attributes: true,
childList: true
});
Copy link
Member

@fregante fregante Apr 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can listen to it directly:

delegate('[data-hydro-view]', 'view', addTime)

The only annoyance is that view is fired after 500ms, for whatever reason:

setTimeout(function() {
	if (document.body && document.body.contains(e)) {
		var t = a.query(e, "[data-hovercard-tracking]").getAttribute("data-hovercard-tracking");
		if (t) {
			var n = {
				event_type: "user-hovercard-load"
			};
			n.dimensions = JSON.parse(t),
			Yr(n)
		}
		var r = a.query(e, "[data-hydro-view]");
		i.fire(r, "view")
	}
}, 500)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only annoyance is that view is fired after 500ms, for whatever reason

I think this won't be an issue if we cache the requests.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah I think we might need a way to make it faster?

@jorgegonzalez
Copy link
Author

jorgegonzalez commented Apr 9, 2018

So @bfred-it, you suggested that we only show the local time and remove the timezone abbreviation, correct?

I'll start working on removing moment-timezone soon.

@jorgegonzalez
Copy link
Author

image

I'm not sure why this is happening.

const location = select('.Popover .octicon-location').nextSibling.textContent.trim();
const timeZone = await getTimezone(location);

if (timeZone) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an off by one issue here that I haven't figured out.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean?

@fregante
Copy link
Member

fregante commented Apr 9, 2018

I'm not sure why this is happening.

Probably because GitHub notices you're hovering the same item over and over and doesn't update the item... but it still fires the "view" event. You'll just have to do a !select.exists(...octicon clock...) at the start of the async function


const now = new Date(Date.now());

if (document.body.hasAttribute(username)) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's probably a better way of doing this.

@jorgegonzalez
Copy link
Author

image

Should work now.

@fregante
Copy link
Member

fregante commented Apr 9, 2018

When you're a good point you can remove "WIP" from the title and I'll review again

@fregante fregante self-assigned this Apr 9, 2018
@fregante
Copy link
Member

fregante commented Apr 16, 2018

Requesting reviews because this adds two API calls to Google servers and new permissions.

@@ -60,5 +63,5 @@ async function updateHovercard() {
}

export default async function () {
delegate('[data-hydro-view]', 'view', updateHovercard);
observeEl('.Popover-message', updateHovercard);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bfred-it Curious, why did you make this switch back?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They dropped the view event

@sindresorhus
Copy link
Member

Seems to work great. Can we include the timezone too? I'm only seeing this:

screen shot 2018-04-17 at 12 02 21

I would like to see 1:02 => 1:02 +5 or something. We should also show if the time is in a different day than my day.

@sindresorhus
Copy link
Member

The time should not be faded in if it's already cached. Then we should just show it right away.

@fregante
Copy link
Member

fregante commented Apr 17, 2018

I would like to see 1:02 => 1:02 +5 or something

@sindresorhus should the timezone be relative to GMT (whatever we get from Intl) or relative to me? "Sindre is -1 from me", not "+6"

We should also show if the time is in a different day than my day.

How do you display that though? Usually it's written as "+1" but you're asking to also show the timezone, so that'd be confusing.

1:02 +5 tomorrow?
1:02 +5 wednesday?

The time should not be faded in if it's already cached. Then we should just show it right away.

Thanks to these lines, there's no way to tell... other than manually tracking the time it took to getTimezoneOffset()

@sindresorhus
Copy link
Member

should the timezone be relative to GMT (whatever we get from Intl) or relative to me? "Sindre is -1 from me", not "+6"

Relative to UTC.

How do you ]display that though? Usually it's written as "+1" but you're asking to also show the timezone, so that'd be confusing.

We would be explicit then:

1:02 (Tomorrow) UTC+5?

Thanks to these lines, there's no way to tell... other than manually tracking the time it took to getTimezoneOffset()

Could just make it return a "tuple":

return [isCached, locationOffsets.get(location)];

@hkdobrev
Copy link
Contributor

I was thinking of just showing the local time without a timezone indicator so let's say person A is in Italy and person B is in Greece (1 hour time difference). So if it is 16:00 in Italy and Person A hovers on Person B, they will see 17:00. We'll need a timezone indicator if we want to refer to an absolute timestamp like a rocket launch window. If we refer to a local time somewhere, it should be like when you ask Google what's the time somewhere and Google shows you the local time with no timezone modifier.

@sindresorhus
Copy link
Member

@hkdobrev Google does show the timezone:

screen shot 2018-04-17 at 14 20 01

@fregante
Copy link
Member

fregante commented Apr 17, 2018

All options are valid, we just have to decide what's useful in this context.

  • We could show the absolute time, 9:19 GMT+2 Tuesday
  • We could show the relative time to us, 9:19 (+7 hours away) and display the full date/time/zone in the title attribute. GH shows relative dates this almost everywhere.

If we answer this question we might get closer to what we should show:

Why would I want to know the time for a specific person?

I would want to know:

  • Is person at work? 9:19 answers this question
  • How far (time) is person from me? -16h would show we're pretty far and will give me a reusable number to my mind (i.e. I don't have to use the popup)

To counter my own point, I made this a while ago. It uses absolute times: https://time.bfred.it/

@fregante fregante removed their assignment Apr 17, 2018
@sindresorhus
Copy link
Member

sindresorhus commented Apr 21, 2018

We could show the relative time to us, 9:19 (+7 hours away) and display the full date/time/zone in the title attribute. GH shows relative dates this almost everywhere.

Let's go with this. I thought I needed the UTC time zone, but I can't think of what I would use it for when I know how many hours away from me and the local time. Like you said, we can display the full info in the title attribute.

If it's a different day, maybe we can also include the name of the day here, to make it quicker to scan: 9:19 Tuesday (-7 hours away)?

@yakov116
Copy link
Member

Giving this a bump...

@jorgegonzalez
Copy link
Author

Sorry, I've been pretty busy recently. I'll try and finish this PR soon. Anyone else is welcome to push to my branch too!

@sindresorhus
Copy link
Member

@jorgegonzalez Do you think you will finish this at some point or should we close? :)

@jorgegonzalez
Copy link
Author

@sindresorhus let me try and finish it this weekend. I've been swamped since I started working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

Add local time to hover cards based on user location
6 participants