From 27823d3e06f56cc0a14f3a5d8c4acc1d490b1141 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 17 Aug 2018 15:31:15 +0100 Subject: [PATCH 1/5] Show history entries for the last 24 hours for free users. --- .../ide/history/HistoryV2Manager.coffee | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/services/web/public/coffee/ide/history/HistoryV2Manager.coffee b/services/web/public/coffee/ide/history/HistoryV2Manager.coffee index 3f231c046b..004221674f 100644 --- a/services/web/public/coffee/ide/history/HistoryV2Manager.coffee +++ b/services/web/public/coffee/ide/history/HistoryV2Manager.coffee @@ -82,6 +82,8 @@ define [ viewMode: null nextBeforeTimestamp: null atEnd: false + userHasFullFeature: @$scope.project?.features?.versioning or false + freeHistoryLimitHit: false selection: { label: null updates: [] @@ -232,9 +234,11 @@ define [ @$scope.history.labels = @_sortLabelsByVersionAndDate response.labels.data @_loadUpdates(updatesData.updates) @$scope.history.nextBeforeTimestamp = updatesData.nextBeforeTimestamp - if !updatesData.nextBeforeTimestamp? + if !updatesData.nextBeforeTimestamp? or @$scope.history.freeHistoryLimitHit @$scope.history.atEnd = true @$scope.history.loading = false + if @$scope.history.updates.length == 0 + @$scope.history.loadingFileTree = false .catch (error) => { status, statusText } = error @$scope.history.error = { status, statusText } @@ -387,23 +391,34 @@ define [ _loadUpdates: (updates = []) -> previousUpdate = @$scope.history.updates[@$scope.history.updates.length - 1] - - for update in updates or [] + dateTimeNow = new Date() + timestamp24hoursAgo = dateTimeNow.setDate(dateTimeNow.getDate() - 1) + cutOffIndex = null + + for update, i in updates or [] for user in update.meta.users or [] if user? user.hue = ColorManager.getHueForUserId(user.id) if !previousUpdate? or !moment(previousUpdate.meta.end_ts).isSame(update.meta.end_ts, "day") update.meta.first_in_day = true - + update.selectedFrom = false update.selectedTo = false update.inSelection = false previousUpdate = update + if !@$scope.history.userHasFullFeature and update.meta.end_ts < timestamp24hoursAgo + cutOffIndex = i + @$scope.history.freeHistoryLimitHit = true + break + firstLoad = @$scope.history.updates.length == 0 + if !@$scope.history.userHasFullFeature and cutOffIndex? + updates = updates.slice 0, cutOffIndex + @$scope.history.updates = @$scope.history.updates.concat(updates) From fc424aee9e3a2bc8cf0126db3684845bf14f50e6 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 17 Aug 2018 15:31:59 +0100 Subject: [PATCH 2/5] Adapt styles to avoid layout breaking/showing options that do not make sense when no history entries are loaded. --- services/web/app/views/project/editor/history/toolbarV2.pug | 6 +++--- services/web/public/stylesheets/app/editor/history-v2.less | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/services/web/app/views/project/editor/history/toolbarV2.pug b/services/web/app/views/project/editor/history/toolbarV2.pug index a683facba4..2acb93854b 100644 --- a/services/web/app/views/project/editor/history/toolbarV2.pug +++ b/services/web/app/views/project/editor/history/toolbarV2.pug @@ -6,7 +6,7 @@ i.fa.fa-spin.fa-refresh |    #{translate("loading")}... span.history-toolbar-selected-version( - ng-show="!history.loadingFileTree && !history.showOnlyLabels && !history.error" + ng-show="!history.loadingFileTree && !history.showOnlyLabels && history.selection.updates.length && !history.error" ) #{translate("browsing_project_as_of")}  time.history-toolbar-time {{ history.selection.updates[0].meta.end_ts | formatDate:'Do MMM YYYY, h:mm a' }} span.history-toolbar-selected-version( @@ -19,13 +19,13 @@ button.history-toolbar-btn( ng-click="showAddLabelDialog();" ng-if="!history.showOnlyLabels" - ng-disabled="history.loadingFileTree" + ng-disabled="history.loadingFileTree || history.selection.updates.length == 0" ) i.fa.fa-tag |  #{translate("history_label_this_version")} button.history-toolbar-btn( ng-click="toggleHistoryViewMode();" - ng-disabled="history.loadingFileTree" + ng-disabled="history.loadingFileTree || history.selection.updates.length == 0" ) i.fa.fa-exchange |  #{translate("compare_to_another_version")} diff --git a/services/web/public/stylesheets/app/editor/history-v2.less b/services/web/public/stylesheets/app/editor/history-v2.less index cfefbb462c..8c944062b7 100644 --- a/services/web/public/stylesheets/app/editor/history-v2.less +++ b/services/web/public/stylesheets/app/editor/history-v2.less @@ -19,6 +19,7 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + margin-right: (@line-height-computed / 2); } .history-toolbar-time, .history-toolbar-selected-label { @@ -33,7 +34,7 @@ .btn-xs; padding-left: @padding-small-horizontal; padding-right: @padding-small-horizontal; - margin-left: (@line-height-computed / 2); + margin-right: (@line-height-computed / 2); } .history-toolbar-entries-list { flex: 0 0 @changesListWidth; From 96aa418b94a01bca4dab8b3e010a71179cd83c20 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 17 Aug 2018 16:04:31 +0100 Subject: [PATCH 3/5] Show upgrade prompts when the free history limit is hit. --- .../web/app/views/project/editor/history.pug | 41 ----------------- .../project/editor/history/entriesListV2.pug | 44 +++++++++++++++++++ .../components/historyEntriesList.coffee | 2 + .../stylesheets/app/editor/history-v2.less | 6 +++ 4 files changed, 52 insertions(+), 41 deletions(-) diff --git a/services/web/app/views/project/editor/history.pug b/services/web/app/views/project/editor/history.pug index 35de68957f..f968639cca 100644 --- a/services/web/app/views/project/editor/history.pug +++ b/services/web/app/views/project/editor/history.pug @@ -1,45 +1,4 @@ div#history(ng-show="ui.view == 'history'") - span - .upgrade-prompt(ng-if="project.features.versioning === false && ui.view === 'history'") - .message(ng-if="project.owner._id == user.id") - p.text-center: strong #{translate("upgrade_to_get_feature", {feature:"full Project History"})} - p.text-center.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")} - ul.list-unstyled - li - i.fa.fa-check   - | #{translate("unlimited_projects")} - - li - i.fa.fa-check   - | #{translate("collabs_per_proj", {collabcount:'Multiple'})} - - li - i.fa.fa-check   - | #{translate("full_doc_history")} - - li - i.fa.fa-check   - | #{translate("sync_to_dropbox")} - - li - i.fa.fa-check   - | #{translate("sync_to_github")} - - li - i.fa.fa-check   - |#{translate("compile_larger_projects")} - p.text-center(ng-controller="FreeTrialModalController") - a.btn.btn-success( - href - ng-class="buttonClass" - ng-click="startFreeTrial('history')" - ) #{translate("start_free_trial")} - - .message(ng-show="project.owner._id != user.id") - p #{translate("ask_proj_owner_to_upgrade_for_history")} - p - a.small(href, ng-click="toggleHistory()") #{translate("cancel")} - include ./history/entriesListV1 include ./history/entriesListV2 diff --git a/services/web/app/views/project/editor/history/entriesListV2.pug b/services/web/app/views/project/editor/history/entriesListV2.pug index 5e4c4b66c6..640a761296 100644 --- a/services/web/app/views/project/editor/history/entriesListV2.pug +++ b/services/web/app/views/project/editor/history/entriesListV2.pug @@ -6,11 +6,13 @@ aside.change-list( ng-if="!history.showOnlyLabels && !history.error" entries="history.updates" current-user="user" + current-user-is-owner="project.owner._id === user.id" users="projectUsers" load-entries="loadMore()" load-disabled="history.loading || history.atEnd" load-initialize="ui.view == 'history'" is-loading="history.loading" + free-history-limit-hit="history.freeHistoryLimitHit" on-entry-select="handleEntrySelect(selectedEntry)" on-label-delete="handleLabelDelete(label)" ) @@ -134,6 +136,48 @@ script(type="text/ng-template", id="historyEntriesListTpl") .loading(ng-show="$ctrl.isLoading") i.fa.fa-spin.fa-refresh |    #{translate("loading")}... + .history-entries-list-upgrade-prompt( + ng-if="$ctrl.freeHistoryLimitHit && $ctrl.currentUserIsOwner" + ng-controller="FreeTrialModalController" + ) + p #{translate("currently_seeing_only_24_hrs_history")} + p: strong #{translate("upgrade_to_get_feature", {feature:"full Project History"})} + ul.list-unstyled + li + i.fa.fa-check   + | #{translate("unlimited_projects")} + + li + i.fa.fa-check   + | #{translate("collabs_per_proj", {collabcount:'Multiple'})} + + li + i.fa.fa-check   + | #{translate("full_doc_history")} + + li + i.fa.fa-check   + | #{translate("sync_to_dropbox")} + + li + i.fa.fa-check   + | #{translate("sync_to_github")} + + li + i.fa.fa-check   + |#{translate("compile_larger_projects")} + p.text-center + a.btn.btn-success( + href + ng-class="buttonClass" + ng-click="startFreeTrial('history')" + ) #{translate("start_free_trial")} + p.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")} + .history-entries-list-upgrade-prompt( + ng-if="$ctrl.freeHistoryLimitHit && !$ctrl.currentUserIsOwner" + ) + p #{translate("currently_seeing_only_24_hrs_history")} + strong #{translate("ask_proj_owner_to_upgrade_for_full_history")} script(type="text/ng-template", id="historyEntryTpl") .history-entry( diff --git a/services/web/public/coffee/ide/history/components/historyEntriesList.coffee b/services/web/public/coffee/ide/history/components/historyEntriesList.coffee index de4e4f1b92..7e97121868 100644 --- a/services/web/public/coffee/ide/history/components/historyEntriesList.coffee +++ b/services/web/public/coffee/ide/history/components/historyEntriesList.coffee @@ -30,6 +30,8 @@ define [ loadInitialize: "<" isLoading: "<" currentUser: "<" + freeHistoryLimitHit: "<" + currentUserIsOwner: "<" onEntrySelect: "&" onLabelDelete: "&" controller: historyEntriesListController diff --git a/services/web/public/stylesheets/app/editor/history-v2.less b/services/web/public/stylesheets/app/editor/history-v2.less index 8c944062b7..35190cd662 100644 --- a/services/web/public/stylesheets/app/editor/history-v2.less +++ b/services/web/public/stylesheets/app/editor/history-v2.less @@ -174,6 +174,12 @@ } } +.history-entries-list-upgrade-prompt { + background-color: #FFF; + margin-bottom: 2px; + padding: 5px 10px; +} + .history-labels-list { .history-entries; overflow-y: auto; From 5974afc2e388614715853742f033d0ebe463d4ed Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 17 Aug 2018 16:17:53 +0100 Subject: [PATCH 4/5] Make sure that at least the last update (i.e. the current state) is shown to free users (even if it happened more than 24 hours ago), to allow labelling. --- services/web/public/coffee/ide/history/HistoryV2Manager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/history/HistoryV2Manager.coffee b/services/web/public/coffee/ide/history/HistoryV2Manager.coffee index 004221674f..66b4375c8e 100644 --- a/services/web/public/coffee/ide/history/HistoryV2Manager.coffee +++ b/services/web/public/coffee/ide/history/HistoryV2Manager.coffee @@ -410,7 +410,7 @@ define [ previousUpdate = update if !@$scope.history.userHasFullFeature and update.meta.end_ts < timestamp24hoursAgo - cutOffIndex = i + cutOffIndex = i or 1 # Make sure that we show at least one entry (to allow labelling). @$scope.history.freeHistoryLimitHit = true break From 4dee3fd5e1d9e35fabd5ce4b71c68d47c2df8dfa Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 20 Aug 2018 17:02:55 +0100 Subject: [PATCH 5/5] Update frontend unit tests. --- .../coffee/ide/history/HistoryV2ManagerTests.coffee | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/services/web/test/unit_frontend/coffee/ide/history/HistoryV2ManagerTests.coffee b/services/web/test/unit_frontend/coffee/ide/history/HistoryV2ManagerTests.coffee index 2fc5c5b3b6..542ee3e8eb 100644 --- a/services/web/test/unit_frontend/coffee/ide/history/HistoryV2ManagerTests.coffee +++ b/services/web/test/unit_frontend/coffee/ide/history/HistoryV2ManagerTests.coffee @@ -4,16 +4,21 @@ define ['ide/history/HistoryV2Manager'], (HistoryV2Manager) -> @scope = $watch: sinon.stub() $on: sinon.stub() + project: + features: + versioning: true @ide = {} @historyManager = new HistoryV2Manager(@ide, @scope) - it "should setup the history scope on intialization", -> + it "should setup the history scope on initialization", -> expect(@scope.history).to.deep.equal({ isV2: true updates: [] viewMode: null nextBeforeTimestamp: null atEnd: false + userHasFullFeature: true + freeHistoryLimitHit: false selection: { label: null updates: [] @@ -32,6 +37,12 @@ define ['ide/history/HistoryV2Manager'], (HistoryV2Manager) -> selectedFile: null }) + + it "should setup history without full access to the feature if the project does not have versioning", -> + @scope.project.features.versioning = false + @historyManager = new HistoryV2Manager(@ide, @scope) + expect(@scope.history.userHasFullFeature).to.equal false + describe "_perDocSummaryOfUpdates", -> it "should return the range of updates for the docs", -> result = @historyManager._perDocSummaryOfUpdates([{