From 86667feb6b6b86d935a14b15ceae19a435c91a81 Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Tue, 15 Sep 2020 12:34:51 +0200 Subject: [PATCH 1/6] Add remembered state for smart annotation --- app/assets/javascripts/sitewide/atwho_res.js | 17 +- .../javascripts/sitewide/atwho_res.js.erb | 323 ++++++++++++++++++ app/controllers/at_who_controller.rb | 4 + .../shared/smart_annotation/_menu.html.erb | 2 +- 4 files changed, 343 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/sitewide/atwho_res.js.erb diff --git a/app/assets/javascripts/sitewide/atwho_res.js b/app/assets/javascripts/sitewide/atwho_res.js index e14f75dcb..75e90d34c 100644 --- a/app/assets/javascripts/sitewide/atwho_res.js +++ b/app/assets/javascripts/sitewide/atwho_res.js @@ -71,6 +71,11 @@ var SmartAnnotation = (function() { } } $.getJSON(filterType.dataUrl, params, function(data) { + localStorage.setItem('smart_annotation_state_per_team/' + data.team, JSON.stringify({ + tag: filterType.tag, + repository: data.repository + })); + callback(data.res); if (data.repository) { @@ -132,8 +137,16 @@ var SmartAnnotation = (function() { }); if ($currentAtWho.find('.tab-pane.active').length === 0) { - let filterType = DEFAULT_SEARCH_FILTER; - $currentAtWho.find(`.${filterType.tag}`).click(); + let filterType = DEFAULT_SEARCH_FILTER.tag; + let teamId = $currentAtWho.find('.atwho-header-res').data('team-id'); + let remeberedState = localStorage.getItem('smart_annotation_state_per_team/' + teamId); + if (remeberedState) { + remeberedState = JSON.parse(remeberedState); + filterType = remeberedState.tag; + $currentAtWho.find(`.repository-object[data-object-id=${remeberedState.repository}]`) + .addClass('btn-primary'); + } + $currentAtWho.find(`.${filterType}`).click(); } }) .on('reposition.atwho', function(event, flag, query) { diff --git a/app/assets/javascripts/sitewide/atwho_res.js.erb b/app/assets/javascripts/sitewide/atwho_res.js.erb new file mode 100644 index 000000000..4574c9e1f --- /dev/null +++ b/app/assets/javascripts/sitewide/atwho_res.js.erb @@ -0,0 +1,323 @@ +var SmartAnnotation = (function() { + 'use strict'; + + // utilities + var Util = (function() { + // helper method that binds show/hidden action + function showHideBinding() { + $.each(['show', 'hide'], function (i, ev) { + var el = $.fn[ev]; + $.fn[ev] = function () { + this.trigger(ev); + return el.apply(this, arguments); + }; + }); + } + + var publicApi = { + showHideBinding: showHideBinding + }; + + return publicApi; + })(); + + // stop the user annotation popover on click propagation + function atwhoStopPropagation(element) { + $(element).on('click', function(e) { + e.stopPropagation(); + e.preventDefault(); + }); + } + + function setAtWho(field) { + var FilterTypeEnum = Object.freeze({ + USER: {tag: "users", + dataUrl: $(document.body).attr('data-atwho-users-url')}, + TASK: {tag: "sa-tasks", + dataUrl: $(document.body).attr('data-atwho-task-url')}, + PROJECT: {tag: "sa-projects", + dataUrl: $(document.body).attr('data-atwho-project-url')}, + EXPERIMENT: {tag: "sa-experiments", + dataUrl: $(document.body).attr('data-atwho-experiment-url')}, + REPOSITORY: {tag: "sa-repositories", + dataUrl: $(document.body).attr('data-atwho-rep-items-url')}, + MENU: {tag: "menu", + dataUrl: $(document.body).attr('data-atwho-menu-items')} + }); + var prevAt, + // Default selected filter when using '#' + DEFAULT_SEARCH_FILTER = FilterTypeEnum.REPOSITORY, + atWhoUpdating = false; + + function _matchHighlighter(html, query, filterType) { + var $html = $(html); + var $li_text = $html.find('.item-text'); + + if ($li_text.length === 0 || !query) return html; + + $.each($li_text, function(i, item) { + $(item).html($(item).text().replace(new RegExp(query, 'gi'), '$&')); + }) + + return $html; + } + + function _generateInputTag(value, li) { + return `[#${li.attr('data-name')}~${li.attr('data-type')}~${li.attr('data-id')}]`; + } + + // Generates suggestion dropdown filter + function generateFilterMenu(active, res_data) { + var menu = ''; + $.ajax({ + async: false, + dataType: 'json', + url: $(document.body).attr('data-atwho-repositories-url'), + success: function(data) { + menu = data.html + } + }); + return menu; + } + + function atWhoSettings(at, defaultFilterType) { + return { + at: at, + callbacks: { + remoteFilter: function(query, callback) { + var $currentAtWho = $('.atwho-view[style]'); + var filterType; + var params = { query: query }; + filterType = FilterTypeEnum[$currentAtWho.find('.tab-pane.active').data('object-type')] + if (!filterType) { + callback([{name: ''}]); + return false + } + + if(filterType.tag === 'sa-repositories') { + let repositoryTab = $currentAtWho.find('[data-object-type="REPOSITORY"]') + let activeRepository = repositoryTab.find('.btn-primary'); + if (activeRepository.length) { + params['repository_id'] = activeRepository.data('object-id') + } + + } + $.getJSON(filterType.dataUrl, params, function(data) { + + localStorage.setItem('smart_annotation_state_per_team/' + data.team, JSON.stringify({ + tag: filterType.tag, + repository: data.repository + })); + callback(data.res); + + if (data.repository) { + $currentAtWho.find(`.repository-object[data-object-id="${data.repository}"]`) + .addClass('btn-primary').removeClass('btn-light') + } + }); + }, + tplEval: function(_tpl, items) { + return items.name; + }, + highlighter: function(li, query) { + return _matchHighlighter(li, query, true); + return li; + }, + beforeInsert: function(value, li) { + return _generateInputTag(value, li); + }, + matcher:function(flag, subtext, should_startWithSpace, acceptSpaceBar) { + var _a, _y, match, regexp, space; + flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + if (should_startWithSpace) { + flag = '(?:^|\\s)' + flag; + } + _a = decodeURI("%C3%80"); + _y = decodeURI("%C3%BF"); + regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_/:\\s\+\-\]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi'); + match = regexp.exec(subtext); + if (match) { + return match[2] || match[1]; + } else { + return null; + } + }, + }, + headerTpl: generateFilterMenu(defaultFilterType), + limit: <%= Constants::ATWHO_SEARCH_LIMIT %>, + startWithSpace: true, + acceptSpaceBar: true, + displayTimeout: 120000 + } + } + + function init() { + $(field) + .on("shown.atwho", function() { + var $currentAtWho = $('.atwho-view[style]'); + $currentAtWho.find('.tab-button').off().on('shown.bs.tab', function() { + $(field).click().focus(); + $(this).closest('.nav-tabs').find('.tab-button').removeClass('active'); + $(this).addClass('active'); + }) + $currentAtWho.find('.repository-object').off().on('click', function() { + $(this).parent().find('.repository-object').removeClass('btn-primary').addClass('btn-light'); + $(this).addClass('btn-primary').removeClass('btn-light'); + $(field).click().focus(); + }) + + if ($currentAtWho.find('.tab-pane.active').length == 0) { + let filterType = DEFAULT_SEARCH_FILTER.tag; + let teamId = $currentAtWho.find('.atwho-header-res').data('team-id'); + let remeberedState = localStorage.getItem('smart_annotation_state_per_team/' + teamId); + if (remeberedState) { + remeberedState = JSON.parse(remeberedState); + filterType = remeberedState.tag; + $currentAtWho.find(`.repository-object[data-object-id=${remeberedState.repository}]`) + .addClass('btn-primary'); + } + $currentAtWho.find(`.${filterType}`).click(); + } + }) + .on("reposition.atwho", function(event, flag, query) { + let inputFieldLeft = query.$inputor.offset().left; + if (inputFieldLeft > $(window).width()) { + let leftPosition; + if (inputFieldLeft < flag.left + $(window).scrollLeft()) { + leftPosition = inputFieldLeft; + } else { + leftPosition = flag.left + $(window).scrollLeft(); + } + query.$el.find('.atwho-view').css('left', leftPosition + 'px'); + } + if ($('.repository-show').length) { + query.$el.find('.atwho-view').css('top', flag.top + 'px'); + } + }) + .atwho({ + at: '@', + callbacks: { + remoteFilter: function(query, callback) { + $.getJSON( + FilterTypeEnum.USER.dataUrl, + {query: query}, + function(data) { + if (data.users.length < 1) { + callback([{no_results: 1}]); + } else { + callback(data.users); + } + initDismissButton($('.atwho-view[style]')); + } + ); + }, + sorter: function(query, items, _searchKey) { + // Sorting is already done on server-side + return items; + }, + tplEval: function(_tpl, map) { + var res; + try { + if (map.no_results) { + res = ''; + } else { + res = ''; + res += '
  • '; + res += ''; + res += ''; + res += map.full_name; + res += ''; + res += ''; + res += ' '; + res += '·'; + res += ' '; + res += ''; + res += map.email; + res += ''; + res += ''; + res += '
  • '; + } + } catch (_error) { + res = ''; + } + return res; + }, + highlighter: function(li, query) { + return li;//_matchHighlighter(li, query); + }, + beforeInsert: function(value, li) { + var res = ''; + res += '[@' + li.attr('data-full-name'); + res += '~' + li.attr('data-id') + ']'; + return res; + } + }, + headerTpl: + '
    ' + + '
    <%= I18n.t("atwho.users.title") %>
    ' + + '
    ' + + '
    ' + + '<%= I18n.t("atwho.users.navigate_1") %> ' + + '<%= I18n.t("atwho.users.navigate_2") %>' + + '
    ' + + '
    ' + + '<%= I18n.t("atwho.users.confirm_1") %> ' + + '<%= I18n.t("atwho.users.confirm_2") %>' + + '
    ' + + '
    ' + + '<%= I18n.t("atwho.users.dismiss_1") %> ' + + '<%= I18n.t("atwho.users.dismiss_2") %>' + + '
    ' + + '
    ' + + '
    ' + + '' + + '
    ' + + '
    ', + limit: <%= Constants::ATWHO_SEARCH_LIMIT %>, + startsWithSpace: true, + acceptSpaceBar: true, + displayTimeout: 120000 + }) + .atwho(atWhoSettings('#', DEFAULT_SEARCH_FILTER)) + // .atwho(atWhoSettings('task#', FilterTypeEnum.TASK)) Waiting for better times + // .atwho(atWhoSettings('project#', FilterTypeEnum.PROJECT)) + // .atwho(atWhoSettings('experiment#', FilterTypeEnum.EXPERIMENT)) + // .atwho(atWhoSettings('sample#', FilterTypeEnum.REPOSITORY)); + } + + return { + init: init + }; + } + // Closes the atwho popup * needed in repositories to close the popup + // if nothing is selected and the user leaves the form * + function closePopup() { + $('.atwho-header-res').find('.fa-times').click(); + } + + function initialize(field) { + var atWho = new setAtWho(field); + atWho.init(); + } + + var publicApi = Object.freeze({ + init: initialize, + preventPropagation: atwhoStopPropagation, + closePopup: closePopup + }); + + return publicApi; + +})(); + + +// initialize the smart annotations +(function initSmartAnnotation() { + $(document).on('focus', '[data-atwho-edit]', function() { + if(_.isUndefined($(this).data('atwho'))) { + SmartAnnotation.init(this); + } + }); +})(); diff --git a/app/controllers/at_who_controller.rb b/app/controllers/at_who_controller.rb index a4cc40aec..7c71816d9 100644 --- a/app/controllers/at_who_controller.rb +++ b/app/controllers/at_who_controller.rb @@ -48,6 +48,7 @@ class AtWhoController < ApplicationController repository_rows: items })], repository: repository.id, + team: current_team.id, status: :ok } end @@ -68,6 +69,7 @@ class AtWhoController < ApplicationController res: [render_to_string(partial: 'shared/smart_annotation/project_items.html.erb', locals: { projects: res.projects })], + team: current_team.id, status: :ok } end @@ -82,6 +84,7 @@ class AtWhoController < ApplicationController res: [render_to_string(partial: 'shared/smart_annotation/experiment_items.html.erb', locals: { experiments: res.experiments })], + team: current_team.id, status: :ok } end @@ -96,6 +99,7 @@ class AtWhoController < ApplicationController res: [render_to_string(partial: 'shared/smart_annotation/my_module_items.html.erb', locals: { my_modules: res.my_modules })], + team: current_team.id, status: :ok } end diff --git a/app/views/shared/smart_annotation/_menu.html.erb b/app/views/shared/smart_annotation/_menu.html.erb index ca05b2b8f..afb2f223b 100644 --- a/app/views/shared/smart_annotation/_menu.html.erb +++ b/app/views/shared/smart_annotation/_menu.html.erb @@ -1,5 +1,5 @@ <% at_who_key = SecureRandom.hex %> -
    +