From 4b6ab45c57a74b4a8d0572ba6543e95e6ce33466 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 5 Jul 2022 19:45:17 +0530 Subject: [PATCH] refactor: Replace fluxify with vuex Reason: fluxify has GPL2 licence which is incompatible with MIT hence using vuex which is a similar library and has MIT license. --- .eslintrc | 1 - attributions.md | 1 - ...kanban_board.js => kanban_board.bundle.js} | 214 ++++++++---------- .../js/frappe/views/kanban/kanban_view.js | 5 +- frappe/public/js/lib/fluxify.min.js | 6 - package.json | 3 +- yarn.lock | 5 + 7 files changed, 107 insertions(+), 128 deletions(-) rename frappe/public/js/frappe/views/kanban/{kanban_board.js => kanban_board.bundle.js} (84%) delete mode 100644 frappe/public/js/lib/fluxify.min.js diff --git a/.eslintrc b/.eslintrc index adc4aebb28..dd9e350b1b 100644 --- a/.eslintrc +++ b/.eslintrc @@ -134,7 +134,6 @@ "Webcam": true, "PhotoSwipe": true, "PhotoSwipeUI_Default": true, - "fluxify": true, "io": true, "JsBarcode": true, "L": true, diff --git a/attributions.md b/attributions.md index 43c6b14912..5afc9f9d46 100644 --- a/attributions.md +++ b/attributions.md @@ -11,7 +11,6 @@ The following 3rd-party software packages may be used by or distributed with ### Icon Fonts diff --git a/frappe/public/js/frappe/views/kanban/kanban_board.js b/frappe/public/js/frappe/views/kanban/kanban_board.bundle.js similarity index 84% rename from frappe/public/js/frappe/views/kanban/kanban_board.js rename to frappe/public/js/frappe/views/kanban/kanban_board.bundle.js index 64e90f5326..c9793d6eb4 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_board.js +++ b/frappe/public/js/frappe/views/kanban/kanban_board.bundle.js @@ -1,14 +1,17 @@ // TODO: Refactor for better UX +import Vuex from 'vuex'; + frappe.provide("frappe.views"); (function() { var method_prefix = 'frappe.desk.doctype.kanban_board.kanban_board.'; - var store = fluxify.createStore({ - id: 'store', - initialState: { + let columns_unwatcher = null; + + var store = new Vuex.Store({ + state: { doctype: '', board: {}, card_meta: {}, @@ -18,12 +21,16 @@ frappe.provide("frappe.views"); cur_list: {}, empty_state: true }, - actionCallbacks: { - init: function(updater, opts) { - updater.set({ + mutations: { + update_state(state, obj) { + Object.assign(state, obj); + } + }, + actions: { + init: function(context, opts) { + context.commit("update_state", { empty_state: true }); - var board = opts.board; var card_meta = opts.card_meta; opts.card_meta = card_meta; @@ -32,8 +39,7 @@ frappe.provide("frappe.views"); return prepare_card(card, opts); }); var columns = prepare_columns(board.columns); - - updater.set({ + context.commit("update_state", { doctype: opts.doctype, board: board, card_meta: card_meta, @@ -44,20 +50,20 @@ frappe.provide("frappe.views"); wrapper: opts.wrapper }); }, - update_cards: function(updater, cards) { - var state = this; + update_cards: function(context, cards) { + var state = context.state; var _cards = cards .map(card => prepare_card(card, state)) - .concat(this.cards) + .concat(state.cards) .uniqBy(card => card.name); - updater.set({ + context.commit("update_state", { cards: _cards }); }, - add_column: function(updater, col) { + add_column: function(context, col) { if(frappe.model.can_create('Custom Field')) { - fluxify.doAction('update_column', col, 'add'); + store.dispatch('update_column', col, 'add'); } else { frappe.msgprint({ title: __('Not permitted'), @@ -66,15 +72,15 @@ frappe.provide("frappe.views"); }); } }, - archive_column: function(updater, col) { - fluxify.doAction('update_column', col, 'archive'); + archive_column: function(context, col) { + store.dispatch('update_column', col, 'archive'); }, - restore_column: function(updater, col) { - fluxify.doAction('update_column', col, 'restore'); + restore_column: function(context, col) { + store.dispatch('update_column', col, 'restore'); }, - update_column: function(updater, col, action) { - var doctype = this.doctype; - var board = this.board; + update_column: function(context, col, action) { + var doctype = context.state.doctype; + var board = context.state.board; fetch_customization(doctype) .then(function(doc) { return modify_column_field_in_c11n(doc, board, col.title, action); @@ -84,23 +90,23 @@ frappe.provide("frappe.views"); return update_kanban_board(board.name, col.title, action); }).then(function(r) { var cols = r.message; - updater.set({ + context.commit("update_state", { columns: prepare_columns(cols) }); }, function(err) { console.error(err); // eslint-disable-line }); }, - add_card: function(updater, card_title, column_title) { - var doc = frappe.model.get_new_doc(this.doctype); - var field = this.card_meta.title_field; - var quick_entry = this.card_meta.quick_entry; - var state = this; + add_card: function(context, card_title, column_title) { + var state = context.state; + var doc = frappe.model.get_new_doc(state.doctype); + var field = state.card_meta.title_field; + var quick_entry = state.card_meta.quick_entry; var doc_fields = {}; doc_fields[field.fieldname] = card_title; - doc_fields[this.board.field_name] = column_title; - this.cur_list.filter_area.get().forEach(function(f) { + doc_fields[state.board.field_name] = column_title; + state.cur_list.filter_area.get().forEach(function(f) { if (f[2] !== "=") return; doc_fields[f[1]] = f[3]; }); @@ -114,7 +120,7 @@ frappe.provide("frappe.views"); const cards = [...state.cards, card]; // remember the name which we will override later const old_name = doc.name; - updater.set({ cards }); + context.commit("update_state", { cards }); if (field && !quick_entry) { return insert_doc(doc) @@ -125,49 +131,49 @@ frappe.provide("frappe.views"); const card = prepare_card(updated_doc, state); const new_cards = state.cards.slice(); new_cards[index] = card; - updater.set({ cards: new_cards }); + context.commit("update_state", { cards: new_cards }); const args = { new: 1, name: card.name, colname: updated_doc[state.board.field_name], }; - fluxify.doAction('update_order_for_single_card', args); + store.dispatch('update_order_for_single_card', args); }); } else { - frappe.new_doc(this.doctype, doc); + frappe.new_doc(state.doctype, doc); } }, - update_card: function(updater, card) { + update_card: function(context, card) { var index = -1; - this.cards.forEach(function(c, i) { + context.state.cards.forEach(function(c, i) { if (c.name === card.name) { index = i; } }); - var cards = this.cards.slice(); + var cards = context.state.cards.slice(); if (index !== -1) { cards.splice(index, 1, card); } - updater.set({ cards: cards }); + context.commit("update_state", { cards: cards }); }, - update_order_for_single_card: function(updater, card) { + update_order_for_single_card: function(context, card) { // cache original order - const _cards = this.cards.slice(); - const _columns = this.columns.slice(); + const _cards = context.state.cards.slice(); + const _columns = context.state.columns.slice(); let args = {}; let method_name = ""; if (card.new) { method_name = "add_card"; args = { - board_name: this.board.name, + board_name: context.state.board.name, docname: card.name, colname: card.colname, }; } else { method_name = "update_order_for_single_card"; args = { - board_name: this.board.name, + board_name: context.state.board.name, docname: card.name, from_colname: card.from_colname, to_colname: card.to_colname, @@ -184,7 +190,7 @@ frappe.provide("frappe.views"); let updated_cards = [{'name': card.name, 'column': card.to_colname || card.colname}]; let cards = update_cards_column(updated_cards); let columns = prepare_columns(board.columns); - updater.set({ + context.commit("update_state", { cards: cards, columns: columns }); @@ -192,20 +198,20 @@ frappe.provide("frappe.views"); } }).fail(function() { // revert original order - updater.set({ + context.commit("update_state", { cards: _cards, columns: _columns }); frappe.dom.unfreeze(); }); }, - update_order: function(updater) { + update_order: function(context) { // cache original order - const _cards = this.cards.slice(); - const _columns = this.columns.slice(); + const _cards = context.state.cards.slice(); + const _columns = context.state.columns.slice(); const order = {}; - this.wrapper.find('.kanban-column[data-column-value]') + context.state.wrapper.find('.kanban-column[data-column-value]') .each(function() { var col_name = $(this).data().columnValue; order[col_name] = []; @@ -218,7 +224,7 @@ frappe.provide("frappe.views"); frappe.call({ method: method_prefix + "update_order", args: { - board_name: this.board.name, + board_name: context.state.board.name, order: order }, callback: (r) => { @@ -226,46 +232,46 @@ frappe.provide("frappe.views"); var updated_cards = r.message[1]; var cards = update_cards_column(updated_cards); var columns = prepare_columns(board.columns); - updater.set({ + context.commit("update_state", { cards: cards, columns: columns }); } }).fail(function() { // revert original order - updater.set({ + context.commit("update_state", { cards: _cards, columns: _columns }); }); }, - update_column_order: function(updater, order) { + update_column_order: function(context, order) { return frappe.call({ method: method_prefix + "update_column_order", args: { - board_name: this.board.name, + board_name: context.state.board.name, order: order } }).then(function(r) { var board = r.message; var columns = prepare_columns(board.columns); - updater.set({ + context.commit("update_state", { columns: columns }); }); }, - set_indicator: function(updater, column, color) { + set_indicator: function(context, column, color) { return frappe.call({ method: method_prefix + "set_indicator", args: { - board_name: this.board.name, + board_name: context.state.board.name, column_name: column.title, indicator: color } }).then(function(r) { var board = r.message; var columns = prepare_columns(board.columns); - updater.set({ + context.commit("update_state", { columns: columns }); }); @@ -285,20 +291,29 @@ frappe.provide("frappe.views"); opts.cards = cards; if(self.wrapper.find('.kanban').length > 0 && self.cur_list.start !== 0) { - fluxify.doAction('update_cards', cards); + store.dispatch('update_cards', cards); } else { init(); } }; function init() { - fluxify.doAction('init', opts); - store.off('change:columns').on('change:columns', make_columns); + store.dispatch('init', opts); + columns_unwatcher && columns_unwatcher(); + store.watch((state, getters) => { + return state.columns; + }, make_columns); prepare(); - store.on('change:cur_list', setup_restore_columns); - store.on('change:columns', setup_restore_columns); - store.on('change:empty_state', show_empty_state); - fluxify.doAction('update_order'); + store.watch((state, getters) => { + return state.cur_list; + }, setup_restore_columns); + columns_unwatcher = store.watch((state, getters) => { + return state.columns; + }, setup_restore_columns); + store.watch((state, getters) => { + return state.empty_state; + }, show_empty_state); + store.dispatch('update_order'); } function prepare() { @@ -316,7 +331,7 @@ frappe.provide("frappe.views"); function make_columns() { self.$kanban_board.find(".kanban-column").not(".add-new-column").remove(); - var columns = store.getState().columns; + var columns = store.state.columns; columns.filter(is_active_column).map(function(col) { frappe.views.KanbanBoardColumn(col, self.$kanban_board); @@ -338,7 +353,7 @@ frappe.provide("frappe.views"); onEnd: function() { var order = sortable.toArray(); order = order.slice(1); - fluxify.doAction('update_column_order', order); + store.dispatch('update_column_order', order); } }); } @@ -365,7 +380,7 @@ frappe.provide("frappe.views"); var col = { title: title.trim() }; - fluxify.doAction('add_column', col); + store.dispatch('add_column', col); $compose_column_form.find('input').val(''); $compose_column.show(); $compose_column_form.hide(); @@ -421,8 +436,8 @@ frappe.provide("frappe.views"); } function setup_restore_columns() { - var cur_list = store.getState().cur_list; - var columns = store.getState().columns; + var cur_list = store.state.cur_list; + var columns = store.state.columns; var list_row_right = cur_list.$page .find(`[data-list-renderer='Kanban'] .list-row-right`) .css('margin-right', '15px'); @@ -456,12 +471,12 @@ frappe.provide("frappe.views"); title: column_title, status: 'Archived' }; - fluxify.doAction('restore_column', col); + store.dispatch('restore_column', col); }); } function show_empty_state() { - var empty_state = store.getState().empty_state; + var empty_state = store.state.empty_state; if(empty_state) { self.$kanban_board.find('.kanban-column').hide(); @@ -485,7 +500,9 @@ frappe.provide("frappe.views"); make_dom(); setup_sortable(); make_cards(); - store.on('change:cards', make_cards); + store.watch((state, getters) => { + return state.cards; + }, make_cards); bind_add_card(); bind_options(); } @@ -494,7 +511,7 @@ frappe.provide("frappe.views"); self.$kanban_column = $(frappe.render_template( 'kanban_column', { title: column.title, - doctype: store.getState().doctype, + doctype: store.state.doctype, indicator: frappe.scrub(column.indicator, '-') })).appendTo(wrapper); self.$kanban_cards = self.$kanban_column.find('.kanban-cards'); @@ -502,7 +519,7 @@ frappe.provide("frappe.views"); function make_cards() { self.$kanban_cards.empty(); - var cards = store.getState().cards; + var cards = store.state.cards; filtered_cards = get_cards_for_column(cards, column); var filtered_cards_names = filtered_cards.map(card => card.name); @@ -548,7 +565,7 @@ frappe.provide("frappe.views"); old_index: e.oldIndex, new_index: e.newIndex, }; - fluxify.doAction('update_order_for_single_card', args); + store.dispatch('update_order_for_single_card', args); }, onAdd: function() { }, @@ -579,7 +596,7 @@ frappe.provide("frappe.views"); var card_title = $textarea.val(); $new_card_area.hide(); $textarea.val(''); - fluxify.doAction('add_card', card_title, column.title) + store.dispatch('add_card', card_title, column.title) .then(() => { $btn_add.show(); }); @@ -602,10 +619,10 @@ frappe.provide("frappe.views"); var action = $btn.data().action; if (action === "archive") { - fluxify.doAction('archive_column', column); + store.dispatch('archive_column', column); } else if (action === "indicator") { var color = $btn.data().indicator; - fluxify.doAction('set_indicator', column, color); + store.dispatch('set_indicator', column, color); } }); get_column_indicators(function(indicators) { @@ -728,7 +745,7 @@ frappe.provide("frappe.views"); callback: function() { const users = self.assign_to_dialog.get_values().assign_to; card.assigned_list = [...new Set(card.assigned_list.concat(users))]; - fluxify.doAction('update_card', card); + store.dispatch('update_card', card); } }); self.assign_to_dialog = self.assign_to.dialog; @@ -850,21 +867,6 @@ frappe.provide("frappe.views"); }); } - function is_filters_modified(board, cur_list) { - return new Promise(function(resolve) { - setTimeout(function() { - try { - var list_filters = JSON.stringify(cur_list.filter_area.get()); - resolve(list_filters !== board.filters); - } catch(e) { - // sometimes the filter_list is not initiated - resolve(false); - } - - }, 2000); - }); - } - function is_active_column(col) { return col.status !== 'Archived'; } @@ -876,13 +878,13 @@ frappe.provide("frappe.views"); } function get_card(name) { - return store.getState().cards.find(function(c) { + return store.state.cards.find(function(c) { return c.name === name; }); } function update_cards_column(updated_cards) { - var cards = store.getState().cards; + var cards = store.state.cards; cards.forEach(function(c) { updated_cards.forEach(function(uc) { if(uc.name === c.name) { @@ -909,22 +911,4 @@ frappe.provide("frappe.views"); callback(indicators); }); } - - function isBound(el, event, fn) { - var events = $._data(el[0], 'events'); - if(!events) return false; - var handlers = events[event]; - var flag = false; - handlers.forEach(function(h) { - if(h.handler.name === fn.name) - flag = true; - }); - return flag; - } - - function remove_img_tags(html) { - const $temp = $(`
${html}
`); - $temp.find('img').remove(); - return $temp.html(); - } })(); diff --git a/frappe/public/js/frappe/views/kanban/kanban_view.js b/frappe/public/js/frappe/views/kanban/kanban_view.js index 129db13b07..f38cb906a2 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_view.js +++ b/frappe/public/js/frappe/views/kanban/kanban_view.js @@ -209,10 +209,7 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView { } get required_libs() { - return [ - 'assets/frappe/js/lib/fluxify.min.js', - 'assets/frappe/js/frappe/views/kanban/kanban_board.js' - ]; + return 'kanban_board.bundle.js'; } }; diff --git a/frappe/public/js/lib/fluxify.min.js b/frappe/public/js/lib/fluxify.min.js deleted file mode 100644 index 586630486a..0000000000 --- a/frappe/public/js/lib/fluxify.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/* -fluxify v0.2.3 -https://github.com/arqex/fluxify -GNU-2: https://github.com/arqex/fluxify/raw/master/LICENSE -*/ -!function(t,e){"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?module.exports=e():t.fluxify=e()}(this,function(){"use strict";var t={_extend:function(t){for(var e,i,r=0;r=0;i--)r[i].callback===e&&r.splice(i,1)}return this},trigger:function(t){var e,i,r=[].slice.call(arguments,1),n=this._events[t]||[],s=[];for(e=0;e=0;e--)n.splice(s[e],1);return this}};t._extend(i,{addListener:i.on,removeListener:i.off,removeAllListeners:i.off,emit:i.trigger}),e.prototype={};for(var r in i)Object.defineProperty(e.prototype,r,{value:i[r]});Object.defineProperty(e,"_extend",{value:function(e){var i,r=this;i=e&&e.hasOwnProperty(constructor)?e.constructor:function(){return r.apply(this,arguments)},t._extend(i,r);var n=function(){Object.defineProperty(this,"constructor",{value:i})};if(n.prototype=r.prototype,i.prototype=new n,e)for(var s in e)"constructor"!=s&&Object.defineProperty(i.prototype,s,{value:e[s]});return i.__super__=r.prototype,i}});var n=e._extend({initialize:function(t){if(!t)return this.props={};this.props={};for(var e in t)this.props[e]=t[e]},get:function(t){return this.props[t]},set:function(t,e){var i,r,n=t,s=[];"undefined"!=typeof e&&(n={},n[t]=e);for(r in n)this.props[r]!=n[r]&&(i=this.props[r],this.props[r]=n[r],s.push({prop:r,previousValue:i,value:n[r]}));s.length&&this.emit("change",s)}}),s=e._extend({initialize:function(t){var e,i,r=this,s=t||{},o=new n(s.initialState);t.id&&Object.defineProperty(this,"_id",{value:t.id}),Object.defineProperties(this,{_callbacks:{writable:!0,configurable:!0,value:{}},addActionCallbacks:{value:function(t){for(e in t)r._callbacks[e]=t[e].bind(this,o)}},callback:{value:function(){var t=arguments[0],e=[].slice.call(arguments,1);return this._callbacks[t]?this._callbacks[t].apply(this,e):!0}.bind(this)}}),this.addActionCallbacks(s.actionCallbacks||{});var a=function(t){Object.defineProperty(r,t,{enumerable:!0,configurable:!1,get:function(){return o.get(t)}})};if(s.initialState)for(i in s.initialState)a(i,s.initialState[i]);o.on("change",function(t){var e,i,n=t.length;for(i=0;n>i;i++)e=t[i],r.hasOwnProperty(e.prop)||a(e.prop,e.value),r.emit("change:"+e.prop,e.value,e.previousValue);r.emit("change",t)})},getState:function(){return t._extend({},this)},waitFor:function(t){return this._dispatcher.waitFor(t)}}),o=function(){this._callbacks={},this._dispatchQueue=[],this._currentDispatch=!1,this._ID=1,"undefined"!=typeof Promise&&(this._Promise=Promise)};o.prototype={register:function(t,e){var i=t;return"function"==typeof t&&(i="ID_"+this._ID,e=t),this._callbacks[i]=e,this._ID++,i},registerStore:function(t,e){return Object.defineProperty(e,"_dispatcher",{value:this}),this.register(t,e.callback)},unregister:function(t){delete this._callbacks[t]},waitFor:function(t){var e=[],i=0;for(Array.isArray(t)||(t=[t]);i