From 1a0b30ec1735fa1732e563b1f4170772cb92d0ef Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 30 Jun 2019 08:17:20 -0500 Subject: [PATCH 1/3] explorer: rework proposal page Applies new dcrdata style to /proposal. A small meter is used and some helpful descriptions are added to describe the current state of voting. --- explorer/explorerroutes.go | 5 +- gov/politeia/types/types.go | 71 +++- public/js/controllers/proposal_controller.js | 40 ++- public/js/helpers/meters.js | 14 +- public/scss/charts.scss | 24 +- views/proposal.tmpl | 353 +++++++++---------- 6 files changed, 298 insertions(+), 209 deletions(-) diff --git a/explorer/explorerroutes.go b/explorer/explorerroutes.go index a2d2dd369..44b793ebc 100644 --- a/explorer/explorerroutes.go +++ b/explorer/explorerroutes.go @@ -1812,16 +1812,19 @@ func (exp *explorerUI) ProposalPage(w http.ResponseWriter, r *http.Request) { } } + commonData := exp.commonData(r) str, err := exp.templates.execTemplateToString("proposal", struct { *CommonPageData Data *pitypes.ProposalInfo PoliteiaURL string TimeRemaining string + Metadata *pitypes.ProposalMetadata }{ - CommonPageData: exp.commonData(r), + CommonPageData: commonData, Data: proposalInfo, PoliteiaURL: exp.politeiaAPIURL, TimeRemaining: timeLeft, + Metadata: proposalInfo.Metatdata(int64(commonData.Tip.Height), int64(exp.ChainParams.TargetTimePerBlock/time.Second)), }) if err != nil { diff --git a/gov/politeia/types/types.go b/gov/politeia/types/types.go index 83fd04686..9ba6da6fa 100644 --- a/gov/politeia/types/types.go +++ b/gov/politeia/types/types.go @@ -3,7 +3,14 @@ package types -import piapi "github.com/decred/politeia/politeiawww/api/www/v1" +import ( + "strconv" + + piapi "github.com/decred/politeia/politeiawww/api/www/v1" +) + +// Politeia votes occur in 2016 block windows. +const windowSize = 2016 // ProposalInfo holds the proposal details as document here // https://github.com/decred/politeia/blob/master/politeiawww/api/www/v1/api.md#user-proposals. @@ -204,3 +211,65 @@ func (a *ProposalInfo) IsEqual(b *ProposalInfo) bool { } return true } + +// ProposalMetadata contains some status-dependent data representations for +// display purposes. +type ProposalMetadata struct { + // The start height of the vote. The end height is already part of the + // ProposalInfo struct. + StartHeight int64 + // Time until start for "Authorized" proposals, Time until done for "Started" + // proposals. + SecondsTil int64 + IsPassing bool + Approval float32 + Rejection float32 + Yes int64 + No int64 + VoteCount int64 + QuorumCount int64 + QuorumAchieved bool + PassPercent float32 +} + +// Metadata performs some common manipulations of the ProposalInfo data to +// prepare figures for display. Many of these manipulations require a tip height +// and a target block time for the network, so those must be provided as +// arguments. +func (pinfo *ProposalInfo) Metatdata(tip, targetBlockTime int64) *ProposalMetadata { + meta := new(ProposalMetadata) + desc := pinfo.VoteStatus.ShortDesc() + switch desc { + case "Authorized": + blocksTil := windowSize - tip%windowSize + meta.StartHeight = tip + blocksTil + meta.SecondsTil = blocksTil * targetBlockTime + case "Started", "Finished": + endHeight, _ := strconv.ParseInt(pinfo.ProposalVotes.Endheight, 10, 64) + meta.StartHeight = endHeight - windowSize + for _, count := range pinfo.VoteResults { + switch count.Option.OptionID { + case "yes": + meta.Yes = count.VotesReceived + case "no": + meta.No = count.VotesReceived + } + } + meta.VoteCount = meta.Yes + meta.No + quorumPct := float32(pinfo.QuorumPercentage) / 100 + meta.QuorumCount = int64(quorumPct * float32(pinfo.NumOfEligibleVotes)) + meta.PassPercent = float32(pinfo.PassPercentage) / 100 + pctVoted := float32(meta.VoteCount) / float32(pinfo.NumOfEligibleVotes) + meta.QuorumAchieved = pctVoted > quorumPct + if meta.VoteCount > 0 { + meta.Approval = float32(meta.Yes) / float32(meta.VoteCount) + meta.Rejection = 1 - meta.Approval + } + meta.IsPassing = meta.Approval > meta.PassPercent + if desc == "Started" { + blocksLeft := windowSize - tip%windowSize + meta.SecondsTil = blocksLeft * targetBlockTime + } + } + return meta +} diff --git a/public/js/controllers/proposal_controller.js b/public/js/controllers/proposal_controller.js index 70095f61e..0d623186a 100644 --- a/public/js/controllers/proposal_controller.js +++ b/public/js/controllers/proposal_controller.js @@ -1,4 +1,7 @@ import { Controller } from 'stimulus' +import { ProgressMeter } from '../helpers/meters.js' +import { darkEnabled } from '../services/theme_service' +import globalEventBus from '../services/event_bus_service' import { getDefault } from '../helpers/module_helper' import { multiColumnBarPlotter, synchronize } from '../helpers/chart_helper' import dompurify from 'dompurify' @@ -20,7 +23,6 @@ let percentConfig = { ylabel: 'Approval (%)', colors: ['#2971FF'], labelsSeparateLines: true, - labelsDiv: 'percent-of-votes-legend', underlayCallback: function (context, area, g) { let xVals = g.xAxisExtremes() let xl = g.toDomCoords(xVals[0], 60) @@ -42,7 +44,6 @@ let cumulativeConfig = { labels: ['Date', 'Total Votes'], ylabel: 'Vote Count', colors: ['#2971FF'], - labelsDiv: 'cumulative-legend', underlayCallback: function (context, area, g) { let xVals = g.xAxisExtremes() let xl = g.toDomCoords(xVals[0], 8269) @@ -98,7 +99,6 @@ var hourlyVotesConfig = { ylabel: 'Votes Per Hour', xlabel: 'Date', colors: ['#2DD8A3', '#ED6D47'], - labelsDiv: 'votes-by-time-legend', fillColors: ['rgb(150,235,209)', 'rgb(246,182,163)'] } @@ -111,10 +111,24 @@ let cumulativeData let hourlyVotesData export default class extends Controller { static get targets () { - return ['token'] + return ['token', 'approvalMeter', 'cumulative', 'cumulativeLegend', + 'approval', 'approvalLegend', 'log', 'logLegend' + ] } async connect () { + console.log(this.approvalMeterTarget) + if (this.hasApprovalMeterTarget) { + var opts = { + darkMode: darkEnabled(), + showIndicator: false, + radius: 0.5, + centralFontSize: 30, + meterWidth: 15 + } + this.approvalMeter = new ProgressMeter(this.approvalMeterTarget, opts) + } + let response = await axios.get('/api/proposal/' + this.tokenTarget.dataset.hash) chartData = response.data @@ -124,10 +138,13 @@ export default class extends Controller { this.setChartsData() this.plotGraph() + this.setNightMode = this._setNightMode.bind(this) + globalEventBus.on('NIGHT_MODE', this.setNightMode) } disconnect () { gs.map((chart) => { chart.destroy() }) + globalEventBus.off('NIGHT_MODE', this.setNightMode) } setChartsData () { @@ -152,19 +169,22 @@ export default class extends Controller { } plotGraph () { + percentConfig.labelsDiv = this.approvalLegendTarget + cumulativeConfig.labelsDiv = this.cumulativeLegendTarget + hourlyVotesConfig.labelsDiv = this.logLegendTarget gs = [ new Dygraph( - document.getElementById('percent-of-votes'), + this.approvalTarget, percentData, percentConfig ), new Dygraph( - document.getElementById('cumulative'), + this.cumulativeTarget, cumulativeData, cumulativeConfig ), new Dygraph( - document.getElementById('votes-by-time'), + this.logTarget, hourlyVotesData, hourlyVotesConfig ) @@ -177,4 +197,10 @@ export default class extends Controller { synchronize(gs, options) } + + _setNightMode (state) { + if (this.approvalMeter) { + this.approvalMeter.setDarkMode(state.nightMode) + } + } } diff --git a/public/js/helpers/meters.js b/public/js/helpers/meters.js index 8d496134d..c627abc8b 100644 --- a/public/js/helpers/meters.js +++ b/public/js/helpers/meters.js @@ -24,6 +24,7 @@ function sleep (ms) { // parameter should be a block element with class .meter. CSS classes .large-gap // and .arch can also be applied for increasingly large gap angle, with .arch // being a semi-circle. Any contents of parent will be replaced with the Meter. +// Apply class .lil for a smaller meter. class Meter { constructor (parent, opts) { opts = opts || {} @@ -209,7 +210,7 @@ export class VoteMeter extends Meter { opts.revoteColor = opts.revoteColor || '#ffe4a7' opts.rejectColor = opts.rejectColor || '#ed6d47' opts.dotColor = opts.dotColor || '#888' - opts.showIndicator = opts.showIndicator || true + if (opts.showIndicator === undefined) opts.showIndictor = true this.darkTheme = opts.darkTheme || { text: 'white', tray: '#999' @@ -306,7 +307,8 @@ export class ProgressMeter extends Meter { opts.meterWidth = opts.meterWidth || 14 opts.centralFontSize = opts.centralFontSize || 18 opts.successColor = opts.successColor = '#2dd8a3' - opts.showIndicator = opts.showIndicator || true + opts.dotSize = opts.dotSize || 3 + if (opts.showIndicator === undefined) opts.showIndictor = true this.darkTheme = opts.darkTheme || { tray: '#777', text: 'white' @@ -332,8 +334,8 @@ export class ProgressMeter extends Meter { var end = super.normedPolarToCartesian(this.radius + halfLen, value) ctx.lineWidth = 1.5 ctx.strokeStyle = color - super.dot(start, color, 3) - super.dot(end, color, 3) + super.dot(start, color, opts.dotSize) + super.dot(end, color, opts.dotSize) ctx.strokeStyle = theme.text super.line(start, end) } @@ -344,7 +346,7 @@ export class ProgressMeter extends Meter { var opts = this.options var theme = this.activeTheme - ctx.lineWidth = opts.meterWidth + ctx.lineWidth = opts.meterWidth * 0.95 // Prevents rough looking edge var c = this.data.progress >= this.threshold ? opts.successColor : theme.tray super.segment(0, 1, c) @@ -362,7 +364,7 @@ export class ProgressMeter extends Meter { var offset = opts.showIndicator ? super.denorm(0.05) : 0 this.ctx.fillStyle = this.activeTheme.text - this.ctx.font = `bold ${opts.centralFontSize}px sans-serif` + this.ctx.font = `500 ${opts.centralFontSize}px 'source-sans-pro-semibold', sans-serif` this.write(`${parseInt(this.data.progress * 100)}%`, makePt(this.middle.x, this.middle.y + offset), super.denorm(0.5)) } } diff --git a/public/scss/charts.scss b/public/scss/charts.scss index 93549719b..f853f128c 100644 --- a/public/scss/charts.scss +++ b/public/scss/charts.scss @@ -554,6 +554,11 @@ body.darkBG #chart .dygraph-y2label { overflow: hidden; } +.meter.lil { + width: 75px; + height: 75px; +} + .meter.large-gap { height: 110px; } @@ -567,6 +572,11 @@ body.darkBG #chart .dygraph-y2label { height: 135px; } +.meter.lil > canvas { + width: 75px; + height: 75px; +} + .market-chart-legend { position: absolute; top: 5px; @@ -611,18 +621,14 @@ body.darkBG { } .proposal-chart-legend { - left: 85%; + top: 5px; + right: 5px; + padding: 2px; + border-radius: 0; } .proposal-chart-align { - height: 23vh !important; - margin: 0 auto 0 auto; - width: 70%; -} - -.proposal-charts-align { - margin: auto; - height: inherit; + height: 23vh; } .tp-outputs-align { diff --git a/views/proposal.tmpl b/views/proposal.tmpl index 255131669..e0e0ec192 100644 --- a/views/proposal.tmpl +++ b/views/proposal.tmpl @@ -3,201 +3,183 @@ {{template "html-head" printf "Decred Proposal Charts"}} {{template "navbar" .}} + {{$metadata := .Metadata}} {{with .Data}} -
-
-
- All Proposals > +
+
+
+ {{$status := .VoteStatus.ShortDesc}} + + {{- /* INVALID AND NON-EXISTENT */ -}} + {{if or (eq $status "Invalid") (eq $status "Doesn't Exist")}} + Invalid proposal {{.Name}} + + {{- /* NOT AUTHORIZED */ -}} + {{else if eq $status "Not Authorized"}} + {{if .AbandonedDate}} + Proposal abandoned +
{{.Name}}
+ This proposal was designated abandoned {{TimeConversion .AbandonedDate}}. + {{else}} + Proposal in discussion +
{{.Name}}
+
+ {{.Username}} has not initiated voting yet. There is + an ongoing discussion + of this proposal happening on Politeia. +
+ {{end}} + + {{- /* AUTHORIZED */ -}} + {{else if eq $status "Authorized"}} + vote scheduled +
{{.Name}}
+
+ {{.Username}} has authorized voting on this proposal. + Voting begins at block {{$metadata.StartHeight}} + (about {{secondsToShortDurationString $metadata.SecondsTil}})
-
-

{{.Name}}

+ + {{- /* LIVE OR FINISHED */ -}} + {{else if or (eq $status "Started") (eq $status "Finished")}} + {{if eq $status "Started"}} +
+ voting now +
+ ~{{secondsToShortDurationString $metadata.SecondsTil}} remaining +
+
+ {{else}} + voting complete – + {{if and $metadata.IsPassing $metadata.QuorumAchieved}} + approved + {{else}} + rejected + {{end}} + {{end}} +
{{.Name}}
+
+
+
Approval
+
+ {{printf "%.1f" (f32x100 $metadata.Approval)}}% +
+
+
+ {{$metadata.Yes}} / {{$metadata.VoteCount}} voters approve + {{if $metadata.QuorumAchieved}} +
+ Quorum achieved
+ {{else}} + {{if eq $status "Started"}} + Quorum +
+ {{$metadata.VoteCount}} of {{$metadata.QuorumCount}} needed votes +
+ {{else}} +
+ Quorum not achieved
+ {{end}} + {{end}} +
-
-
-
- - - - - - - - - - - - - - - - - - {{if ne .CensoredDate 0}} - - - - - {{end}} - {{if ne .AbandonedDate 0}} - - - - - {{end}} - - - - -
Token: - - {{.TokenVal}} - -
Author: - {{.Username}} -
Published: - {{TimeConversion .PublishedDate}} -
Updated: - {{TimeConversion .Timestamp}} -
Censored: - {{TimeConversion .CensoredDate}} -
Abandoned: - {{TimeConversion .AbandonedDate}} -
Comments Count: - {{.NumComments}} -
-
-
- - - - - - - - - - - - - - - - - - {{if ne $.TimeRemaining ""}} - - - - - {{end}} -
State: - {{with .State}}{{toTitleCase .String}}{{end}} -
Status: - {{with .Status}}{{toTitleCase .String}}{{end}} -
Version: - v{{.Version}} -
End Height: - - {{if .Endheight}} - {{.Endheight}} - {{else}} - N/A - {{end}} - -
Remaining: - {{$.TimeRemaining}} -
+
+ {{if eq $status "Started"}} + {{if $metadata.QuorumAchieved}} + {{if $metadata.IsPassing}} + {{.QuorumPercentage}}% quorum has been achieved, and stakeholders are tending towards approval. + {{else}} + {{.QuorumPercentage}}% quorum has been achieved, but stakeholders are tending towards rejection. + {{end}} + {{else}}{{- /* Quroum Not Achieved */ -}} + This proposal has not achieved {{.QuorumPercentage}}% quorum. + {{end}} + {{else}} + {{if $metadata.QuorumAchieved}} + {{if $metadata.IsPassing}} + Stakeholders approved this proposal. + {{else}} + Stakeholders rejected this proposal. + {{end}} + {{else}}{{- /* Quroum Not Achieved */ -}} + This proposal was rejected for failing to achieve {{.QuorumPercentage}}% quorum. + {{end}} + {{end}}
-
- - {{if .VoteStatus}} - {{range $i, $v := .VoteResults}} - {{if eq $v.Option.OptionID "yes"}} - - - - - {{end}} - {{end}} - {{end}} - {{if .VoteStatus}} - {{range $i, $v := .VoteResults}} - {{if eq $v.Option.OptionID "no"}} - - - - - {{end}} - {{end}} - {{end}} - - - - - - - - - {{if .VoteResults}} - {{range $i, $v := .VoteResults}} - {{if eq $v.Option.OptionID "yes"}} - - - - - {{end}} - {{end}} - {{end}} -
Yes Votes: - {{$v.VotesReceived}} -
No Votes: - {{$v.VotesReceived}} -
Vote Status: - - {{if .VoteStatus}} - {{with .VoteStatus}} - {{.ShortDesc}} - - {{end}} - {{else}} - N/A - {{end}} - -
Quorum: - - {{if and .QuorumPercentage (and .NumOfEligibleVotes .TotalVotes)}} - {{$voted := (percentage .TotalVotes .NumOfEligibleVotes)}} - {{.TotalVotes}} of {{.NumOfEligibleVotes}}, {{printf "%.0f" $voted}}% voted, {{.QuorumPercentage}}% needed - {{else}} - N/A - {{end}} - -
Total: - - {{printf "%.2f" (percentage $v.VotesReceived $.Data.TotalVotes)}}% yes, {{if $.Data.PassPercentage}}{{$.Data.PassPercentage}}%{{end}} needed - -
-
-
-
+ {{end}} + +
{{/* END COLUMN 1 */ -}} + + {{- /* ADDITIONAL DATA */ -}} +
+ + + + + + + + + {{if $metadata.VoteCount}} + + + + + + + + + + + + + {{end}} + + + + + + + + + + + + + + + + + + +
Author:{{.Username}}Status:{{.State.String}} / {{.Status.String}}
Yes:{{$metadata.Yes}} votes ({{printf "%.1f" (f32x100 $metadata.Approval)}}%)No:{{$metadata.No}} votes ({{printf "%.1f" (f32x100 $metadata.Rejection)}}%)
Eligible:{{.NumOfEligibleVotes}} ticketsLive Range:{{$metadata.StartHeight}} – {{.Endheight}}
Discussion:{{.NumComments}} commentsUpdated:{{TimeConversion .Timestamp}}
Version:v{{.Version}}Published:{{TimeConversion .PublishedDate}}
Token:{{.TokenVal}}
+ + +
{{/* END COLUMN 2 */ -}} +
{{/* END ROW */ -}} + + {{- /* CHARTS */ -}} {{if gt (len .VoteResults) 1}}
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
{{else}} @@ -207,7 +189,8 @@ {{end}} - {{end}} +
{{/* END CONTAINER */ -}} + {{end}}{{/* END WITH .DATA */}} {{template "footer" . }} From a129f5108bb8e4f33b8501da74d1c35df888c299 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 1 Jul 2019 16:48:45 -0500 Subject: [PATCH 2/3] tighten up layout and verbiage. use smaller meter. --- public/js/controllers/proposal_controller.js | 14 ++-- public/js/helpers/meters.js | 80 ++++++++++++++---- public/scss/charts.scss | 16 ++-- views/proposal.tmpl | 88 ++++++++++++-------- 4 files changed, 130 insertions(+), 68 deletions(-) diff --git a/public/js/controllers/proposal_controller.js b/public/js/controllers/proposal_controller.js index 0d623186a..9fd0ca463 100644 --- a/public/js/controllers/proposal_controller.js +++ b/public/js/controllers/proposal_controller.js @@ -1,5 +1,5 @@ import { Controller } from 'stimulus' -import { ProgressMeter } from '../helpers/meters.js' +import { MiniMeter } from '../helpers/meters.js' import { darkEnabled } from '../services/theme_service' import globalEventBus from '../services/event_bus_service' import { getDefault } from '../helpers/module_helper' @@ -117,16 +117,16 @@ export default class extends Controller { } async connect () { - console.log(this.approvalMeterTarget) if (this.hasApprovalMeterTarget) { + let d = this.approvalMeterTarget.dataset var opts = { darkMode: darkEnabled(), - showIndicator: false, - radius: 0.5, - centralFontSize: 30, - meterWidth: 15 + segments: [ + { end: d.threshold, color: '#ed6d47' }, + { end: 1, color: '#2dd8a3' } + ] } - this.approvalMeter = new ProgressMeter(this.approvalMeterTarget, opts) + this.approvalMeter = new MiniMeter(this.approvalMeterTarget, opts) } let response = await axios.get('/api/proposal/' + this.tokenTarget.dataset.hash) diff --git a/public/js/helpers/meters.js b/public/js/helpers/meters.js index c627abc8b..b00f2db4f 100644 --- a/public/js/helpers/meters.js +++ b/public/js/helpers/meters.js @@ -162,6 +162,21 @@ class Meter { this.ctx.fillText(text, pt.x, pt.y, maxWidth) } + drawIndicator (value, color) { + var ctx = this.ctx + var opts = this.options + var theme = this.activeTheme + var halfLen = this.norm(opts.meterWidth) * 0.5 + var start = super.normedPolarToCartesian(this.radius - halfLen, value) + var end = super.normedPolarToCartesian(this.radius + halfLen, value) + ctx.lineWidth = 1.5 + ctx.strokeStyle = color + super.dot(start, color, opts.dotSize) + super.dot(end, color, opts.dotSize) + ctx.strokeStyle = theme.text + super.line(start, end) + } + async animate (key, target) { // key is a string referencing any property of Meter.data. var opts = this.options @@ -325,21 +340,6 @@ export class ProgressMeter extends Meter { this.animate('progress', progress) } - drawIndicator (value, color) { - var ctx = this.ctx - var opts = this.options - var theme = this.activeTheme - var halfLen = this.norm(opts.meterWidth) * 0.5 - var start = super.normedPolarToCartesian(this.radius - halfLen, value) - var end = super.normedPolarToCartesian(this.radius + halfLen, value) - ctx.lineWidth = 1.5 - ctx.strokeStyle = color - super.dot(start, color, opts.dotSize) - super.dot(end, color, opts.dotSize) - ctx.strokeStyle = theme.text - super.line(start, end) - } - draw () { super.clear() var ctx = this.ctx @@ -350,12 +350,12 @@ export class ProgressMeter extends Meter { var c = this.data.progress >= this.threshold ? opts.successColor : theme.tray super.segment(0, 1, c) - this.drawIndicator(this.threshold, c) + super.drawIndicator(this.threshold, c) ctx.lineWidth = opts.meterWidth super.segment(0, this.data.progress, opts.meterColor) - this.drawIndicator(this.data.progress, theme.text) + super.drawIndicator(this.data.progress, theme.text) if (opts.showIndicator && this.data.progress >= this.threshold) { ctx.fillStyle = opts.successColor @@ -368,3 +368,49 @@ export class ProgressMeter extends Meter { this.write(`${parseInt(this.data.progress * 100)}%`, makePt(this.middle.x, this.middle.y + offset), super.denorm(0.5)) } } + +// Mini meter is a semi-circular meter with a needle. The segment definitions +// must be passed as an array of objects with the structure +// [{end: float, color: string}, {end: float, color: string}, ...], where end is +// the end of the segments range on the scale [0, 1]. The first range is assumed +// to start at 0, and each subsequent segment will start at the previous +// segment's end. The MiniMeter is designed to work with the .arch.lil CSS +// classes, but not limited to that particular class. +export class MiniMeter extends Meter { + constructor (parent, opts) { + super(parent, opts) + this.buttCap() + opts = this.options + this.radius = opts.radius || 0.475 + this.darkTheme = opts.darkTheme || { text: 'white' } + this.lightTheme = opts.lightTheme || { text: '#333333' } + this.activeTheme = opts.darkMode ? this.darkTheme : this.lightTheme + opts.meterWidth = opts.meterWidth || 18 + this.value = parseFloat(parent.dataset.value) + this.draw() + } + + draw () { + super.clear() + var ctx = this.ctx + var opts = this.options + ctx.lineWidth = opts.meterWidth + var textColor = this.activeTheme.text + + // Draw the segments. + var start = 0 + opts.segments.forEach(segment => { + super.segment(start, segment.end, segment.color) + start = segment.end + }) + + // Draw the needle + var tipLen = this.norm(opts.meterWidth) * 0.75 + var center = super.normedPolarToCartesian(0, 0) + var end = super.normedPolarToCartesian(this.radius + tipLen, this.value) + super.dot(center, textColor, 7) + ctx.strokeStyle = textColor + ctx.lineWidth = 5 + super.line(center, end) + } +} diff --git a/public/scss/charts.scss b/public/scss/charts.scss index f853f128c..f4e118a3c 100644 --- a/public/scss/charts.scss +++ b/public/scss/charts.scss @@ -554,27 +554,27 @@ body.darkBG #chart .dygraph-y2label { overflow: hidden; } +.lilbox { + height: 45px; +} + .meter.lil { - width: 75px; - height: 75px; + width: 80px; + height: 45px; } .meter.large-gap { height: 110px; } -.meter.arch { - height: 90px; -} - .meter > canvas { width: 135px; height: 135px; } .meter.lil > canvas { - width: 75px; - height: 75px; + width: 80px; + height: 80px; } .market-chart-legend { diff --git a/views/proposal.tmpl b/views/proposal.tmpl index e0e0ec192..b9765c5eb 100644 --- a/views/proposal.tmpl +++ b/views/proposal.tmpl @@ -7,23 +7,28 @@ {{with .Data}}
-
+
{{$status := .VoteStatus.ShortDesc}} {{- /* INVALID AND NON-EXISTENT */ -}} {{if or (eq $status "Invalid") (eq $status "Doesn't Exist")}} - Invalid proposal {{.Name}} - + {{if .AbandonedDate}} + {{block "abandoned" .}} +
{{.Name}}
+
Proposal abandoned
+
This proposal was designated abandoned {{TimeConversion .AbandonedDate}}.
+ {{end}} + {{else}} + Invalid proposal: {{.Name}} + {{end}} {{- /* NOT AUTHORIZED */ -}} {{else if eq $status "Not Authorized"}} {{if .AbandonedDate}} - Proposal abandoned -
{{.Name}}
- This proposal was designated abandoned {{TimeConversion .AbandonedDate}}. + {{template "abandoned" .}} {{else}} - Proposal in discussion
{{.Name}}
-
+ Proposal in discussion +
{{.Username}} has not initiated voting yet. There is an ongoing discussion of this proposal happening on Politeia. @@ -32,9 +37,9 @@ {{- /* AUTHORIZED */ -}} {{else if eq $status "Authorized"}} - vote scheduled
{{.Name}}
-
+ vote scheduled +
{{.Username}} has authorized voting on this proposal. Voting begins at block {{$metadata.StartHeight}} (about {{secondsToShortDurationString $metadata.SecondsTil}}) @@ -42,53 +47,57 @@ {{- /* LIVE OR FINISHED */ -}} {{else if or (eq $status "Started") (eq $status "Finished")}} +
{{.Name}}
{{if eq $status "Started"}}
- voting now + voting now
~{{secondsToShortDurationString $metadata.SecondsTil}} remaining
{{else}} - voting complete – + voting complete – {{if and $metadata.IsPassing $metadata.QuorumAchieved}} - approved + accepted {{else}} rejected {{end}} {{end}} -
{{.Name}}
-
-
-
Approval
+ +
+
+
{{printf "%.1f" (f32x100 $metadata.Approval)}}%
-
- {{$metadata.Yes}} / {{$metadata.VoteCount}} voters approve +
+ + {{printf "%.1f" (f32x100 $metadata.Approval)}}% + approval of {{intComma $metadata.VoteCount}} votes + {{if $metadata.QuorumAchieved}} -
- Quorum achieved
+
+ Quorum achieved
{{else}} {{if eq $status "Started"}} Quorum -
- {{$metadata.VoteCount}} of {{$metadata.QuorumCount}} needed votes +
+ {{$metadata.VoteCount}} of {{$metadata.QuorumCount}} needed votes
{{else}} -
- Quorum not achieved
+
+ Quorum not achieved
{{end}} {{end}}
-
+
{{if eq $status "Started"}} {{if $metadata.QuorumAchieved}} {{if $metadata.IsPassing}} @@ -102,16 +111,17 @@ {{else}} {{if $metadata.QuorumAchieved}} {{if $metadata.IsPassing}} - Stakeholders approved this proposal. + Voting on this proposal has completed. Stakeholders voted to accept this proposal. {{else}} - Stakeholders rejected this proposal. + Voting on this proposal has completed. Stakeholders voted to reject this proposal. {{end}} {{else}}{{- /* Quroum Not Achieved */ -}} - This proposal was rejected for failing to achieve {{.QuorumPercentage}}% quorum. + This proposal was rejected for failing to achieve quorum. + Fewer than {{.QuorumPercentage}}% of eligible ticket holders voted on this proposal. {{end}} {{end}}
- {{end}} + {{end}}{{/* END LIVE OR FINISHED */ -}}
{{/* END COLUMN 1 */ -}} @@ -135,8 +145,16 @@ Eligible: {{.NumOfEligibleVotes}} tickets - Live Range: - {{$metadata.StartHeight}} – {{.Endheight}} + Voting Period: + {{$metadata.StartHeight}} + – + {{if eq $status "Finished"}} + {{.Endheight}} + {{else}} + {{.Endheight}} + {{end}} + {{end}} @@ -155,11 +173,9 @@ Token: {{.TokenVal}} - -
{{/* END COLUMN 2 */ -}}
{{/* END ROW */ -}} From 17d77ac0864e6ab657837e2092e2c7f46b215760 Mon Sep 17 00:00:00 2001 From: Brian Date: Wed, 3 Jul 2019 12:51:44 -0500 Subject: [PATCH 3/3] more layout tweaks. remove description --- public/fonts/icomoon.svg | 2 + public/fonts/icomoon.ttf | Bin 5780 -> 6144 bytes public/fonts/icomoon.woff | Bin 5856 -> 6220 bytes public/js/controllers/proposal_controller.js | 10 +-- public/scss/charts.scss | 21 +++-- public/scss/icons.scss | 8 ++ views/proposal.tmpl | 82 ++++++++----------- 7 files changed, 65 insertions(+), 58 deletions(-) diff --git a/public/fonts/icomoon.svg b/public/fonts/icomoon.svg index ba6244078..109eb0735 100644 --- a/public/fonts/icomoon.svg +++ b/public/fonts/icomoon.svg @@ -25,4 +25,6 @@ + + \ No newline at end of file diff --git a/public/fonts/icomoon.ttf b/public/fonts/icomoon.ttf index 7dfe6251233c7a0dd89c8bdd933ca726a3ab4879..94a3d0ee9109608e86b24bac91411b1536300775 100644 GIT binary patch delta 737 zcmY+CO-vI}5Xa|rx7}{AAK16MfCWpb{Q|nB?6y=1p#~61sSzR?`H+f8jTB1Zfbl@9 zhC~`;OuZNm9Mr%`4?s)|2QDUh^QZ?C4t6#$f6NS zC+9QJ2Rq0u$d0LGdi+Tbzl(ecH?WwQo}I&u;wOF9^1v|LkG5Bdt(YS9{1syQn>^7uFke4@?U>fP-E(&-8*9utiTr zNidT-r`x0bJptlzt4gGeM8eG>sfI|RKngwT-BX+vEOk z2{z`k8?DCX%MR)nS`{_1Ac>-sP?c5-D3gs_LR*G;`H;uKqxvX&@e-an#~tX+MZF1)K_w zQXh_l@gSVQ6CsQQ&tW$n{%S7)wRT7N>BcK-k$Pp@447mG(|gj5teXpGAPV7|S0kDhdLaW2cPaUXaG b-^0)GOIWTw;R^T)QLPnp+V!u0r_ae>W~Q1~ delta 350 zcmZoLn4(+Hz{tSBz|GLWz|3IaAFOZ0e^>f2P-G7fCnV=47VO#NvXp^=Q3i+w(i4jd zfZ_rS3`{yenj<}@GOew4$}b?_gn>bDOGav9is-guFBlkvftbN811P}Z&eF@kAff=| zt7PPsRCu{C#{>B;Kz>9{ezIetk%itopuilUfL?B5MFB$q!%HCl43MvomzbM6(~I*r zkpBkgfVP7C;u4@kfk3PTB+tOiym{h*<&1)pGZ?j5ABk-ilbSq(G2T=VD9Ojb@KO(g zK`wq72f++LxtBj6m_dkv@$+vW{tHwuyjh2-mv^(IpgD_-{@V$E2-^>K4UPhiSsX_=ZUL2mLQ`zBo!B8p0F=5|ZU6uP diff --git a/public/fonts/icomoon.woff b/public/fonts/icomoon.woff index ef0d560a3ed59a104201ee920a00e42f8a355699..8bb10c8e1f829d2e77dd9d00cb6af27a1db1570c 100644 GIT binary patch delta 753 zcmY+CO-vJE5QgXL?snT?OR?YX7E-X#+CN}hO1A|i1PoLqOI3(yo?-h>3|N960LD0~Zb^CdNc1fU~WdxXH7#^Ugcp?o9S`{Z^3P zIv0xp0laJ~loTR7p|uO!qp8F+07)VijW_v@jq$`RYPxx}8UA1ypSXP;wG4ojYV>AX zUnIv8*8qg4=!s~E`Ma-@D5AE7xl6-vnthZ^&EZ+XSIkE=dfqWB6H}w;i;L$`2x**r z9sQL`%;Ss2`Pcjb~lPz3HjhIh>^KpO3*OhS|&=A*;li%adAe zN8AwYxg9dBSg~dX*p-XGQH(=H0BgHHiU0-jZ4cQ;zpy^9duUqF0UUO-i%d6o0b4jK zN`jdU?f5WcYwp`_WV}b)YrFmkRh25>2%ENxsmVg?|&W&u?J+4WPa-7@h{5{ z(y-20npykQ`_-A@(w6T~!s6-yKAl>*`Zwk-(oN*cL1hK7G32(`7J{pIl~#PR>m%U|?X(0Ac|U-m}SNX?kKY zP>iVpsFVYW1=4dW(|}?P3=D!>K)9`T%CC&n#1sYw5fh*qGY}TtcI-t4P!K4V1LUiK zFo!!!Z$@qj&@7P&Kz;-Wd$};j=j0~?)%l6M01D`Vu#tt{yxhbJpv9sRK)wPP2Qa+M zOUz9Lidg_1*9O8fy*Pgtn1H+BO2kT+<' + series.labelHTML + ' ' - }).join('
') + series.color + ';">' + series.labelHTML + ' ' + }).join('') } else { - html = this.getLabels()[0] + ': ' + data.xHTML + ' UTC
' + html = this.getLabels()[0] + ': ' + data.xHTML + ' UTC   ' data.series.forEach(function (series, index) { if (!series.isVisible) return @@ -79,12 +79,12 @@ function legendFormatter (data) { } else if (index === 0) { html = '' } else { - html += '
' + // html += '
' } var labeledData = '' + series.labelHTML + ' : ' + Math.abs(series.y) - html += series.dashHTML + ' ' + labeledData + '' + symbol + html += series.dashHTML + ' ' + labeledData + '' + symbol + '  ' }) } dompurify.sanitize(html) diff --git a/public/scss/charts.scss b/public/scss/charts.scss index f4e118a3c..903447d1c 100644 --- a/public/scss/charts.scss +++ b/public/scss/charts.scss @@ -596,6 +596,17 @@ body.darkBG #chart .dygraph-y2label { border: 1px solid #7774; } +.proposal-chart-legend { + display: none; + position: absolute; + top: 0; + right: 0; + padding: 0 2px; + font-size: 14px; + border-radius: 0; + background-color: #fffa; +} + body.darkBG { .address_chart_wrap { background-color: #333; @@ -615,16 +626,14 @@ body.darkBG { background-color: #34a7; } - .market-chart-title { + .market-chart-title, + .proposal-chart-legend { background-color: #333a; } } -.proposal-chart-legend { - top: 5px; - right: 5px; - padding: 2px; - border-radius: 0; +.proposal-charts:hover .proposal-chart-legend { + display: block; } .proposal-chart-align { diff --git a/public/scss/icons.scss b/public/scss/icons.scss index e7faa1e21..35fb770c5 100644 --- a/public/scss/icons.scss +++ b/public/scss/icons.scss @@ -97,6 +97,14 @@ content: "\e910"; } +.dcricon-discussion::before { + content: "\e912"; +} + +.dcricon-back::before { + content: "\e913"; +} + .exchange-logo { vertical-align: middle; display: inline-block; diff --git a/views/proposal.tmpl b/views/proposal.tmpl index b9765c5eb..7ddf90c40 100644 --- a/views/proposal.tmpl +++ b/views/proposal.tmpl @@ -7,7 +7,7 @@ {{with .Data}}
-
+
{{$status := .VoteStatus.ShortDesc}} {{- /* INVALID AND NON-EXISTENT */ -}} @@ -38,7 +38,7 @@ {{- /* AUTHORIZED */ -}} {{else if eq $status "Authorized"}}
{{.Name}}
- vote scheduled + Vote scheduled
{{.Username}} has authorized voting on this proposal. Voting begins at block {{$metadata.StartHeight}} @@ -50,21 +50,23 @@
{{.Name}}
{{if eq $status "Started"}}
- voting now + Voting now
~{{secondsToShortDurationString $metadata.SecondsTil}} remaining
{{else}} - voting complete – - {{if and $metadata.IsPassing $metadata.QuorumAchieved}} - accepted - {{else}} - rejected - {{end}} +
+ Voting complete – + {{if and $metadata.IsPassing $metadata.QuorumAchieved}} + accepted + {{else}} + rejected + {{end}} +
{{end}} -
+
{{if $metadata.QuorumAchieved}} -
+
Quorum achieved
{{else}} {{if eq $status "Started"}} Quorum -
+
{{$metadata.VoteCount}} of {{$metadata.QuorumCount}} needed votes
{{else}} -
+
Quorum not achieved
{{end}} {{end}}
-
- {{if eq $status "Started"}} - {{if $metadata.QuorumAchieved}} - {{if $metadata.IsPassing}} - {{.QuorumPercentage}}% quorum has been achieved, and stakeholders are tending towards approval. - {{else}} - {{.QuorumPercentage}}% quorum has been achieved, but stakeholders are tending towards rejection. - {{end}} - {{else}}{{- /* Quroum Not Achieved */ -}} - This proposal has not achieved {{.QuorumPercentage}}% quorum. - {{end}} - {{else}} - {{if $metadata.QuorumAchieved}} - {{if $metadata.IsPassing}} - Voting on this proposal has completed. Stakeholders voted to accept this proposal. - {{else}} - Voting on this proposal has completed. Stakeholders voted to reject this proposal. - {{end}} - {{else}}{{- /* Quroum Not Achieved */ -}} - This proposal was rejected for failing to achieve quorum. - Fewer than {{.QuorumPercentage}}% of eligible ticket holders voted on this proposal. - {{end}} - {{end}} -
{{end}}{{/* END LIVE OR FINISHED */ -}} - +
{{/* END COLUMN 1 */ -}} {{- /* ADDITIONAL DATA */ -}} -
- +
+
@@ -184,18 +171,19 @@
-
-
+
+
-
-
+
+
-
-
-
+
+
+
{{else}}
Author: