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

Use auto-updating, natively hoverable, localized time elements #23988

Merged
merged 59 commits into from
Apr 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
506d1b3
Use auto-updating, natively hoverable, localized time elements
yardenshoham Apr 7, 2023
baee7bd
Use shared templates
yardenshoham Apr 7, 2023
630767a
Merge branch 'main' into time
yardenshoham Apr 8, 2023
c5ac2d2
Merge branch 'main' into time
yardenshoham Apr 8, 2023
fcb90d3
Merge branch 'main' into time
yardenshoham Apr 8, 2023
a58c19f
Merge branch 'main' into time
yardenshoham Apr 8, 2023
f9c0e98
Use `tense="path"` to avoid the unlocalized `on`
yardenshoham Apr 8, 2023
687e973
Merge branch 'main' into time
yardenshoham Apr 8, 2023
359706e
Remove test file
yardenshoham Apr 8, 2023
bdcc448
Merge branch 'main' into time
yardenshoham Apr 8, 2023
f35ad55
Merge branch 'main' into time
yardenshoham Apr 8, 2023
4801743
Use a better format for javascript parsing
yardenshoham Apr 8, 2023
5d282d1
Merge branch 'main' into time
yardenshoham Apr 8, 2023
113aaf7
Fix tests
yardenshoham Apr 8, 2023
fc6b5c3
Drop tense=past to support future dates
yardenshoham Apr 8, 2023
f2838d9
Update comment
yardenshoham Apr 8, 2023
def9978
Localize `on`
yardenshoham Apr 8, 2023
0c313f6
Undo unnecessary changes
yardenshoham Apr 8, 2023
617435f
Merge branch 'main' into time
yardenshoham Apr 8, 2023
fc81d1b
Fix missed undo of find-and-replace
yardenshoham Apr 9, 2023
29f385e
Merge branch 'main' into time
yardenshoham Apr 9, 2023
4c238b2
Merge branch 'main' into time
yardenshoham Apr 9, 2023
05d4c58
Merge branch 'main' into time
yardenshoham Apr 9, 2023
85385cb
Merge branch 'main' into time
yardenshoham Apr 9, 2023
217c36e
Merge branch 'main' into time
yardenshoham Apr 9, 2023
89abdd3
Merge branch 'main' into time
yardenshoham Apr 9, 2023
8c18c51
Merge branch 'main' into time
yardenshoham Apr 9, 2023
8423fba
Merge branch 'main' into time
yardenshoham Apr 9, 2023
faee6f0
Use `RFC3339`
yardenshoham Apr 9, 2023
49b26a4
Merge branch 'main' into time
yardenshoham Apr 9, 2023
7aaee69
Allow interactive tooltips, disable the `title`
yardenshoham Apr 9, 2023
98e89a0
Revert "Allow interactive tooltips, disable the `title`"
silverwind Apr 9, 2023
1b34f56
add tooltip swap and add it to mutation observer
silverwind Apr 9, 2023
e72bdf7
tweak comment
silverwind Apr 9, 2023
8976d33
filter for .time-since
silverwind Apr 9, 2023
da185ae
general title => tippy tooltip switching
wxiaoguang Apr 10, 2023
80becbc
Merge branch 'main' into time
yardenshoham Apr 10, 2023
722967b
send UTC only to browsers
silverwind Apr 10, 2023
67b0aa9
Merge branch 'main' into time
yardenshoham Apr 10, 2023
d6e28d6
handle newly added tooltip element itself
wxiaoguang Apr 10, 2023
ec57cdd
Update web_src/js/modules/tippy.js
wxiaoguang Apr 10, 2023
db92c16
explain why the title attribute is kept
wxiaoguang Apr 10, 2023
89b0f02
Update templates/admin/org/list.tmpl
yardenshoham Apr 10, 2023
8969ba8
Update templates/package/shared/cleanup_rules/preview.tmpl
yardenshoham Apr 10, 2023
617add3
Update templates/admin/user/list.tmpl
yardenshoham Apr 10, 2023
d651534
Update templates/admin/packages/list.tmpl
yardenshoham Apr 10, 2023
e03f316
Update templates/admin/user/list.tmpl
yardenshoham Apr 10, 2023
17d883f
Update templates/admin/repo/list.tmpl
yardenshoham Apr 10, 2023
12cecdf
Drop unnecessary `title` from process-row
yardenshoham Apr 10, 2023
fde6c38
Update modules/timeutil/timestamp.go
yardenshoham Apr 10, 2023
3cd6cd8
Merge branch 'main' into time
yardenshoham Apr 10, 2023
460fe71
Found more dates
yardenshoham Apr 10, 2023
b7064ed
Localize notice
yardenshoham Apr 10, 2023
e81c5cd
add on_date
silverwind Apr 10, 2023
838b867
Merge branch 'main' into time
yardenshoham Apr 10, 2023
113e2d5
take pending mutations when disconnect
wxiaoguang Apr 10, 2023
7e27112
update comment, tweak interactiveBorder to prevent some double tooltips
silverwind Apr 10, 2023
16cde5e
Merge branch 'main' into time
yardenshoham Apr 10, 2023
c706cab
Merge branch 'main' into time
yardenshoham Apr 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/templates/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func NewFuncMap() []template.FuncMap {
"TimeSinceUnix": timeutil.TimeSinceUnix,
"Sec2Time": util.SecToTime,
"DateFmtLong": func(t time.Time) string {
return t.Format(time.RFC1123Z)
return t.Format(time.RFC3339)
},
"LoadTimes": func(startTime time.Time) string {
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
Expand Down
135 changes: 6 additions & 129 deletions modules/timeutil/since.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ package timeutil
import (
"fmt"
"html/template"
"math"
"strings"
"time"

"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation"
)

Expand All @@ -24,10 +22,6 @@ const (
Year = 12 * Month
)

func round(s float64) int64 {
return int64(math.Round(s))
}

func computeTimeDiffFloor(diff int64, lang translation.Locale) (int64, string) {
diffStr := ""
switch {
Expand Down Expand Up @@ -86,94 +80,6 @@ func computeTimeDiffFloor(diff int64, lang translation.Locale) (int64, string) {
return diff, diffStr
}

func computeTimeDiff(diff int64, lang translation.Locale) (int64, string) {
diffStr := ""
switch {
case diff <= 0:
diff = 0
diffStr = lang.Tr("tool.now")
case diff < 2:
diff = 0
diffStr = lang.Tr("tool.1s")
case diff < 1*Minute:
diffStr = lang.Tr("tool.seconds", diff)
diff = 0

case diff < Minute+Minute/2:
diff -= 1 * Minute
diffStr = lang.Tr("tool.1m")
case diff < 1*Hour:
minutes := round(float64(diff) / Minute)
if minutes > 1 {
diffStr = lang.Tr("tool.minutes", minutes)
} else {
diffStr = lang.Tr("tool.1m")
}
diff -= diff / Minute * Minute

case diff < Hour+Hour/2:
diff -= 1 * Hour
diffStr = lang.Tr("tool.1h")
case diff < 1*Day:
hours := round(float64(diff) / Hour)
if hours > 1 {
diffStr = lang.Tr("tool.hours", hours)
} else {
diffStr = lang.Tr("tool.1h")
}
diff -= diff / Hour * Hour

case diff < Day+Day/2:
diff -= 1 * Day
diffStr = lang.Tr("tool.1d")
case diff < 1*Week:
days := round(float64(diff) / Day)
if days > 1 {
diffStr = lang.Tr("tool.days", days)
} else {
diffStr = lang.Tr("tool.1d")
}
diff -= diff / Day * Day

case diff < Week+Week/2:
diff -= 1 * Week
diffStr = lang.Tr("tool.1w")
case diff < 1*Month:
weeks := round(float64(diff) / Week)
if weeks > 1 {
diffStr = lang.Tr("tool.weeks", weeks)
} else {
diffStr = lang.Tr("tool.1w")
}
diff -= diff / Week * Week

case diff < 1*Month+Month/2:
diff -= 1 * Month
diffStr = lang.Tr("tool.1mon")
case diff < 1*Year:
months := round(float64(diff) / Month)
if months > 1 {
diffStr = lang.Tr("tool.months", months)
} else {
diffStr = lang.Tr("tool.1mon")
}
diff -= diff / Month * Month

case diff < Year+Year/2:
diff -= 1 * Year
diffStr = lang.Tr("tool.1y")
default:
years := round(float64(diff) / Year)
if years > 1 {
diffStr = lang.Tr("tool.years", years)
} else {
diffStr = lang.Tr("tool.1y")
}
diff -= (diff / Year) * Year
}
return diff, diffStr
}

// MinutesToFriendly returns a user friendly string with number of minutes
// converted to hours and minutes.
func MinutesToFriendly(minutes int, lang translation.Locale) string {
Expand Down Expand Up @@ -208,43 +114,14 @@ func timeSincePro(then, now time.Time, lang translation.Locale) string {
return strings.TrimPrefix(timeStr, ", ")
}

func timeSince(then, now time.Time, lang translation.Locale) string {
return timeSinceUnix(then.Unix(), now.Unix(), lang)
}

func timeSinceUnix(then, now int64, lang translation.Locale) string {
lbl := "tool.ago"
diff := now - then
if then > now {
lbl = "tool.from_now"
diff = then - now
}
if diff <= 0 {
return lang.Tr("tool.now")
}

_, diffStr := computeTimeDiff(diff, lang)
return lang.Tr(lbl, diffStr)
}

// TimeSince calculates the time interval and generate user-friendly string.
// TimeSince renders relative time HTML given a time.Time
func TimeSince(then time.Time, lang translation.Locale) template.HTML {
return htmlTimeSince(then, time.Now(), lang)
timestamp := then.UTC().Format(time.RFC3339)
// declare data-tooltip-content attribute to switch from "title" tooltip to "tippy" tooltip
delvh marked this conversation as resolved.
Show resolved Hide resolved
return template.HTML(fmt.Sprintf(`<relative-time class="time-since" prefix="%s" datetime="%s" data-tooltip-content data-tooltip-interactive="true">%s</relative-time>`, lang.Tr("on_date"), timestamp, timestamp))
}

func htmlTimeSince(then, now time.Time, lang translation.Locale) template.HTML {
return template.HTML(fmt.Sprintf(`<span class="time-since" data-tooltip-content="%s" data-tooltip-interactive="true">%s</span>`,
then.In(setting.DefaultUILocation).Format(GetTimeFormat(lang.Language())),
timeSince(then, now, lang)))
}

// TimeSinceUnix calculates the time interval and generate user-friendly string.
// TimeSinceUnix renders relative time HTML given a TimeStamp
func TimeSinceUnix(then TimeStamp, lang translation.Locale) template.HTML {
return htmlTimeSinceUnix(then, TimeStamp(time.Now().Unix()), lang)
}

func htmlTimeSinceUnix(then, now TimeStamp, lang translation.Locale) template.HTML {
return template.HTML(fmt.Sprintf(`<span class="time-since" data-tooltip-content="%s" data-tooltip-interactive="true">%s</span>`,
then.FormatInLocation(GetTimeFormat(lang.Language()), setting.DefaultUILocation),
timeSinceUnix(int64(then), int64(now), lang)))
return TimeSince(then.AsLocalTime(), lang)
}
95 changes: 0 additions & 95 deletions modules/timeutil/since_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package timeutil

import (
"context"
"fmt"
"os"
"testing"
"time"
Expand Down Expand Up @@ -40,46 +39,6 @@ func TestMain(m *testing.M) {
os.Exit(retVal)
}

func TestTimeSince(t *testing.T) {
assert.Equal(t, "now", timeSince(BaseDate, BaseDate, translation.NewLocale("en-US")))

// test that each diff in `diffs` yields the expected string
test := func(expected string, diffs ...time.Duration) {
t.Run(expected, func(t *testing.T) {
for _, diff := range diffs {
actual := timeSince(BaseDate, BaseDate.Add(diff), translation.NewLocale("en-US"))
assert.Equal(t, translation.NewLocale("en-US").Tr("tool.ago", expected), actual)
actual = timeSince(BaseDate.Add(diff), BaseDate, translation.NewLocale("en-US"))
assert.Equal(t, translation.NewLocale("en-US").Tr("tool.from_now", expected), actual)
}
})
}
test("1 second", time.Second, time.Second+50*time.Millisecond)
test("2 seconds", 2*time.Second, 2*time.Second+50*time.Millisecond)
test("1 minute", time.Minute, time.Minute+29*time.Second)
test("2 minutes", 2*time.Minute, time.Minute+30*time.Second)
test("2 minutes", 2*time.Minute, 2*time.Minute+29*time.Second)
test("1 hour", time.Hour, time.Hour+29*time.Minute)
test("2 hours", 2*time.Hour, time.Hour+30*time.Minute)
test("2 hours", 2*time.Hour, 2*time.Hour+29*time.Minute)
test("3 hours", 3*time.Hour, 2*time.Hour+30*time.Minute)
test("1 day", DayDur, DayDur+11*time.Hour)
test("2 days", 2*DayDur, DayDur+12*time.Hour)
test("2 days", 2*DayDur, 2*DayDur+11*time.Hour)
test("3 days", 3*DayDur, 2*DayDur+12*time.Hour)
test("1 week", WeekDur, WeekDur+3*DayDur)
test("2 weeks", 2*WeekDur, WeekDur+4*DayDur)
test("2 weeks", 2*WeekDur, 2*WeekDur+3*DayDur)
test("3 weeks", 3*WeekDur, 2*WeekDur+4*DayDur)
test("1 month", MonthDur, MonthDur+14*DayDur)
test("2 months", 2*MonthDur, MonthDur+15*DayDur)
test("2 months", 2*MonthDur, 2*MonthDur+14*DayDur)
test("1 year", YearDur, YearDur+5*MonthDur)
test("2 years", 2*YearDur, YearDur+6*MonthDur)
test("2 years", 2*YearDur, 2*YearDur+5*MonthDur)
test("3 years", 3*YearDur, 2*YearDur+6*MonthDur)
}

func TestTimeSincePro(t *testing.T) {
assert.Equal(t, "now", timeSincePro(BaseDate, BaseDate, translation.NewLocale("en-US")))

Expand Down Expand Up @@ -113,60 +72,6 @@ func TestTimeSincePro(t *testing.T) {
12*time.Minute+17*time.Second)
}

func TestHtmlTimeSince(t *testing.T) {
setting.TimeFormat = time.UnixDate
setting.DefaultUILocation = time.UTC
// test that `diff` yields a result containing `expected`
test := func(expected string, diff time.Duration) {
actual := htmlTimeSince(BaseDate, BaseDate.Add(diff), translation.NewLocale("en-US"))
assert.Contains(t, actual, `data-tooltip-content="Sat Jan 1 00:00:00 UTC 2000"`)
assert.Contains(t, actual, expected)
}
test("1 second", time.Second)
test("3 minutes", 3*time.Minute+5*time.Second)
test("1 day", DayDur+11*time.Hour)
test("1 week", WeekDur+3*DayDur)
test("3 months", 3*MonthDur+2*WeekDur)
test("2 years", 2*YearDur)
test("3 years", 2*YearDur+11*MonthDur+4*WeekDur)
}

func TestComputeTimeDiff(t *testing.T) {
// test that for each offset in offsets,
// computeTimeDiff(base + offset) == (offset, str)
test := func(base int64, str string, offsets ...int64) {
for _, offset := range offsets {
t.Run(fmt.Sprintf("%s:%d", str, offset), func(t *testing.T) {
diff, diffStr := computeTimeDiff(base+offset, translation.NewLocale("en-US"))
assert.Equal(t, offset, diff)
assert.Equal(t, str, diffStr)
})
}
}
test(0, "now", 0)
test(1, "1 second", 0)
test(2, "2 seconds", 0)
test(Minute, "1 minute", 0, 1, 29)
test(Minute, "2 minutes", 30, Minute-1)
test(2*Minute, "2 minutes", 0, 29)
test(2*Minute, "3 minutes", 30, Minute-1)
test(Hour, "1 hour", 0, 1, 29*Minute)
test(Hour, "2 hours", 30*Minute, Hour-1)
test(5*Hour, "5 hours", 0, 29*Minute)
test(Day, "1 day", 0, 1, 11*Hour)
test(Day, "2 days", 12*Hour, Day-1)
test(5*Day, "5 days", 0, 11*Hour)
test(Week, "1 week", 0, 1, 3*Day)
test(Week, "2 weeks", 4*Day, Week-1)
test(3*Week, "3 weeks", 0, 3*Day)
test(Month, "1 month", 0, 1)
test(Month, "2 months", 16*Day, Month-1)
test(10*Month, "10 months", 0, 13*Day)
test(Year, "1 year", 0, 179*Day)
test(Year, "2 years", 180*Day, Year-1)
test(3*Year, "3 years", 0, 179*Day)
}

func TestMinutesToFriendly(t *testing.T) {
// test that a number of minutes yields the expected string
test := func(expected string, minutes int) {
Expand Down
5 changes: 2 additions & 3 deletions modules/timeutil/timestamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,8 @@ func (ts TimeStamp) AsLocalTime() time.Time {
}

// AsTimeInLocation convert timestamp as time.Time in Local locale
func (ts TimeStamp) AsTimeInLocation(loc *time.Location) (tm time.Time) {
tm = time.Unix(int64(ts), 0).In(loc)
return tm
func (ts TimeStamp) AsTimeInLocation(loc *time.Location) time.Time {
return time.Unix(int64(ts), 0).In(loc)
}

// AsTimePtr convert timestamp as *time.Time in Local locale
Expand Down
3 changes: 2 additions & 1 deletion options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ never = Never

rss_feed = RSS Feed

on_date = on

[aria]
navbar = Navigation Bar
footer = Footer
Expand Down Expand Up @@ -3191,7 +3193,6 @@ details.documentation_site = Documentation Site
details.license = License
assets = Assets
versions = Versions
versions.on = on
versions.view_all = View all
dependency.id = ID
dependency.version = Version
Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@citation-js/plugin-software-formats": "0.6.1",
"@claviska/jquery-minicolors": "2.3.6",
"@github/markdown-toolbar-element": "2.1.1",
"@github/relative-time-element": "4.2.4",
"@github/text-expander-element": "2.3.0",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "18.3.0",
Expand Down
6 changes: 2 additions & 4 deletions routers/web/admin/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ import (
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/updatechecker"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/cron"
Expand All @@ -34,7 +32,7 @@ const (
)

var sysStatus struct {
Uptime string
StartTime string
NumGoroutine int

// General statistics.
Expand Down Expand Up @@ -75,7 +73,7 @@ var sysStatus struct {
}

func updateSystemStatus() {
sysStatus.Uptime = timeutil.TimeSincePro(setting.AppStartTime, translation.NewLocale("en-US"))
sysStatus.StartTime = setting.AppStartTime.Format(time.RFC3339)

m := new(runtime.MemStats)
runtime.ReadMemStats(m)
Expand Down
4 changes: 2 additions & 2 deletions templates/admin/auth/list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{.Name}}</a></td>
<td>{{.TypeName}}</td>
<td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td><span data-tooltip-content="{{.UpdatedUnix.FormatShort}}"><time data-format="short-date" datetime="{{.UpdatedUnix.FormatLong}}">{{.UpdatedUnix.FormatShort}}</time></span></td>
<td><span data-tooltip-content="{{.CreatedUnix.FormatLong}}"><time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time></span></td>
<td>{{template "shared/datetime/short" (dict "Datetime" .UpdatedUnix.FormatLong "Fallback" .UpdatedUnix.FormatShort)}}</td>
<td>{{template "shared/datetime/short" (dict "Datetime" .CreatedUnix.FormatLong "Fallback" .CreatedUnix.FormatShort)}}</td>
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
</tr>
{{end}}
Expand Down
4 changes: 2 additions & 2 deletions templates/admin/cron.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
<td><button type="submit" class="ui green button" name="op" value="{{.Name}}" title="{{$.locale.Tr "admin.dashboard.operation_run"}}">{{svg "octicon-triangle-right"}}</button></td>
<td>{{$.locale.Tr (printf "admin.dashboard.%s" .Name)}}</td>
<td>{{.Spec}}</td>
<td>{{DateFmtLong .Next}}</td>
<td>{{if gt .Prev.Year 1}}{{DateFmtLong .Prev}}{{else}}N/A{{end}}</td>
<td>{{template "shared/datetime/full" (dict "Datetime" (DateFmtLong .Next) "Fallback" (DateFmtLong .Next) )}}</td>
<td>{{if gt .Prev.Year 1}}{{template "shared/datetime/full" (dict "Datetime" (DateFmtLong .Prev) "Fallback" (DateFmtLong .Prev) )}}{{else}}N/A{{end}}</td>
<td>{{.ExecTimes}}</td>
<td {{if ne .Status ""}}data-tooltip-content="{{.FormatLastMessage $.locale}}"{{end}} >{{if eq .Status ""}}—{{else if eq .Status "finished"}}{{svg "octicon-check" 16}}{{else}}{{svg "octicon-x" 16}}{{end}}</td>
</tr>
Expand Down
Loading