diff --git a/customize.dist/src/less2/include/toolbar-history.less b/customize.dist/src/less2/include/toolbar-history.less index 7538fb2bc..68c79649d 100644 --- a/customize.dist/src/less2/include/toolbar-history.less +++ b/customize.dist/src/less2/include/toolbar-history.less @@ -5,15 +5,148 @@ } & { .cp-toolbar-history { + + @history_lineBg: #FFFFFF; + @history_userBg1: #DDD; + @history_userBg2: #BBB; + @pos-color: @cryptpad_text_col; + @fill-width: 40px; + display: none; - text-align: center; width: 100%; - padding: 10px 0; + padding: 10px 0 0; align-items: center; justify-content: center; + color: @cryptpad_text_col; * { font: @colortheme_app-font; } + + .cp-toolbar-history-timeline { + display: flex; + flex-flow: column; + flex: 1; + margin-left: 10px; + margin-right: @fill-width; + } + .cp-toolbar-history-actions { + display: flex; + justify-content: space-between; + align-items: center; + height: 39px; + align-self: baseline; + margin-right: 5px; + .cp-history-actions-first { + margin-right: @fill-width; + } + button { + margin: 0 5px; + border: 1px solid @cryptpad_text_col; + text-transform: uppercase; + .fa:not(:last-child) { + margin-right: 5px; + } + } + } + + .cp-history-timeline-line { + display: flex; + .cp-history-timeline-legend { + display: flex; + flex-flow: column; + justify-content: space-around; + align-items: center; + margin-right: 4px; + } + .cp-history-timeline-loadmore { + width: 20px; + display: flex; + align-items: center; + justify-content: center; + button { + padding: 0; + width: 100%; + height: 100%; + background: @history_lineBg; + margin-right: 1px; + .fa-refresh { + font-size: 13px; + } + } + } + .cp-history-timeline-container { + flex: 1; + position: relative; + background-color: @history_lineBg; + height: 39px; + } + .cp-history-timeline-bar { + display: flex; + flex-flow: column; + padding: 1px; + & > span { + height: 18px; + display: flex; + } + .cp-history-timeline-users { + margin-bottom: 1px; + .cp-history-bar-el { + background-color: @history_userBg1; + &:nth-child(2n) { + background-color: @history_userBg2; + } + } + } + .cp-history-timeline-user { + .cp-history-bar-el { + background-color: @history_userBg2; + &:nth-child(2n) { + background-color: @history_userBg1; + } + } + } + } + } + .cp-history-timeline-actions { + display: flex; + justify-content: space-between; + align-items: center; + margin-left: 40px; + button { + width: 50px; + .fa:first-child:not(:last-child) { + margin-right: 5px; + } + } + .cp-history-timeline-next { + button:last-child { + margin-right: 0; + } + } + .cp-history-timeline-prev { + button:first-child { + margin-left: 0; + } + } + } + + .cp-history-timeline-pos { + width: 2px; + height: 37px; + top: 1px; + background: @pos-color; + position: absolute; + &:after { + content: ''; + border: 9px solid transparent; + border-top-color: @pos-color; + margin-left: -8px; + position: absolute; + top: -8px; + } + } + +/* .cp-history-filler { flex: 1; } @@ -91,6 +224,7 @@ .fa-spinner { font-size: 66px; } +*/ } } diff --git a/www/common/sframe-common-history.js b/www/common/sframe-common-history.js index 4c463c24c..016256443 100644 --- a/www/common/sframe-common-history.js +++ b/www/common/sframe-common-history.js @@ -56,6 +56,76 @@ define([ }); }; + var realtime; + var states = []; + var c = 0;//states.length - 1; + + var getIndex = function (i) { + return states.length - 1 + i; + }; + var getRank = function (idx) { + return idx - states.length + 1; + }; + // Get the author or group of author linked to a state + var getAuthor = function (idx, semantic) { + if (semantic === 1 || !config.extractMetadata) { + return states[idx].author; + } + try { + var val = JSON.parse(states[idx].getContent().doc); + var md = config.extractMetadata(val); + var users = Object.keys(md.users).sort(); + return users.join(); + } catch (e) { + console.error(e); + return states[idx].author; + } + }; + + // Refresh the timeline UI with the block states + var bar = h('span.cp-history-timeline-bar'); + var refreshBar = function () { + var $bar = $(bar).html(''); + var users = { + list: [], + author: '', + el: undefined, + i: 0 + }; + var user = { + list: [], + author: '', + el: undefined, + i: 0 + }; + + var max = states.length - 1; + var check = function (obj, author, i) { + if (obj.author !== author) { + obj.author = author; + if (obj.el) { + $(obj.el).css('width', (100*(i - obj.i)/max)+'%'); + } + obj.el = h('span.cp-history-bar-el'); + obj.list.push(obj.el); + obj.i = i; + } + }; + + for (var i = 1; i < states.length; i++) { + check(user, getAuthor(i, 1), i); + check(users, getAuthor(i, 2), i); + } + $(user.el).css('width', (100*(max + 1 - user.i)/max)+'%'); + $(users.el).css('width', (100*(max + 1 - users.i)/max)+'%'); + + $bar.append([ + h('span.cp-history-timeline-users', users.list), + h('span.cp-history-timeline-user', user.list), + ]); + + }; + var allMessages = []; var lastKnownHash; var isComplete = false; @@ -111,11 +181,6 @@ define([ config.setHistory(true); - var realtime; - - var states = []; - var c = 0;//states.length - 1; - var $hist = $toolbar.find('.cp-toolbar-history'); var $bottom = $toolbar.find('.cp-toolbar-bottom'); var $cke = $toolbar.find('.cke_toolbox_main'); @@ -126,13 +191,11 @@ define([ UI.spinner($hist).get().show(); - var onUpdate; - var update = function (newRt) { realtime = newRt; if (!realtime) { return []; } states = getStates(realtime); - if (typeof onUpdate === "function") { onUpdate(); } + refreshBar(); return states; }; @@ -143,8 +206,8 @@ define([ var loadMore = function (cb) { if (loading) { return; } loading = true; - $loadMore.removeClass('fa fa-ellipsis-h') - .append($('', {'class': 'fa fa-refresh fa-spin fa-3x fa-fw'})); + $loadMore.find('.fa-ellipsis-h').hide(); + $loadMore.find('.fa-refresh').show(); loadMoreHistory(config, common, function (err, newRt, isFull) { if (err === 'EFULL') { @@ -156,7 +219,8 @@ define([ loading = false; if (err) { return void console.error(err); } update(newRt); - $loadMore.addClass('fa fa-ellipsis-h').html(''); + $loadMore.find('.fa-ellipsis-h').show(); + $loadMore.find('.fa-refresh').hide(); get(c); if (isFull) { $loadMore.off('click').hide(); @@ -166,27 +230,6 @@ define([ }); }; - var getIndex = function (i) { - return states.length - 1 + i; - }; - var getRank = function (idx) { - return idx - states.length + 1; - }; - var getAuthor = function (idx, semantic) { - if (semantic === 1 || !config.extractMetadata) { - return states[idx].author; - } - try { - var val = JSON.parse(states[idx].getContent().doc); - var md = config.extractMetadata(val); - var users = Object.keys(md.users).sort(); - return users.join(); - } catch (e) { - console.error(e); - return states[idx].author; - } - }; - // semantic === 1 : group by user // semantic === 2 : group by "group of users" get = function (i, blockOnly, semantic) { @@ -199,15 +242,16 @@ define([ } var idx = getIndex(i); - if (semantic) { + if (semantic && i !== c) { // If semantic is truc, jump to the next patch from a different netflux ID var author = getAuthor(idx, semantic); - for (var j = idx; (j > 1 && j < (states.length - 1)); ((i > c) ? j++ : j--)) { - idx = j; - i = getRank(idx); + var forward = i > c; + for (var j = idx; (j > 0 && j < states.length ); (forward ? j++ : j--)) { if (author !== getAuthor(j, semantic)) { break; } + idx = j; + i = getRank(idx); } } @@ -215,21 +259,17 @@ define([ var val = states[idx].getContent().doc; c = i; - if (typeof onUpdate === "function") { onUpdate(); } - $hist.find('.cp-toolbar-history-next, .cp-toolbar-history-previous, ' + - '.cp-toolbar-history-fast-next, .cp-toolbar-history-fast-previous') - .css('visibility', ''); + $hist.find('.cp-toolbar-history-next, .cp-toolbar-history-previous') + .prop('disabled', ''); if (c === -(states.length-1)) { - $hist.find('.cp-toolbar-history-previous').css('visibility', 'hidden'); - $hist.find('.cp-toolbar-history-fast-previous').css('visibility', 'hidden'); + $hist.find('.cp-toolbar-history-previous').prop('disabled', 'disabled'); } if (c === 0) { - $hist.find('.cp-toolbar-history-next').css('visibility', 'hidden'); - $hist.find('.cp-toolbar-history-fast-next').css('visibility', 'hidden'); + $hist.find('.cp-toolbar-history-next').prop('disabled', 'disabled'); } - var $pos = $hist.find('.cp-toolbar-history-pos'); - var p = 100 * (1 - (-c / (states.length-2))); - $pos.css('margin-left', p+'%'); + var $pos = $hist.find('.cp-history-timeline-pos'); + var p = 100 * (1 - (-(c - 1) / (states.length-1))); + $pos.css('left', 'calc('+p+'% - 2px)'); // Display the version when the full history is loaded // Note: the first version is always empty and probably can't be displayed, so @@ -264,13 +304,114 @@ define([ var display = function () { $hist.html(''); + var fastPrev = h('button.cp-toolbar-history-previous', { title: Messages.history_prev }, [ + h('i.fa.fa-step-backward'), + h('i.fa.fa-users') + ]); + var userPrev = h('button.cp-toolbar-history-previous', { title: Messages.history_prev }, [ + h('i.fa.fa-step-backward'), + h('i.fa.fa-user') + ]); + var prev = h('button.cp-toolbar-history-previous', { title: Messages.history_prev }, [ + h('i.fa.fa-step-backward') + ]); + var fastNext = h('button.cp-toolbar-history-next', { title: Messages.history_next }, [ + h('i.fa.fa-users'), + h('i.fa.fa-step-forward'), + ]); + var userNext = h('button.cp-toolbar-history-next', { title: Messages.history_next }, [ + h('i.fa.fa-user'), + h('i.fa.fa-step-forward'), + ]); + var next = h('button.cp-toolbar-history-next', { title: Messages.history_next }, [ + h('i.fa.fa-step-forward') + ]); + var $fastPrev = $(fastPrev); + var $userPrev = $(userPrev); + var $prev = $(prev); + var $fastNext = $(fastNext); + var $userNext = $(userNext); + var $next = $(next); + + var _loadMore = h('button.cp-toolbar-history-loadmore', { title: Messages.history_loadMore }, [ + h('i.fa.fa-ellipsis-h'), + h('i.fa.fa-refresh.fa-spin.fa-3x.fa-fw', { style: 'display: none;' }) + ]); + + var pos = h('span.cp-history-timeline-pos'); + var time = h('div.cp-history-timeline-time'); + $time = $(time); + $version = $(); // XXX + var timeline = h('div.cp-toolbar-history-timeline', [ + h('div.cp-history-timeline-line', [ + h('span.cp-history-timeline-legend', [ + h('i.fa.fa-users'), + h('i.fa.fa-user') + ]), + h('span.cp-history-timeline-loadmore', _loadMore), + h('span.cp-history-timeline-container', [ + pos, + bar + ]) + ]), + h('div.cp-history-timeline-actions', [ + h('span.cp-history-timeline-prev', [ + fastPrev, + userPrev, + prev, + ]), + time, + h('span.cp-history-timeline-next', [ + next, + userNext, + fastNext + ]) + ]) + ]); + + Messages.history_restore = "Restore";// XXX + Messages.history_close = "Close";// XXX + var snapshot = h('button', { + title: Messages.snapshots_button, + disabled: History.readOnly ? 'disabled' : undefined + }, [ + h('i.fa.fa-camera') + ]); + var share = h('button', { title: Messages.shareButton }, [ + h('i.fa.fa-shhare-alt'), + h('span', Messages.shareButton) + ]); + var restore = h('button', { + title: Messages.history_restoreTitle, + disabled: History.readOnly ? 'disabled' : undefined + }, [ + h('i.fa.fa-check'), + h('span', Messages.history_restore) + ]); + var close = h('button', { title: Messages.history_closeTitle }, [ + h('i.fa.fa-times'), + h('span', Messages.history_close) + ]); + var actions = h('div.cp-toolbar-history-actions', [ + h('span.cp-history-actions-first', [ + snapshot, + share + ]), + h('span.cp-history-actions-last', [ + restore, + close + ]) + ]); + + $hist.append([timeline, actions]); + + /* var $rev = $('