diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..ded31c0 --- /dev/null +++ b/.babelrc @@ -0,0 +1,18 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": "> 1%", + "uglify": true + }, + "useBuiltIns": true + }] + ], + + "plugins": [ + "syntax-dynamic-import", + "transform-object-rest-spread", + ["transform-class-properties", { "spec": true }] + ] +} diff --git a/.gitignore b/.gitignore index 83117ec..6809ebb 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,8 @@ # Ignore dragonfly uploads /public/system +/public/packs +/public/packs-test +/node_modules +yarn-debug.log* +.yarn-integrity diff --git a/.postcssrc.yml b/.postcssrc.yml new file mode 100644 index 0000000..150dac3 --- /dev/null +++ b/.postcssrc.yml @@ -0,0 +1,3 @@ +plugins: + postcss-import: {} + postcss-cssnext: {} diff --git a/Gemfile b/Gemfile index ca8aaa6..2450b1a 100644 --- a/Gemfile +++ b/Gemfile @@ -16,6 +16,7 @@ gem "puma" gem "sass-rails" gem "simple_form" gem "uglifier" +gem "webpacker" group :doc do gem "sdoc", require: false @@ -30,6 +31,7 @@ end group :test do gem "capybara" gem "launchy" + gem "poltergeist" gem "rspec-rails" gem "shoulda-matchers" gem "webmock" diff --git a/Gemfile.lock b/Gemfile.lock index 98ad65b..105fabc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -59,6 +59,7 @@ GEM rack (>= 1.6.0) rack-test (>= 0.6.3) xpath (~> 3.1) + cliver (0.3.2) coderay (1.1.2) concurrent-ruby (1.0.5) crack (0.4.3) @@ -142,12 +143,18 @@ GEM oauth2 (~> 1.1) omniauth (~> 1.2) pg (1.1.3) + poltergeist (1.18.1) + capybara (>= 2.1, < 4) + cliver (~> 0.3.1) + websocket-driver (>= 0.2.0) pry (0.11.3) coderay (~> 1.1.0) method_source (~> 0.9.0) public_suffix (3.0.3) puma (3.12.0) rack (2.0.5) + rack-proxy (0.6.4) + rack rack-test (1.1.0) rack (>= 1.0, < 3) rails (5.2.1) @@ -236,6 +243,10 @@ GEM addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff + webpacker (3.5.5) + activesupport (>= 4.2) + rack-proxy (>= 0.6.1) + railties (>= 4.2) websocket-driver (0.7.0) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.3) @@ -257,6 +268,7 @@ DEPENDENCIES launchy omniauth-github pg + poltergeist pry puma rails (= 5.2.1) @@ -267,6 +279,7 @@ DEPENDENCIES simple_form uglifier webmock + webpacker RUBY VERSION ruby 2.5.1p57 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js deleted file mode 100644 index 1ca0e3b..0000000 --- a/app/assets/javascripts/application.js +++ /dev/null @@ -1,15 +0,0 @@ -// This is a manifest file that'll be compiled into application.js, which will include all the files -// listed below. -// -// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, -// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. -// -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -// compiled file. -// -// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details -// about supported directives. -// -//= require jquery -//= require jquery_ujs -//= require_tree . diff --git a/app/assets/javascripts/home.js b/app/assets/javascripts/home.js deleted file mode 100644 index 4532725..0000000 --- a/app/assets/javascripts/home.js +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the behaviors and hooks related to the matching controller here. -// All this logic will automatically be available in application.js. -// You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/images.js b/app/assets/javascripts/images.js deleted file mode 100644 index 4532725..0000000 --- a/app/assets/javascripts/images.js +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the behaviors and hooks related to the matching controller here. -// All this logic will automatically be available in application.js. -// You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/sessions.js b/app/assets/javascripts/sessions.js deleted file mode 100644 index 4532725..0000000 --- a/app/assets/javascripts/sessions.js +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the behaviors and hooks related to the matching controller here. -// All this logic will automatically be available in application.js. -// You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/images/show.scss b/app/assets/stylesheets/images/show.scss index a94dcc5..c2ca734 100644 --- a/app/assets/stylesheets/images/show.scss +++ b/app/assets/stylesheets/images/show.scss @@ -14,7 +14,7 @@ grid-area: image-info; } - .image-tags { + .image-tags-list { list-style: none; padding-left: 0; } @@ -27,6 +27,10 @@ color: #333; margin: 0.25rem 0.125rem; padding: 0.125rem 0.5rem; + + &.hidden { + display: none; + } } img { diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index dd8446b..0dee1df 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -4,7 +4,7 @@ class TagsController < ApplicationController def create image = @current_user.images.find(params[:image_id]) tag = params[:tag] - image.tags << tag unless image.tags.include? tag + image.tags << tag unless image.tags.include?(tag) image.save respond_to do |format| diff --git a/app/javascript/controllers/tag_controller.js b/app/javascript/controllers/tag_controller.js new file mode 100644 index 0000000..35a9d09 --- /dev/null +++ b/app/javascript/controllers/tag_controller.js @@ -0,0 +1,31 @@ +import { Controller } from "stimulus"; +import ajaxService from "../services/ajax_service"; + +export default class extends Controller { + static targets = [ "name" ]; + + connect() { + console.log("Connected to our tags"); + } + + disconnect() { + console.log("Bye bye tag!"); + } + + delete(event) { + const imageId = this.element.dataset.imageId; + const tag = this.nameTarget.innerText; + + event.preventDefault(); + + this.element.classList.add("hidden"); + + ajaxService.deleteTag(tag) + .then(response => { + this.element.parentElement.removeChild(this.element); + }) + .catch(error => { + this.element.classList.remove("hidden") + }); + } +} diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js new file mode 100644 index 0000000..a04b5aa --- /dev/null +++ b/app/javascript/packs/application.js @@ -0,0 +1,15 @@ +/* eslint no-console:0 */ +// This file is automatically compiled by Webpack, along with any other files +// present in this directory. You're encouraged to place your actual application logic in +// a relevant structure within app/javascript and only use these pack files to reference +// that code so it'll be compiled. +// +// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate +// layout file, like app/views/layouts/application.html.erb + +import { Application } from "stimulus"; +import { definitionsFromContext } from "stimulus/webpack-helpers"; + +const application = Application.start(); +const context = require.context("controllers", true, /.js$/); +application.load(definitionsFromContext(context)); diff --git a/app/javascript/services/ajax_service.js b/app/javascript/services/ajax_service.js new file mode 100644 index 0000000..2d7e395 --- /dev/null +++ b/app/javascript/services/ajax_service.js @@ -0,0 +1,29 @@ +import axios from "axios"; + +const csrfToken = () => { + return document + .getElementsByName("csrf-token")[0] + .attributes["content"] + .value; +} + +const imageId = () => { + return document + .getElementsByClassName("show-image")[0] + .attributes["data-image-id"] + .value; +} + +const config = () => ({ + headers: { "X-CSRF-Token": csrfToken() } +}); + + +export default { + createTag: (tag) => { + }, + + deleteTag: (tag) => { + return axios.delete(`/user/images/${imageId()}/tags/${tag}.json`, config()); + } +} diff --git a/app/views/images/_tag.html.erb b/app/views/images/_tag.html.erb new file mode 100644 index 0000000..1f3289d --- /dev/null +++ b/app/views/images/_tag.html.erb @@ -0,0 +1,10 @@ +
  • + <%= tag %> + <%= link_to "×".html_safe, + user_image_tag_path(@image, tag), + method: :delete, + data: { action: "click->tag#delete" }, + class: "delete-tag" %> +
  • diff --git a/app/views/images/show.html.erb b/app/views/images/show.html.erb index 7ad4c66..6080784 100644 --- a/app/views/images/show.html.erb +++ b/app/views/images/show.html.erb @@ -4,19 +4,23 @@ <%= @image.image.name %> <% end %> -
    +
    <%= render "image", image: @image %>
    - <%= render "tags/tags", image: @image, tags: @image.tags %> +
    + <%= render "tags/tags", image: @image, tags: @image.tags %> -
    - <%= form_tag user_image_tags_path(@image), remote: true, method: :post do %> - <%= text_field_tag :tag %> - <%= submit_tag "Add Tag" %> - <% end %> +
    + <%= form_tag user_image_tags_path(@image), + remote: true, + method: :post do %> + <%= text_field_tag :tag %> + <%= submit_tag "Add Tag" %> + <% end %> +
    diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 1a1747d..f627d95 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -3,8 +3,8 @@ My Images - <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> - <%= javascript_include_tag "application", "data-turbolinks-track" => true %> + <%= stylesheet_link_tag "application", media: "all" %> + <%= javascript_pack_tag "application" %> <%= csrf_meta_tags %> diff --git a/app/views/tags/_tags.html.erb b/app/views/tags/_tags.html.erb index 2e7f0f2..9718f7a 100644 --- a/app/views/tags/_tags.html.erb +++ b/app/views/tags/_tags.html.erb @@ -1,4 +1,4 @@ -