diff --git a/server/public.go b/server/public.go index ce860869..416ddae4 100644 --- a/server/public.go +++ b/server/public.go @@ -460,6 +460,9 @@ func (s *PublicServer) parseTemplates() []*template.Template { "formatUnixTime": formatUnixTime, "formatAmount": s.formatAmount, "formatAmountWithDecimals": formatAmountWithDecimals, + "formatInt64": formatInt64, + "formatInt": formatInt, + "formatUint32": formatUint32, "setTxToTemplateData": setTxToTemplateData, "feePerByte": feePerByte, "isOwnAddress": isOwnAddress, @@ -528,16 +531,70 @@ func (s *PublicServer) parseTemplates() []*template.Template { return t } -func formatUnixTime(ut int64) string { +func relativeTime(d int64) string { + var u string + if d < 60 { + if d == 1 { + u = " sec" + } else { + u = " secs" + } + } else if d < 3600 { + d /= 60 + if d == 1 { + u = " min" + } else { + u = " mins" + } + } else if d < 3600*24 { + d /= 3600 + if d == 1 { + u = " hour" + } else { + u = " hours" + } + } else { + d /= 3600 * 24 + if d == 1 { + u = " day" + } else { + u = " days" + } + } + return strconv.FormatInt(d, 10) + u +} + +func formatUnixTime(ut int64) template.HTML { t := time.Unix(ut, 0) return formatTime(&t) } -func formatTime(t *time.Time) string { +func formatTime(t *time.Time) template.HTML { if t == nil { return "" } - return t.Format(time.RFC1123) + u := t.Unix() + if u <= 0 { + return "" + } + d := time.Now().Unix() - u + f := t.UTC().Format("2006-01-02 15:04:05") + if d < 0 { + return template.HTML(f) + } + r := relativeTime(d) + if d > 3600*24 { + d = d % (3600 * 24) + if d >= 3600 { + r += " " + relativeTime(d) + } + } else if d > 3600 { + d = d % 3600 + if d >= 60 { + r += " " + relativeTime(d) + } + } + return template.HTML(`` + r + " ago") } func toJSON(data interface{}) string { @@ -564,6 +621,28 @@ func formatAmountWithDecimals(a *api.Amount, d int) string { return a.DecimalString(d) } +func formatInt(i int) template.HTML { + return formatInt64(int64(i)) +} + +func formatUint32(i uint32) template.HTML { + return formatInt64(int64(i)) +} + +func formatInt64(i int64) template.HTML { + s := strconv.FormatInt(i, 10) + t := (len(s) - 1) / 3 + if t <= 0 { + return template.HTML(s) + } + t *= 3 + rv := s[:len(s)-t] + for i := len(s) - t; i < len(s); i += 3 { + rv += `` + s[i:i+3] + "" + } + return template.HTML(rv) +} + // called from template to support txdetail.html functionality func setTxToTemplateData(td *TemplateData, tx *api.Tx) *TemplateData { td.Tx = tx diff --git a/server/public_test.go b/server/public_test.go index b5ba334c..c21622b0 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -4,11 +4,13 @@ package server import ( "encoding/json" + "html/template" "io/ioutil" "net/http" "net/http/httptest" "net/url" "os" + "reflect" "strconv" "strings" "testing" @@ -1607,3 +1609,29 @@ func Test_PublicServer_BitcoinType(t *testing.T) { socketioTestsBitcoinType(t, ts) websocketTestsBitcoinType(t, ts) } + +func Test_formatInt64(t *testing.T) { + tests := []struct { + name string + n int64 + want template.HTML + }{ + {"1", 1, "1"}, + {"13", 13, "13"}, + {"123", 123, "123"}, + {"1234", 1234, `1234`}, + {"91234", 91234, `91234`}, + {"891234", 891234, `891234`}, + {"7891234", 7891234, `7891234`}, + {"67891234", 67891234, `67891234`}, + {"567891234", 567891234, `567891234`}, + {"4567891234", 4567891234, `4567891234`}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := formatInt64(tt.n); !reflect.DeepEqual(got, tt.want) { + t.Errorf("formatInt64() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/static/css/TTHoves/TTHoves-Black.woff b/static/css/TTHoves/TTHoves-Black.woff new file mode 100644 index 00000000..e3024357 Binary files /dev/null and b/static/css/TTHoves/TTHoves-Black.woff differ diff --git a/static/css/TTHoves/TTHoves-Black.woff2 b/static/css/TTHoves/TTHoves-Black.woff2 new file mode 100644 index 00000000..c8150597 Binary files /dev/null and b/static/css/TTHoves/TTHoves-Black.woff2 differ diff --git a/static/css/TTHoves/TTHoves-Bold.woff b/static/css/TTHoves/TTHoves-Bold.woff new file mode 100644 index 00000000..96630ca3 Binary files /dev/null and b/static/css/TTHoves/TTHoves-Bold.woff differ diff --git a/static/css/TTHoves/TTHoves-Bold.woff2 b/static/css/TTHoves/TTHoves-Bold.woff2 new file mode 100644 index 00000000..8f1fd690 Binary files /dev/null and b/static/css/TTHoves/TTHoves-Bold.woff2 differ diff --git a/static/css/TTHoves/TTHoves-BoldItalic.woff b/static/css/TTHoves/TTHoves-BoldItalic.woff new file mode 100644 index 00000000..3c76f859 Binary files /dev/null and b/static/css/TTHoves/TTHoves-BoldItalic.woff differ diff --git a/static/css/TTHoves/TTHoves-BoldItalic.woff2 b/static/css/TTHoves/TTHoves-BoldItalic.woff2 new file mode 100644 index 00000000..5fa30f30 Binary files /dev/null and b/static/css/TTHoves/TTHoves-BoldItalic.woff2 differ diff --git a/static/css/TTHoves/TTHoves-DemiBold.woff b/static/css/TTHoves/TTHoves-DemiBold.woff new file mode 100644 index 00000000..6dc9af24 Binary files /dev/null and b/static/css/TTHoves/TTHoves-DemiBold.woff differ diff --git a/static/css/TTHoves/TTHoves-DemiBold.woff2 b/static/css/TTHoves/TTHoves-DemiBold.woff2 new file mode 100644 index 00000000..ca31b228 Binary files /dev/null and b/static/css/TTHoves/TTHoves-DemiBold.woff2 differ diff --git a/static/css/TTHoves/TTHoves-ExtraBold.woff b/static/css/TTHoves/TTHoves-ExtraBold.woff new file mode 100644 index 00000000..43ab1969 Binary files /dev/null and b/static/css/TTHoves/TTHoves-ExtraBold.woff differ diff --git a/static/css/TTHoves/TTHoves-ExtraBold.woff2 b/static/css/TTHoves/TTHoves-ExtraBold.woff2 new file mode 100644 index 00000000..c37df5c4 Binary files /dev/null and b/static/css/TTHoves/TTHoves-ExtraBold.woff2 differ diff --git a/static/css/TTHoves/TTHoves-ExtraLight.woff b/static/css/TTHoves/TTHoves-ExtraLight.woff new file mode 100644 index 00000000..e7e8aa23 Binary files /dev/null and b/static/css/TTHoves/TTHoves-ExtraLight.woff differ diff --git a/static/css/TTHoves/TTHoves-ExtraLight.woff2 b/static/css/TTHoves/TTHoves-ExtraLight.woff2 new file mode 100644 index 00000000..15db8acd Binary files /dev/null and b/static/css/TTHoves/TTHoves-ExtraLight.woff2 differ diff --git a/static/css/TTHoves/TTHoves-Light.woff b/static/css/TTHoves/TTHoves-Light.woff new file mode 100644 index 00000000..be30da28 Binary files /dev/null and b/static/css/TTHoves/TTHoves-Light.woff differ diff --git a/static/css/TTHoves/TTHoves-Light.woff2 b/static/css/TTHoves/TTHoves-Light.woff2 new file mode 100644 index 00000000..4a57aeb4 Binary files /dev/null and b/static/css/TTHoves/TTHoves-Light.woff2 differ diff --git a/static/css/TTHoves/TTHoves-Medium.woff b/static/css/TTHoves/TTHoves-Medium.woff new file mode 100644 index 00000000..3277fb8a Binary files /dev/null and b/static/css/TTHoves/TTHoves-Medium.woff differ diff --git a/static/css/TTHoves/TTHoves-Medium.woff2 b/static/css/TTHoves/TTHoves-Medium.woff2 new file mode 100644 index 00000000..3dae893a Binary files /dev/null and b/static/css/TTHoves/TTHoves-Medium.woff2 differ diff --git a/static/css/TTHoves/TTHoves-Regular.woff b/static/css/TTHoves/TTHoves-Regular.woff new file mode 100644 index 00000000..dd7b6fe5 Binary files /dev/null and b/static/css/TTHoves/TTHoves-Regular.woff differ diff --git a/static/css/TTHoves/TTHoves-Regular.woff2 b/static/css/TTHoves/TTHoves-Regular.woff2 new file mode 100644 index 00000000..a2cddb29 Binary files /dev/null and b/static/css/TTHoves/TTHoves-Regular.woff2 differ diff --git a/static/css/TTHoves/TTHoves.css b/static/css/TTHoves/TTHoves.css new file mode 100644 index 00000000..2d2b8a30 --- /dev/null +++ b/static/css/TTHoves/TTHoves.css @@ -0,0 +1,39 @@ +@font-face { + font-family: 'TT Hoves'; + src: url('./TTHoves-Bold.woff2') format('woff2'), + url('./TTHoves-Bold.woff') format('woff'); + font-weight: bold; + font-style: normal; +} + +@font-face { + font-family: 'TT Hoves'; + src: url('./TTHoves-Regular.woff2') format('woff2'), + url('./TTHoves-Regular.woff') format('woff'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'TT Hoves'; + src: url('./TTHoves-Light.woff2') format('woff2'), + url('./TTHoves-Light.woff') format('woff'); + font-weight: 300; + font-style: normal; +} + +@font-face { + font-family: 'TT Hoves'; + src: url('./TTHoves-DemiBold.woff2') format('woff2'), + url('./TTHoves-DemiBold.woff') format('woff'); + font-weight: 600; + font-style: normal; +} + +@font-face { + font-family: 'TT Hoves'; + src: url('./TTHoves-Medium.woff2') format('woff2'), + url('./TTHoves-Medium.woff') format('woff'); + font-weight: 500; + font-style: normal; +} \ No newline at end of file diff --git a/static/css/main2.css b/static/css/main2.css new file mode 100644 index 00000000..18d3d447 --- /dev/null +++ b/static/css/main2.css @@ -0,0 +1,303 @@ +@import "TTHoves/TTHoves.css"; + +* { + margin: 0px; + padding: 0px; + outline: none; + font-family: "TT Hoves", -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif; +} + +html, +body { + height: 100%; +} + +body { + min-height: 100%; + margin: 0; + background: linear-gradient(to bottom, #F6F6F6 300px, #E5E5E5 0), #E5E5E5; + background-repeat: no-repeat; +} + +a { + color: #00854D; + text-decoration: none; +} + +a:hover { + color: #00854D; + text-decoration: underline; +} + +#header { + position: absolute; + top: 0; + left: 0; + width: 100%; + margin: 0; + padding-bottom: 0; + padding-top: 0; + background-color: white; + border: 0; + z-index: 10; +} + +#header .navbar { + --bs-navbar-padding-y: 0.7rem; +} + +#header .form-control-lg { + font-size: 1rem; + padding: 0.75rem 1rem; +} + +.bb-group { + border: 0.6rem solid #F6F6F6; + background-color: #F6F6F6; + border-radius: 0.5rem; + position: relative; + display: inline-flex; + vertical-align: middle; +} + +.bb-group>.btn { + --bs-btn-padding-x: 0.5rem; + --bs-btn-padding-y: 0.22rem; + --bs-btn-border-radius: 0.3rem; + --bs-btn-border-width: 0; + color: #545454; +} + +.bb-group>.btn-check:checked+.btn, +.bb-group .btn.active { + color: black; + font-weight: bold; + background-color: white; +} + +.paging { + display: flex; +} + +.paging .bb-group>.btn { + min-width: 2rem; + margin-left: 0.1rem; + margin-right: 0.1rem; +} + +.paging .bb-group>.btn:hover { + background-color: white; +} + +.paging a { + text-decoration: none; +} + +.btn-paging { + --bs-btn-color: #757575; + --bs-btn-border-color: #E2E2E2; + --bs-btn-hover-color: black; + --bs-btn-hover-bg: #F6F6F6; + --bs-btn-hover-border-color: #E2E2E2; + --bs-btn-focus-shadow-rgb: 108, 117, 125; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #E2E2E2; + --bs-btn-active-border-color: #E2E2E2; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-gradient: none; + --bs-btn-padding-y: 0.75rem; + --bs-btn-padding-x: 1.1rem; + --bs-btn-border-radius: 0.5rem; + --bs-btn-font-weight: bold; + background-color: #F6F6F6; +} + +span.btn-paging { + cursor: initial; +} + +span.btn-paging:hover { + color: #757575; +} + +.paging-group { + border: 1px solid #E2E2E2; + border-radius: 0.5rem; +} + +.paging-group>.bb-group { + border: 0.53rem solid #F6F6F6; +} + +#wrap { + min-height: 100%; + height: auto; + padding: 112px 0 75px 0; + margin: 0 auto -56px; +} + +#footer { + background-color: black; + color: #757575; + height: 56px; + overflow: hidden; +} + +.navbar-form { + width: 60%; +} + +.navbar-form button { + margin-left: -50px; + position: relative; +} + +.search-icon { + width: 16px; + height: 16px; + position: absolute; + top: 16px; + background-size: cover; + background-image: url("data:image/svg+xml, %3Csvg style='background: white%3B' width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7.24976 12.5C10.1493 12.5 12.4998 10.1495 12.4998 7.25C12.4998 4.35051 10.1493 2 7.24976 2C4.35026 2 1.99976 4.35051 1.99976 7.25C1.99976 10.1495 4.35026 12.5 7.24976 12.5Z' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' /%3E%3Cpath d='M10.962 10.9625L13.9996 14.0001' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' /%3E%3C/svg%3E"); +} + +.navbar-form ::placeholder { + color: #E2E2E2; +} + +.ellipsis { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.data-table { + table-layout: fixed; + overflow-wrap: break-word; + margin-left: 8px; + margin-top: 2rem; + margin-bottom: 2rem; + width: calc(100% - 16px); +} + +.data-table thead { + padding-bottom: 20px; +} + +.table.data-table>:not(caption)>*>* { + padding: 0.8rem 0.8rem; + background-color: var(--bs-table-bg); + border-bottom-width: 1px; + box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg); +} + +.table.data-table>thead>*>* { + padding-bottom: 1.5rem; +} + +.table.data-table>*>*:last-child>* { + border-bottom: none; +} + +.data-table thead, +.data-table thead tr, +.data-table thead th { + color: #757575; + border: none; + font-weight: normal; +} + +.data-table tbody { + background: white; + border-radius: 8px; + box-shadow: 0 0 0 8px white; +} + +.data-table h3, +.data-table h6 { + margin-bottom: 0; +} + +.data-table h3 { + color: black; +} + +.info-table tbody { + display: inline-table; + width: 100%; +} + +.info-table td { + font-weight: bold; +} + +.info-table tr>td:first-child { + font-weight: normal; + color: #757575; +} + +.ns:before { + content: " "; +} + +.trezor-logo { + width: 128px; + height: 32px; + position: absolute; + top: 15px; + background-size: cover; + background-image: url("data:image/svg+xml, %3Csvg style='width: 128px%3B' version='1.1' xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 163.7 41.9' space='preserve'%3E%3Cpolygon points='101.1 12.8 118.2 12.8 118.2 17.3 108.9 29.9 118.2 29.9 118.2 35.2 101.1 35.2 101.1 30.7 110.4 18.1 101.1 18.1'%3E%3C/polygon%3E%3Cpath d='M158.8 26.9c2.1-0.8 4.3-2.9 4.3-6.6c0-4.5-3.1-7.4-7.7-7.4h-10.5v22.3h5.8v-7.5h2.2l4.1 7.5h6.7L158.8 26.9z M154.7 22.5h-4V18h4c1.5 0 2.5 0.9 2.5 2.2C157.2 21.6 156.2 22.5 154.7 22.5z'%3E%3C/path%3E%3Cpath d='M130.8 12.5c-6.8 0-11.6 4.9-11.6 11.5s4.9 11.5 11.6 11.5s11.7-4.9 11.7-11.5S137.6 12.5 130.8 12.5z M130.8 30.3c-3.4 0-5.7-2.6-5.7-6.3c0-3.8 2.3-6.3 5.7-6.3c3.4 0 5.8 2.6 5.8 6.3C136.6 27.7 134.2 30.3 130.8 30.3z'%3E%3C/path%3E%3Cpolygon points='82.1 12.8 98.3 12.8 98.3 18 87.9 18 87.9 21.3 98 21.3 98 26.4 87.9 26.4 87.9 30 98.3 30 98.3 35.2 82.1 35.2'%3E%3C/polygon%3E%3Cpath d='M24.6 9.7C24.6 4.4 20 0 14.4 0S4.2 4.4 4.2 9.7v3.1H0v22.3h0l14.4 6.7l14.4-6.7h0V12.9h-4.2V9.7z M9.4 9.7c0-2.5 2.2-4.5 5-4.5s5 2 5 4.5v3.1H9.4V9.7z M23 31.5l-8.6 4l-8.6-4V18.1H23V31.5z'%3E%3C/path%3E%3Cpath d='M79.4 20.3c0-4.5-3.1-7.4-7.7-7.4H61.2v22.3H67v-7.5h2.2l4.1 7.5H80l-4.9-8.3C77.2 26.1 79.4 24 79.4 20.3z M71 22.5h-4V18h4c1.5 0 2.5 0.9 2.5 2.2C73.5 21.6 72.5 22.5 71 22.5z'%3E%3C/path%3E%3Cpolygon points='40.5 12.8 58.6 12.8 58.6 18.1 52.4 18.1 52.4 35.2 46.6 35.2 46.6 18.1 40.5 18.1'%3E%3C/polygon%3E%3C/svg%3E"); +} + +.copy-icon { + width: 16px; + height: 16px; + background-size: cover; + background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10.5 10.4996H13.5V2.49963H5.5V5.49963' stroke='%2300854D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M10.4998 5.49976H2.49976V13.4998H10.4998V5.49976Z' stroke='%2300854D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); +} + +@media (max-width: 768px) { + body { + font-size: 0.8rem; + } + + .btn { + --bs-btn-font-size: 0.8rem; + } +} + +@media (max-width: 991px) { + .trezor-logo { + top: 10px; + } + + .table.data-table>:not(caption)>*>* { + padding: 0.8rem 0.4rem; + } +} + +@media (min-width: 769px) { + body { + font-size: 0.9rem; + } + + .btn { + --bs-btn-font-size: 0.9rem; + } +} + +@media (min-width: 1200px) { + + .h1, + h1 { + font-size: 2.4rem; + } + + body { + font-size: 1rem; + } + + .btn { + --bs-btn-font-size: 1rem; + } +} \ No newline at end of file diff --git a/static/templates/address.html b/static/templates/address.html index c3bc0c9e..03fb4fa9 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -203,11 +203,11 @@ {{- end -}}
- + {{template "paging" $data}}
{{- range $tx := $addr.Transactions}}{{$data := setTxToTemplateData $data $tx}}{{template "txdetail" $data}}{{end -}}
- +{{template "paging" $data }} {{end}}{{end}} \ No newline at end of file diff --git a/static/templates/base.html b/static/templates/base.html index 7127a63f..58d0bd7b 100644 --- a/static/templates/base.html +++ b/static/templates/base.html @@ -4,90 +4,92 @@ - - + + + Trezor {{.CoinLabel}} Explorer +
{{- template "specific" . -}}
-