update dependencies; live task editing
This commit is contained in:
parent
a306f72215
commit
dd915624a3
4
Gemfile
4
Gemfile
@ -1,9 +1,9 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rails', '6.1.3.2'
|
||||
gem 'rails', '6.1.4.4'
|
||||
gem 'pg', '~> 1.2.3'
|
||||
|
||||
gem 'webpacker', '5.3.0'
|
||||
gem 'webpacker', '5.4.3'
|
||||
gem 'bootsnap', '>= 1.1.0', require: false
|
||||
|
||||
gem 'oj', '~> 3.11.5'
|
||||
|
118
Gemfile.lock
118
Gemfile.lock
@ -1,60 +1,60 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
actioncable (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
activejob (= 6.1.3.2)
|
||||
activerecord (= 6.1.3.2)
|
||||
activestorage (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
actionmailbox (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activejob (= 6.1.4.4)
|
||||
activerecord (= 6.1.4.4)
|
||||
activestorage (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
actionview (= 6.1.3.2)
|
||||
activejob (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
actionmailer (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
actionview (= 6.1.4.4)
|
||||
activejob (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (6.1.3.2)
|
||||
actionview (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
actionpack (6.1.4.4)
|
||||
actionview (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
rack (~> 2.0, >= 2.0.9)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
activerecord (= 6.1.3.2)
|
||||
activestorage (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
actiontext (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activerecord (= 6.1.4.4)
|
||||
activestorage (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
actionview (6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
activejob (6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
activejob (6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
activerecord (6.1.3.2)
|
||||
activemodel (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
activestorage (6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
activejob (= 6.1.3.2)
|
||||
activerecord (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
activemodel (6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
activerecord (6.1.4.4)
|
||||
activemodel (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
activestorage (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activejob (= 6.1.4.4)
|
||||
activerecord (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
marcel (~> 1.0.0)
|
||||
mini_mime (~> 1.0.2)
|
||||
activesupport (6.1.3.2)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (6.1.4.4)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
@ -127,7 +127,7 @@ GEM
|
||||
memoizable (0.4.2)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
method_source (1.0.0)
|
||||
mini_mime (1.0.3)
|
||||
mini_mime (1.1.2)
|
||||
mini_portile2 (2.7.1)
|
||||
minitest (5.15.0)
|
||||
msgpack (1.4.4)
|
||||
@ -153,20 +153,20 @@ GEM
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (6.1.3.2)
|
||||
actioncable (= 6.1.3.2)
|
||||
actionmailbox (= 6.1.3.2)
|
||||
actionmailer (= 6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
actiontext (= 6.1.3.2)
|
||||
actionview (= 6.1.3.2)
|
||||
activejob (= 6.1.3.2)
|
||||
activemodel (= 6.1.3.2)
|
||||
activerecord (= 6.1.3.2)
|
||||
activestorage (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
rails (6.1.4.4)
|
||||
actioncable (= 6.1.4.4)
|
||||
actionmailbox (= 6.1.4.4)
|
||||
actionmailer (= 6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
actiontext (= 6.1.4.4)
|
||||
actionview (= 6.1.4.4)
|
||||
activejob (= 6.1.4.4)
|
||||
activemodel (= 6.1.4.4)
|
||||
activerecord (= 6.1.4.4)
|
||||
activestorage (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 6.1.3.2)
|
||||
railties (= 6.1.4.4)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
@ -177,11 +177,11 @@ GEM
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.4.2)
|
||||
loofah (~> 2.3)
|
||||
railties (6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
railties (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
rake (>= 0.13)
|
||||
thor (~> 1.0)
|
||||
rake (13.0.6)
|
||||
rb-fsevent (0.11.0)
|
||||
@ -231,7 +231,7 @@ GEM
|
||||
memoizable (~> 0.4)
|
||||
parslet (~> 1.5)
|
||||
signed_multiset (~> 0.2)
|
||||
webpacker (5.3.0)
|
||||
webpacker (5.4.3)
|
||||
activesupport (>= 5.2)
|
||||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 5.2)
|
||||
@ -256,14 +256,14 @@ DEPENDENCIES
|
||||
oj (~> 3.11.5)
|
||||
pg (~> 1.2.3)
|
||||
puma (~> 5.3)
|
||||
rails (= 6.1.3.2)
|
||||
rails (= 6.1.4.4)
|
||||
rails-controller-testing
|
||||
redcarpet (~> 3.5.1)
|
||||
rspec-rails (~> 5.0.1)
|
||||
sqlite3 (~> 1.4.2)
|
||||
tzinfo-data
|
||||
unitwise (~> 2.2.0)
|
||||
webpacker (= 5.3.0)
|
||||
webpacker (= 5.4.3)
|
||||
|
||||
BUNDLED WITH
|
||||
2.2.17
|
||||
|
4
app/channels/application_cable/channel.rb
Normal file
4
app/channels/application_cable/channel.rb
Normal file
@ -0,0 +1,4 @@
|
||||
module ApplicationCable
|
||||
class Channel < ActionCable::Channel::Base
|
||||
end
|
||||
end
|
18
app/channels/application_cable/connection.rb
Normal file
18
app/channels/application_cable/connection.rb
Normal file
@ -0,0 +1,18 @@
|
||||
module ApplicationCable
|
||||
class Connection < ActionCable::Connection::Base
|
||||
identified_by :current_user
|
||||
|
||||
def connect
|
||||
self.current_user = find_verified_user
|
||||
end
|
||||
|
||||
private
|
||||
def find_verified_user
|
||||
if verified_user = User.find_by(id: cookies.encrypted['_parsley_session']['user_id'])
|
||||
verified_user
|
||||
else
|
||||
reject_unauthorized_connection
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
12
app/channels/task_channel.rb
Normal file
12
app/channels/task_channel.rb
Normal file
@ -0,0 +1,12 @@
|
||||
class TaskChannel < ApplicationCable::Channel
|
||||
|
||||
def subscribed
|
||||
stream_for current_user.id
|
||||
end
|
||||
|
||||
def self.update_task_list(task_list)
|
||||
task_list.reload
|
||||
self.broadcast_to task_list.user_id, { task_list: TaskListSerializer.for(task_list), action: 'updated' }
|
||||
end
|
||||
|
||||
end
|
@ -9,6 +9,7 @@ class TaskItemsController < ApplicationController
|
||||
@task_item.task_list = @task_list
|
||||
|
||||
if @task_item.save
|
||||
TaskChannel.update_task_list(@task_list)
|
||||
render json: TaskItemSerializer.for(@task_item), status: :created, location: [@task_list, @task_item]
|
||||
else
|
||||
render json: @task_item.errors, status: :unprocessable_entity
|
||||
@ -17,6 +18,7 @@ class TaskItemsController < ApplicationController
|
||||
|
||||
def update
|
||||
if @task_item.update(task_item_params)
|
||||
TaskChannel.update_task_list(@task_list)
|
||||
render json: TaskItemSerializer.for(@task_item), status: :ok, location: [@task_list, @task_item]
|
||||
else
|
||||
render json: @task_item.errors, status: :unprocessable_entity
|
||||
@ -30,6 +32,8 @@ class TaskItemsController < ApplicationController
|
||||
@task_items.each { |i| i.destroy }
|
||||
end
|
||||
|
||||
TaskChannel.update_task_list(@task_list)
|
||||
|
||||
head :no_content
|
||||
end
|
||||
|
||||
@ -41,6 +45,8 @@ class TaskItemsController < ApplicationController
|
||||
@task_items.each { |i| i.update_attribute(:completed, new_status) }
|
||||
end
|
||||
|
||||
TaskChannel.update_task_list(@task_list)
|
||||
|
||||
head :no_content
|
||||
end
|
||||
|
||||
@ -59,4 +65,4 @@ class TaskItemsController < ApplicationController
|
||||
@task_item = @task_list.task_items.find(params[:id])
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -27,6 +27,7 @@ class TaskListsController < ApplicationController
|
||||
def update
|
||||
ensure_owner(@task_list) do
|
||||
if @task_list.update(task_list_params)
|
||||
TaskChannel.update_task_list(@task_list)
|
||||
render json: TaskListSerializer.for(@task_list), status: :ok, location: @task_list
|
||||
else
|
||||
render json: @task_list.errors, status: :unprocessable_entity
|
||||
@ -40,13 +41,14 @@ class TaskListsController < ApplicationController
|
||||
head :no_content
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def add_recipe
|
||||
ensure_owner(@task_list) do
|
||||
recipe = Recipe.find(params[:recipe_id])
|
||||
|
||||
|
||||
@task_list.add_recipe_ingredients(recipe)
|
||||
|
||||
TaskChannel.update_task_list(@task_list)
|
||||
|
||||
head :no_content
|
||||
end
|
||||
end
|
||||
@ -61,4 +63,4 @@ class TaskListsController < ApplicationController
|
||||
@task_list = TaskList.find(params[:id])
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -53,6 +53,12 @@
|
||||
watch: {
|
||||
routeQuery() {
|
||||
this.refreshData();
|
||||
},
|
||||
|
||||
recipe(newRecipe) {
|
||||
if (newRecipe) {
|
||||
document.title = `${newRecipe.name}`;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -76,4 +82,4 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
</style>
|
||||
|
17
app/javascript/lib/ActionCable.js
Normal file
17
app/javascript/lib/ActionCable.js
Normal file
@ -0,0 +1,17 @@
|
||||
import * as cable from "@rails/actioncable";
|
||||
|
||||
let consumer = null;
|
||||
|
||||
function createChannel(baseUrl, ...args) {
|
||||
if (consumer === null) {
|
||||
if (baseUrl !== null && baseUrl.toString() !== "") {
|
||||
consumer = cable.createConsumer(baseUrl);
|
||||
} else {
|
||||
consumer = cable.createConsumer();
|
||||
}
|
||||
}
|
||||
|
||||
return consumer.subscriptions.create(...args);
|
||||
}
|
||||
|
||||
export { createChannel };
|
@ -5,6 +5,7 @@ import Vue from 'vue'
|
||||
import { sync } from 'vuex-router-sync';
|
||||
import { swInit } from "../lib/ServiceWorker";
|
||||
import responsiveSync from "../lib/VuexResponsiveSync";
|
||||
import { createChannel } from "../lib/ActionCable";
|
||||
import VueProgressBar from "vue-progressbar";
|
||||
import VueResize from "vue-resize";
|
||||
import config from '../config';
|
||||
@ -67,6 +68,10 @@ Vue.use(VueResize);
|
||||
sync(store, router);
|
||||
responsiveSync(store);
|
||||
|
||||
Vue.prototype.$createChannel = function(...args) {
|
||||
createChannel(null, ...args);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
const app = document.getElementById('app');
|
||||
|
@ -40,6 +40,10 @@ router.afterEach((to, from) => {
|
||||
if (to.meta.handleInitialLoad !== true && $store.state.initialLoad === false) {
|
||||
$store.commit("setInitialLoad", true);
|
||||
}
|
||||
|
||||
Vue.nextTick(() => {
|
||||
document.title = to.meta.title || 'Parsley';
|
||||
});
|
||||
});
|
||||
|
||||
router.addRoutes(
|
||||
|
@ -2,9 +2,12 @@ import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
import api from '../lib/Api';
|
||||
import { createChannel } from '../lib/ActionCable';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
let taskChannel = null;
|
||||
|
||||
export default new Vuex.Store({
|
||||
strict: process.env.NODE_ENV !== 'production',
|
||||
state: {
|
||||
@ -99,45 +102,15 @@ export default new Vuex.Store({
|
||||
state.currentTaskList = list || null;
|
||||
},
|
||||
|
||||
appendTaskItem(state, item) {
|
||||
const listId = item.task_list_id;
|
||||
const list = state.taskLists.find(l => l.id === listId);
|
||||
if (list) {
|
||||
list.task_items.push(item);
|
||||
}
|
||||
},
|
||||
|
||||
replaceTaskItem(state, item) {
|
||||
const listId = item.task_list_id;
|
||||
const list = state.taskLists.find(l => l.id === listId);
|
||||
if (list) {
|
||||
const taskIdx = list.task_items.findIndex(i => i.id === item.id);
|
||||
if (taskIdx >= 0) {
|
||||
list.task_items.splice(taskIdx, 1, item);
|
||||
replaceTaskList(state, list) {
|
||||
if (state.taskLists) {
|
||||
const listIdx = state.taskLists.findIndex(l => l.id === list.id);
|
||||
if (listIdx >= 0) {
|
||||
state.taskLists.splice(listIdx, 1, list);
|
||||
}
|
||||
if (state.currentTaskList && state.currentTaskList.id === list.id) {
|
||||
state.currentTaskList = list;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
removeTaskItems(state, payload) {
|
||||
const listId = payload.taskList.id;
|
||||
const list = state.taskLists.find(l => l.id === listId);
|
||||
if (list) {
|
||||
|
||||
list.task_items = list.task_items.filter(item => {
|
||||
return payload.taskItems.findIndex(i => i.id === item.id) === -1;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
setTaskItemCompletion(state, payload) {
|
||||
const listId = payload.taskList.id;
|
||||
const list = state.taskLists.find(l => l.id === listId);
|
||||
if (list) {
|
||||
list.task_items.forEach(item => {
|
||||
if (payload.taskItems.findIndex(i => i.id === item.id) >= 0) {
|
||||
item.completed = payload.completed;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -171,7 +144,7 @@ export default new Vuex.Store({
|
||||
});
|
||||
},
|
||||
|
||||
refreshTaskLists({commit, state}) {
|
||||
refreshTaskLists({commit, dispatch, state}) {
|
||||
const cb = function(data) {
|
||||
commit("setTaskLists", data);
|
||||
let ctl = null;
|
||||
@ -182,11 +155,12 @@ export default new Vuex.Store({
|
||||
|
||||
ctl = ctl || data[0] || null;
|
||||
commit("setCurrentTaskList", ctl);
|
||||
dispatch('ensureTaskListChannel');
|
||||
};
|
||||
|
||||
return api.getTaskLists(cb)
|
||||
},
|
||||
|
||||
|
||||
ensureTaskLists({dispatch, state}) {
|
||||
if (state.user && state.taskLists.length === 0) {
|
||||
return dispatch("refreshTaskLists");
|
||||
@ -195,6 +169,18 @@ export default new Vuex.Store({
|
||||
}
|
||||
},
|
||||
|
||||
ensureTaskListChannel({ commit }) {
|
||||
if (taskChannel === null) {
|
||||
taskChannel = createChannel(null, "TaskChannel", {
|
||||
received(data) {
|
||||
if (data && data.action === 'updated') {
|
||||
commit('replaceTaskList', data.task_list);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
createTaskList({commit, dispatch}, newList) {
|
||||
return api.postTaskList(newList)
|
||||
.then(data => commit("setCurrentTaskList", data))
|
||||
@ -210,7 +196,6 @@ export default new Vuex.Store({
|
||||
|
||||
return api.postTaskItem(taskItem.task_list_id, taskItem)
|
||||
.then(data => {
|
||||
commit("appendTaskItem", data);
|
||||
return data;
|
||||
});
|
||||
},
|
||||
@ -218,19 +203,16 @@ export default new Vuex.Store({
|
||||
updateTaskItem({commit}, taskItem) {
|
||||
return api.patchTaskItem(taskItem.task_list_id, taskItem)
|
||||
.then(data => {
|
||||
commit("replaceTaskItem", data);
|
||||
return data;
|
||||
});
|
||||
},
|
||||
|
||||
deleteTaskItems({commit}, payload) {
|
||||
return api.deleteTaskItems(payload.taskList.id, payload.taskItems)
|
||||
.then(() => commit("removeTaskItems", payload));
|
||||
return api.deleteTaskItems(payload.taskList.id, payload.taskItems);
|
||||
},
|
||||
|
||||
completeTaskItems({commit}, payload) {
|
||||
return api.completeTaskItems(payload.taskList.id, payload.taskItems, !payload.completed)
|
||||
.then(() => commit("setTaskItemCompletion", payload));
|
||||
return api.completeTaskItems(payload.taskList.id, payload.taskItems, !payload.completed);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
class TaskItem < ApplicationRecord
|
||||
include DefaultValues
|
||||
|
||||
belongs_to :task_list, touch: true
|
||||
belongs_to :task_list, touch: true, inverse_of: :task_items
|
||||
|
||||
validates :name, presence: true
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
class TaskList < ApplicationRecord
|
||||
|
||||
belongs_to :user
|
||||
has_many :task_items, dependent: :delete_all
|
||||
has_many :task_items, dependent: :delete_all, inverse_of: :task_list
|
||||
|
||||
validates :name,
|
||||
presence: true,
|
||||
|
@ -35,7 +35,7 @@
|
||||
<title>Parsley</title>
|
||||
|
||||
<style type="text/css">
|
||||
|
||||
|
||||
body.loading {
|
||||
background-color: #79A736;
|
||||
}
|
||||
@ -43,14 +43,15 @@
|
||||
#app {
|
||||
transition: opacity 0.1s ease-in;
|
||||
}
|
||||
|
||||
|
||||
body.loading #app {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<%= stylesheet_pack_tag 'application' %>
|
||||
<%= action_cable_meta_tag %>
|
||||
</head>
|
||||
<body class="loading">
|
||||
|
||||
|
@ -53,6 +53,7 @@ module.exports = function(api) {
|
||||
"loose": true
|
||||
}
|
||||
],
|
||||
["@babel/plugin-proposal-private-property-in-object", { "loose": true }],
|
||||
[
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
{
|
||||
@ -75,4 +76,4 @@ module.exports = function(api) {
|
||||
]
|
||||
].filter(Boolean)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ require "action_mailer/railtie"
|
||||
# require "action_mailbox/engine"
|
||||
# require "action_text/engine"
|
||||
require "action_view/railtie"
|
||||
# require "action_cable/engine"
|
||||
require "action_cable/engine"
|
||||
# require "sprockets/railtie"
|
||||
require "rails/test_unit/railtie"
|
||||
|
||||
|
@ -5,5 +5,10 @@ test:
|
||||
adapter: async
|
||||
|
||||
production:
|
||||
adapter: redis
|
||||
url: redis://localhost:6379/1
|
||||
adapter: postgresql
|
||||
|
||||
beta:
|
||||
adapter: postgresql
|
||||
|
||||
docker:
|
||||
adapter: postgresql
|
||||
|
@ -1,9 +1,10 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@rails/webpacker": "5.3.0",
|
||||
"@rails/actioncable": "^6.1.4",
|
||||
"@rails/webpacker": "5.4.3",
|
||||
"@tweenjs/tween.js": "^18.6.4",
|
||||
"autosize": "^4.0.2",
|
||||
"bulma": "^0.8.2",
|
||||
"bulma": "0.9.3",
|
||||
"cheerio": "^1.0.0-rc.9",
|
||||
"css-loader": "^5.2.4",
|
||||
"lodash": "^4.17.21",
|
||||
|
Loading…
Reference in New Issue
Block a user