mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-17 09:46:22 +00:00
Merge remote-tracking branch 'origin/rails' into prosemirror
This commit is contained in:
commit
aaaf49fd04
28 changed files with 338 additions and 203 deletions
3
app/assets/images/arrows-alt-v.svg
Normal file
3
app/assets/images/arrows-alt-v.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<!-- CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/
|
||||
https://github.com/FortAwesome/Font-Awesome/blob/master/svgs/solid/arrows-alt-v.svg -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><path d="M214.059 377.941H168V134.059h46.059c21.382 0 32.09-25.851 16.971-40.971L144.971 7.029c-9.373-9.373-24.568-9.373-33.941 0L24.971 93.088c-15.119 15.119-4.411 40.971 16.971 40.971H88v243.882H41.941c-21.382 0-32.09 25.851-16.971 40.971l86.059 86.059c9.373 9.373 24.568 9.373 33.941 0l86.059-86.059c15.12-15.119 4.412-40.971-16.97-40.971z"/></svg>
|
After Width: | Height: | Size: 571 B |
|
@ -2,4 +2,5 @@
|
|||
//= require turbolinks
|
||||
//= require zepto/dist/zepto.min.js
|
||||
//= require input-tag/input-tag.js
|
||||
//= require table-dragger/dist/table-dragger
|
||||
//= require_tree .
|
||||
|
|
14
app/assets/javascripts/order.js
Normal file
14
app/assets/javascripts/order.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
$(document).on('turbolinks:load', function() {
|
||||
var table = document.querySelector('.table-draggable');
|
||||
|
||||
if (table == null) return;
|
||||
|
||||
tableDragger(table, {
|
||||
mode: 'row',
|
||||
onlyBody: true,
|
||||
dragHandler: '.handle'
|
||||
}).on('drop', function(from, to, el, mode) {
|
||||
$('.reorder').val(function(i,v) { return i; });
|
||||
$('.submit-reorder').removeClass('d-none');
|
||||
});
|
||||
});
|
|
@ -236,6 +236,12 @@ svg {
|
|||
}
|
||||
}
|
||||
|
||||
.handle {
|
||||
img {
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.sutty-editor-prosemirror {
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
|
|
|
@ -9,17 +9,10 @@ class PostsController < ApplicationController
|
|||
authorize Post
|
||||
@site = find_site
|
||||
@category = session[:category] = params.dig(:category)
|
||||
@layout = params.dig(:layout).try :to_sym
|
||||
# TODO: Aplicar policy_scope
|
||||
@posts = @site.posts(lang: I18n.locale)
|
||||
|
||||
if params[:sort_by].present?
|
||||
begin
|
||||
@posts.sort_by! do |p|
|
||||
p.send(params[:sort_by].to_s)
|
||||
end
|
||||
rescue ArgumentError
|
||||
end
|
||||
end
|
||||
@posts = @site.posts(lang: I18n.locale)
|
||||
@posts.sort_by! :order, :date
|
||||
end
|
||||
|
||||
def show
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class MetadataDate < MetadataTemplate
|
||||
def default_value
|
||||
Date.today
|
||||
end
|
||||
end
|
||||
|
|
106
app/models/metadata_file.rb
Normal file
106
app/models/metadata_file.rb
Normal file
|
@ -0,0 +1,106 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Define un campo de archivo
|
||||
class MetadataFile < MetadataTemplate
|
||||
# Una ruta vacía a la imagen con una descripción vacía
|
||||
def default_value
|
||||
{ 'path' => nil, 'description' => nil }
|
||||
end
|
||||
|
||||
def empty?
|
||||
value == default_value
|
||||
end
|
||||
|
||||
def validate
|
||||
super
|
||||
|
||||
errors << I18n.t('metadata.image.path_required') if path_missing?
|
||||
|
||||
errors.compact!
|
||||
errors.empty?
|
||||
end
|
||||
|
||||
# Determina si necesitamos la imagen pero no la tenemos
|
||||
def path_missing?
|
||||
required && !value['path'].blank?
|
||||
end
|
||||
|
||||
# Determina si el archivo ya fue subido
|
||||
def uploaded?
|
||||
value['path'].is_a?(String)
|
||||
end
|
||||
|
||||
# Determina si la ruta es opcional pero deja pasar si la ruta se
|
||||
# especifica
|
||||
def path_optional?
|
||||
!required && value['path'].blank?
|
||||
end
|
||||
|
||||
# Asociar la imagen subida al sitio y obtener la ruta
|
||||
def save
|
||||
return true if uploaded?
|
||||
return true if path_optional?
|
||||
return false unless hardlink.zero?
|
||||
|
||||
# Modificar el valor actual
|
||||
value['path'] = relative_destination_path
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def to_param
|
||||
{ name => %i[description path] }
|
||||
end
|
||||
|
||||
# Almacena el archivo en el sitio y lo devuelve
|
||||
#
|
||||
# @return ActiveStorage::Attachment
|
||||
def static_file
|
||||
ActiveRecord::Base.connection_pool.with_connection do
|
||||
if uploaded?
|
||||
blob = ActiveStorage::Blob.find_by(key: key_from_path)
|
||||
@static_file ||= site.static_files.find_by(blob_id: blob.id)
|
||||
elsif site.static_files.attach(value['path'])
|
||||
@static_file ||= site.static_files.last
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def key_from_path
|
||||
# XXX: No podemos usar self#extension porque en este punto todavía
|
||||
# no sabemos el static_file
|
||||
File.basename(value['path'], '.*')
|
||||
end
|
||||
|
||||
# Hacemos un link duro para colocar el archivo dentro del repositorio
|
||||
# y no duplicar el espacio que ocupan. Esto requiere que ambos
|
||||
# directorios estén dentro del mismo punto de montaje.
|
||||
def hardlink
|
||||
FileUtils.mkdir_p File.dirname(destination_path)
|
||||
FileUtils.ln uploaded_path, destination_path
|
||||
end
|
||||
|
||||
def extension
|
||||
static_file.blob.content_type.split('/').last
|
||||
end
|
||||
|
||||
# Obtener la ruta al archivo
|
||||
# https://stackoverflow.com/a/53908358
|
||||
def uploaded_relative_path
|
||||
ActiveStorage::Blob.service.path_for(static_file.key)
|
||||
end
|
||||
|
||||
def uploaded_path
|
||||
Rails.root.join uploaded_relative_path
|
||||
end
|
||||
|
||||
def relative_destination_path
|
||||
File.join('public', [static_file.key, extension].join('.'))
|
||||
end
|
||||
|
||||
def destination_path
|
||||
File.join(site.path, relative_destination_path)
|
||||
end
|
||||
end
|
|
@ -1,37 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Define un campo de imagen
|
||||
# TODO: Validar que sea una imagen
|
||||
class MetadataImage < MetadataTemplate
|
||||
# Una ruta vacía a la imagen con una descripción vacía
|
||||
def default_value
|
||||
{ 'path' => nil, 'description' => nil }
|
||||
end
|
||||
|
||||
def empty?
|
||||
value == default_value
|
||||
end
|
||||
|
||||
class MetadataImage < MetadataFile
|
||||
def validate
|
||||
super
|
||||
|
||||
errors << I18n.t('metadata.image.path_required') if path_missing?
|
||||
errors << I18n.t('metadata.image.not_an_image') unless image?
|
||||
|
||||
errors.compact!
|
||||
errors.empty?
|
||||
end
|
||||
|
||||
# Determina si necesitamos la imagen pero no la tenemos
|
||||
def path_missing?
|
||||
required && !value['path'].blank?
|
||||
end
|
||||
|
||||
# Determina si el archivo ya fue subido
|
||||
def uploaded?
|
||||
value['path'].is_a?(String)
|
||||
end
|
||||
|
||||
# Determina si es una imagen antes de subirla
|
||||
def image?
|
||||
if value['path'].is_a? ActionDispatch::Http::UploadedFile
|
||||
|
@ -44,78 +23,4 @@ class MetadataImage < MetadataTemplate
|
|||
true
|
||||
end
|
||||
end
|
||||
|
||||
# Determina si la ruta es opcional pero deja pasar si la ruta se
|
||||
# especifica
|
||||
def path_optional?
|
||||
!required && value['path'].blank?
|
||||
end
|
||||
|
||||
# Asociar la imagen subida al sitio y obtener la ruta
|
||||
def save
|
||||
return true if uploaded?
|
||||
return true if path_optional?
|
||||
return false unless hardlink.zero?
|
||||
|
||||
# Modificar el valor actual
|
||||
value['path'] = relative_destination_path
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def to_param
|
||||
{ name => %i[description path] }
|
||||
end
|
||||
|
||||
# Almacena el archivo en el sitio y lo devuelve
|
||||
#
|
||||
# @return ActiveStorage::Attachment
|
||||
def static_file
|
||||
ActiveRecord::Base.connection_pool.with_connection do
|
||||
if uploaded?
|
||||
blob = ActiveStorage::Blob.find_by(key: key_from_path)
|
||||
@static_file ||= site.static_files.find_by(blob_id: blob.id)
|
||||
elsif site.static_files.attach(value['path'])
|
||||
@static_file ||= site.static_files.last
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def key_from_path
|
||||
# XXX: No podemos usar self#extension porque en este punto todavía
|
||||
# no sabemos el static_file
|
||||
File.basename(value['path'], '.*')
|
||||
end
|
||||
|
||||
# Hacemos un link duro para colocar el archivo dentro del repositorio
|
||||
# y no duplicar el espacio que ocupan. Esto requiere que ambos
|
||||
# directorios estén dentro del mismo punto de montaje.
|
||||
def hardlink
|
||||
FileUtils.mkdir_p File.dirname(destination_path)
|
||||
FileUtils.ln uploaded_path, destination_path
|
||||
end
|
||||
|
||||
def extension
|
||||
static_file.blob.content_type.split('/').last
|
||||
end
|
||||
|
||||
# Obtener la ruta al archivo
|
||||
# https://stackoverflow.com/a/53908358
|
||||
def uploaded_relative_path
|
||||
ActiveStorage::Blob.service.path_for(static_file.key)
|
||||
end
|
||||
|
||||
def uploaded_path
|
||||
Rails.root.join uploaded_relative_path
|
||||
end
|
||||
|
||||
def relative_destination_path
|
||||
File.join('public', [static_file.key, extension].join('.'))
|
||||
end
|
||||
|
||||
def destination_path
|
||||
File.join(site.path, relative_destination_path)
|
||||
end
|
||||
end
|
||||
|
|
10
app/models/metadata_order.rb
Normal file
10
app/models/metadata_order.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Un campo de orden
|
||||
class MetadataOrder < MetadataTemplate
|
||||
# El valor según la posición del post en la relación, siguiendo el
|
||||
# orden cronológico inverso
|
||||
def default_value
|
||||
site.posts(lang: post.lang.value).index(post)
|
||||
end
|
||||
end
|
4
app/models/metadata_url.rb
Normal file
4
app/models/metadata_url.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Un campo de URL
|
||||
class MetadataUrl < MetadataString; end
|
|
@ -60,6 +60,10 @@ class Post < OpenStruct
|
|||
path.basename
|
||||
end
|
||||
|
||||
def sha1
|
||||
Digest::SHA1.hexdigest id
|
||||
end
|
||||
|
||||
# Levanta un error si al construir el artículo no pasamos un atributo.
|
||||
def default_attributes_missing(**args)
|
||||
DEFAULT_ATTRIBUTES.each do |attr|
|
||||
|
|
|
@ -31,11 +31,28 @@ class PostRelation < Array
|
|||
post
|
||||
end
|
||||
|
||||
alias sort_by_generic! sort_by!
|
||||
|
||||
# Permite ordenar los artículos por sus atributos
|
||||
#
|
||||
# XXX: Prestar atención cuando estamos mezclando artículos con
|
||||
# diferentes tipos de atributos.
|
||||
def sort_by!(*attrs)
|
||||
sort_by_generic! do |post|
|
||||
attrs.map do |attr|
|
||||
return 0 unless post.attributes.include? attr
|
||||
|
||||
post.public_send(attr).value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias find_generic find
|
||||
|
||||
def find(id)
|
||||
# Encontra un post por su id convertido a SHA1
|
||||
def find(id, sha1: false)
|
||||
find_generic do |p|
|
||||
p.id == id
|
||||
p.sha1 == (sha1 ? id : Digest::SHA1.hexdigest(id))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -39,26 +39,21 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
|||
post
|
||||
end
|
||||
|
||||
# Reordena todos los posts que soporten orden de acuerdo a un array
|
||||
# con las nuevas posiciones. La posición actual la da la posición en
|
||||
# Reordena todos los posts que soporten orden de acuerdo a un hash de
|
||||
# ids y nuevas posiciones. La posición actual la da la posición en
|
||||
# el array.
|
||||
#
|
||||
# [ 1, 0, 2 ] => mover el elemento 2 a la posición 1, mantener 3
|
||||
# { sha1 => 2, sha1 => 1, sha1 => 0 }
|
||||
def reorder
|
||||
posts = site.posts(lang: lang)
|
||||
reorder = params.require(:post).permit(reorder: [])
|
||||
.try(:[], :reorder)
|
||||
.try(:map, &:to_i) || []
|
||||
reorder = params.require(:post).permit(reorder: {}).try(:[], :reorder)
|
||||
|
||||
# Tenemos que pasar un array con la misma cantidad de elementos
|
||||
return false if reorder.size != posts.size
|
||||
|
||||
files = reorder.map.with_index do |new, cur|
|
||||
post = posts[cur]
|
||||
files = reorder.keys.map do |id|
|
||||
post = posts.find(id, sha1: true)
|
||||
next unless post.attributes.include? :order
|
||||
|
||||
post.usuaries << usuarie
|
||||
post.order.value = new
|
||||
post.order.value = reorder[id].to_i
|
||||
post.path.absolute
|
||||
end.compact
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.sutty-editor.sutty-editor-prosemirror{'data-sutty-identifier' => identifier}
|
||||
%textarea.form-control.sutty-editor.sutty-editor-content{rows: '8',
|
||||
'data-sutty-identifier': identifier,
|
||||
name: name}= markdown
|
||||
.sutty-editor.sutty-editor-prosemirror{ 'data-sutty-identifier': identifier }
|
||||
%textarea.form-control.sutty-editor.sutty-editor-content{ rows: 8,
|
||||
'data-sutty-identifier': identifier, name: name }
|
||||
= markdown
|
||||
|
|
7
app/views/posts/attribute_ro/_file.haml
Normal file
7
app/views/posts/attribute_ro/_file.haml
Normal file
|
@ -0,0 +1,7 @@
|
|||
%tr{ id: attribute }
|
||||
%th= post_label_t(attribute, :path, post: post)
|
||||
%td
|
||||
- if metadata.value['path'].present?
|
||||
%figure
|
||||
= link_to url_for(metadata.static_file)
|
||||
%figcaption= metadata.value['description']
|
3
app/views/posts/attribute_ro/_order.haml
Normal file
3
app/views/posts/attribute_ro/_order.haml
Normal file
|
@ -0,0 +1,3 @@
|
|||
%tr{ id: attribute }
|
||||
%th= post_label_t(attribute, post: post)
|
||||
%td= metadata.value
|
3
app/views/posts/attribute_ro/_url.haml
Normal file
3
app/views/posts/attribute_ro/_url.haml
Normal file
|
@ -0,0 +1,3 @@
|
|||
%tr{ id: attribute }
|
||||
%th= post_label_t(attribute, post: post)
|
||||
%td= link_to metadata.value
|
22
app/views/posts/attributes/_file.haml
Normal file
22
app/views/posts/attributes/_file.haml
Normal file
|
@ -0,0 +1,22 @@
|
|||
.form-group
|
||||
- if metadata.uploaded?
|
||||
= hidden_field_tag "post[#{attribute}][path]", metadata.value['path']
|
||||
|
||||
.custom-file
|
||||
= file_field(*field_name_for('post', attribute, :path),
|
||||
**field_options(attribute, metadata),
|
||||
class: "custom-file-input #{invalid(post, attribute)}",
|
||||
data: { preview: "#{attribute}-preview" })
|
||||
= label_tag "post_#{attribute}_path",
|
||||
post_label_t(attribute, :path, post: post), class: 'custom-file-label'
|
||||
= render 'posts/attribute_feedback',
|
||||
post: post, attribute: [attribute, :path], metadata: metadata
|
||||
|
||||
.form-group
|
||||
= label_tag "post_#{attribute}_description",
|
||||
post_label_t(attribute, :description, post: post)
|
||||
= text_field(*field_name_for('post', attribute, :description),
|
||||
value: metadata.value['description'],
|
||||
**field_options(attribute, metadata))
|
||||
= render 'posts/attribute_feedback',
|
||||
post: post, attribute: [attribute, :description], metadata: metadata
|
1
app/views/posts/attributes/_order.haml
Normal file
1
app/views/posts/attributes/_order.haml
Normal file
|
@ -0,0 +1 @@
|
|||
-# nada
|
6
app/views/posts/attributes/_url.haml
Normal file
6
app/views/posts/attributes/_url.haml
Normal file
|
@ -0,0 +1,6 @@
|
|||
.form-group
|
||||
= label_tag "post_#{attribute}", post_label_t(attribute, post: post)
|
||||
= url_field 'post', attribute, value: metadata.value,
|
||||
**field_options(attribute, metadata)
|
||||
= render 'posts/attribute_feedback',
|
||||
post: post, attribute: attribute, metadata: metadata
|
|
@ -25,42 +25,59 @@
|
|||
- if @posts.present?
|
||||
.row
|
||||
.col
|
||||
%table.table.table-condensed
|
||||
%tbody
|
||||
- @posts.each do |post|
|
||||
- next unless policy(post).show?
|
||||
-#
|
||||
saltearse el post a menos que esté en la categoría por
|
||||
la que estamos filtrando
|
||||
- if @category
|
||||
- next unless post.attributes.include? :categories
|
||||
- next unless post.categories.value.include?(@category)
|
||||
%tr
|
||||
%td
|
||||
= link_to post.title.value,
|
||||
site_post_path(@site, post.id)
|
||||
- if post.attributes.include? :draft
|
||||
- if post.draft.value
|
||||
%span.badge.badge-primary
|
||||
= post_label_t(:draft, post: post)
|
||||
- if post.attributes.include? :categories
|
||||
- unless post.categories.value.empty?
|
||||
%br
|
||||
%small
|
||||
- post.categories.value.each do |c|
|
||||
= link_to c, site_posts_path(@site, category: c)
|
||||
= form_tag site_posts_reorder_path, method: :post do
|
||||
= submit_tag t('posts.reorder'), class: 'btn submit-reorder d-none'
|
||||
-# TODO: Permitir cambiar el idioma
|
||||
%table.table.table-condensed.table-draggable
|
||||
%tbody
|
||||
- @posts.each_with_index do |post, i|
|
||||
-#
|
||||
saltearse el post a menos que esté en la categoría por
|
||||
la que estamos filtrando
|
||||
- if @category
|
||||
- next unless post.attributes.include? :categories
|
||||
- next unless post.categories.value.include?(@category)
|
||||
- if @layout
|
||||
- next unless post.layout.name == @layout
|
||||
- next unless policy(post).show?
|
||||
%tr
|
||||
%td
|
||||
.handle
|
||||
= image_tag 'arrows-alt-v.svg'
|
||||
= hidden_field 'post[reorder]', post.sha1,
|
||||
value: i, class: 'reorder'
|
||||
%td
|
||||
%small
|
||||
= link_to post.layout.name.to_s.humanize,
|
||||
site_posts_path(@site, layout: post.layout.name)
|
||||
%br/
|
||||
= link_to post.title.value,
|
||||
site_post_path(@site, post.id)
|
||||
- if post.attributes.include? :draft
|
||||
- if post.draft.value
|
||||
%span.badge.badge-primary
|
||||
= post_label_t(:draft, post: post)
|
||||
- if post.attributes.include? :categories
|
||||
- unless post.categories.value.empty?
|
||||
%br
|
||||
%small
|
||||
- post.categories.value.each do |c|
|
||||
= link_to c, site_posts_path(@site, category: c)
|
||||
|
||||
%td= post.date.value.strftime('%F')
|
||||
%td
|
||||
- if policy(post).edit?
|
||||
= link_to t('posts.edit'),
|
||||
edit_site_post_path(@site, post.id),
|
||||
class: 'btn'
|
||||
- if policy(post).destroy?
|
||||
= link_to t('posts.destroy'),
|
||||
site_post_path(@site, post.id),
|
||||
class: 'btn',
|
||||
method: :delete,
|
||||
data: { confirm: t('posts.confirm_destroy') }
|
||||
%td
|
||||
= post.date.value.strftime('%F')
|
||||
%br/
|
||||
= post.try(:order).try(:value)
|
||||
%td
|
||||
- if policy(post).edit?
|
||||
= link_to t('posts.edit'),
|
||||
edit_site_post_path(@site, post.id),
|
||||
class: 'btn'
|
||||
- if policy(post).destroy?
|
||||
= link_to t('posts.destroy'),
|
||||
site_post_path(@site, post.id),
|
||||
class: 'btn',
|
||||
method: :delete,
|
||||
data: { confirm: t('posts.confirm_destroy') }
|
||||
- else
|
||||
%h2= t('posts.none')
|
||||
|
|
|
@ -10,7 +10,7 @@ require 'active_record/railtie'
|
|||
require 'active_storage/engine'
|
||||
require 'action_controller/railtie'
|
||||
require 'action_mailer/railtie'
|
||||
require 'action_mailbox/engine'
|
||||
# require 'action_mailbox/engine'
|
||||
require 'action_text/engine'
|
||||
require 'action_view/railtie'
|
||||
# require "action_cable/engine"
|
||||
|
|
|
@ -347,7 +347,7 @@ en:
|
|||
required:
|
||||
label: ' (required)'
|
||||
feedback: 'This field cannot be empty!'
|
||||
reorder_posts: 'Reorder posts'
|
||||
reorder: 'Reorder posts'
|
||||
sort:
|
||||
by: 'Sort by'
|
||||
order: 'order'
|
||||
|
|
|
@ -357,7 +357,7 @@ es:
|
|||
required:
|
||||
label: ' (requerido)'
|
||||
feedback: '¡Este campo no puede estar vacío!'
|
||||
reorder_posts: 'Reordenar artículos'
|
||||
reorder: 'Reordenar artículos'
|
||||
sort:
|
||||
by: 'Ordenar por'
|
||||
order: 'posición'
|
||||
|
@ -366,7 +366,7 @@ es:
|
|||
content: 'Cuerpo del artículo'
|
||||
categories: 'Todos'
|
||||
dropdown: 'Desplegar el menú'
|
||||
new: 'Empezar un artículo nuevo'
|
||||
new: 'Crear artículo'
|
||||
new_with_template: 'Comenzar %{template}'
|
||||
index: 'Artículos'
|
||||
edit: 'Editar'
|
||||
|
|
|
@ -13,3 +13,13 @@ Como el orden es un metadato, tenemos que ignorar los tipos de posts que
|
|||
no lo tienen
|
||||
|
||||
El orden por defecto es orden natural, más bajo es más alto.
|
||||
|
||||
El problema que tiene esta implementación es que al reordenar los posts
|
||||
necesitamos mantener el orden original sobre el que estabamos ordenando,
|
||||
sino terminamos aplicando un orden incorrecto. Esto requiere que
|
||||
implementemos una forma de mantener el orden entre sesiones e incluso
|
||||
general. Nos vendría bien para no tener que recargar el sitio una y
|
||||
otra vez.
|
||||
|
||||
Lo más controlado sería enviar exactamente el id del post con su nueva
|
||||
ubicación en el orden. Esta es la implementación anterior.
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
"@rails/webpacker": "^4.0.7",
|
||||
"commonmark": "^0.29.0",
|
||||
"input-tag": "https://0xacab.org/sutty/input-tag.git",
|
||||
"table-dragger": "https://0xacab.org/sutty/table-dragger.git",
|
||||
"prosemirror-gapcursor": "^1.0.4",
|
||||
"prosemirror-inputrules": "^1.0.4",
|
||||
"prosemirror-markdown": "^1.3.1",
|
||||
"prosemirror-model": "^1.7.4",
|
||||
"prosemirror-state": "^1.2.4",
|
||||
"prosemirror-view": "^1.11.7",
|
||||
"table-dragger": "^1.0.2",
|
||||
"zepto": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -145,7 +145,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
|||
test 'se pueden reordenar' do
|
||||
lang = I18n.available_locales.sample
|
||||
posts = @site.posts(lang: lang)
|
||||
reorder = posts.each_index.to_a.shuffle
|
||||
reorder = Hash[posts.map(&:sha1).shuffle.each_with_index.to_a]
|
||||
|
||||
post site_posts_reorder_url(@site),
|
||||
headers: @authorization,
|
||||
|
@ -156,6 +156,8 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
|||
assert_equal I18n.t('post_service.reorder'),
|
||||
@site.repository.rugged.head.target.message
|
||||
assert_equal reorder,
|
||||
@site.posts(lang: lang).map(&:order).map(&:value)
|
||||
Hash[@site.posts(lang: lang).map do |p|
|
||||
[p.sha1, p.order.value]
|
||||
end]
|
||||
end
|
||||
end
|
||||
|
|
75
yarn.lock
75
yarn.lock
|
@ -579,6 +579,14 @@
|
|||
"@babel/helper-regex" "^7.4.4"
|
||||
regexpu-core "^4.5.4"
|
||||
|
||||
"@babel/polyfill@^7.4.0":
|
||||
version "7.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.7.0.tgz#e1066e251e17606ec7908b05617f9b7f8180d8f3"
|
||||
integrity sha512-/TS23MVvo34dFmf8mwCisCbWGrfhbiWZSwBo6HkADTBhUa2Q/jWltyY/tpofz/b6/RIhqaqQcquptCirqIhOaQ==
|
||||
dependencies:
|
||||
core-js "^2.6.5"
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
"@babel/preset-env@^7.4.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.5.tgz#bc470b53acaa48df4b8db24a570d6da1fef53c9a"
|
||||
|
@ -1192,13 +1200,6 @@ babel-plugin-macros@^2.5.0:
|
|||
cosmiconfig "^5.2.0"
|
||||
resolve "^1.10.0"
|
||||
|
||||
babel-runtime@^6.20.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||
dependencies:
|
||||
core-js "^2.4.0"
|
||||
regenerator-runtime "^0.11.0"
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
|
@ -1875,9 +1876,15 @@ core-js-compat@^3.1.1:
|
|||
browserslist "^4.6.6"
|
||||
semver "^6.3.0"
|
||||
|
||||
core-js@^2.4.0:
|
||||
version "2.5.5"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.5.tgz#b14dde936c640c0579a6b50cabcc132dd6127e3b"
|
||||
core-js@^2.6.5:
|
||||
version "2.6.10"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.10.tgz#8a5b8391f8cc7013da703411ce5b585706300d7f"
|
||||
integrity sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==
|
||||
|
||||
core-js@^3.0.1:
|
||||
version "3.3.6"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.3.6.tgz#6ad1650323c441f45379e176ed175c0d021eac92"
|
||||
integrity sha512-u4oM8SHwmDuh5mWZdDg9UwNVq5s1uqq6ZDLLIs07VY+VJU91i3h4f3K/pgFvtUQPGdeStrZ+odKyfyt4EnKHfA==
|
||||
|
||||
core-js@^3.1.3:
|
||||
version "3.2.1"
|
||||
|
@ -1955,12 +1962,6 @@ crossvent@1.5.4:
|
|||
dependencies:
|
||||
custom-event "1.0.0"
|
||||
|
||||
crossvent@^1.5.5:
|
||||
version "1.5.5"
|
||||
resolved "https://registry.yarnpkg.com/crossvent/-/crossvent-1.5.5.tgz#ad20878e4921e9be73d9d6976f8b2ecd0f71a0b1"
|
||||
dependencies:
|
||||
custom-event "^1.0.0"
|
||||
|
||||
crypto-browserify@^3.11.0:
|
||||
version "3.12.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
|
||||
|
@ -2172,10 +2173,6 @@ custom-event@1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.0.tgz#2e4628be19dc4b214b5c02630c5971e811618062"
|
||||
|
||||
custom-event@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
|
||||
|
||||
cyclist@~0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
|
||||
|
@ -2401,9 +2398,10 @@ dot-prop@^4.1.1:
|
|||
dependencies:
|
||||
is-obj "^1.0.0"
|
||||
|
||||
dragula-with-animation@^3.7.2:
|
||||
dragula@^3.7.0:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/dragula-with-animation/-/dragula-with-animation-3.7.2.tgz#9b992db2a274324325c70bb0cb752e77e1faa8af"
|
||||
resolved "https://registry.yarnpkg.com/dragula/-/dragula-3.7.2.tgz#4a35c9d3981ffac1a949c29ca7285058e87393ce"
|
||||
integrity sha1-SjXJ05gf+sGpScKcpyhQWOhzk84=
|
||||
dependencies:
|
||||
contra "1.9.4"
|
||||
crossvent "1.5.4"
|
||||
|
@ -5777,6 +5775,11 @@ querystringify@^2.1.1:
|
|||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
|
||||
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
|
||||
|
||||
ramda@^0.26.1:
|
||||
version "0.26.1"
|
||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
|
||||
integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
|
||||
|
||||
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||
|
@ -5892,10 +5895,6 @@ regenerate@^1.4.0:
|
|||
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
|
||||
integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
|
||||
|
||||
regenerator-runtime@^0.11.0:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||
|
||||
regenerator-runtime@^0.13.2:
|
||||
version "0.13.3"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
|
||||
|
@ -6094,9 +6093,12 @@ run-queue@^1.0.0, run-queue@^1.0.3:
|
|||
dependencies:
|
||||
aproba "^1.1.1"
|
||||
|
||||
rx@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
|
||||
rxjs@^6.5.2:
|
||||
version "6.5.3"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a"
|
||||
integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
|
@ -6703,14 +6705,15 @@ svgo@^1.0.0:
|
|||
unquote "~1.1.1"
|
||||
util.promisify "~1.0.0"
|
||||
|
||||
table-dragger@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/table-dragger/-/table-dragger-1.0.2.tgz#981f46c62fd2899b3fc5e644055ea72831949707"
|
||||
"table-dragger@https://0xacab.org/sutty/table-dragger.git":
|
||||
version "2.0.2"
|
||||
resolved "https://0xacab.org/sutty/table-dragger.git#a5199975398dca9b3a849f5d56220fd11e68733c"
|
||||
dependencies:
|
||||
babel-runtime "^6.20.0"
|
||||
crossvent "^1.5.5"
|
||||
dragula-with-animation "^3.7.2"
|
||||
rx "^4.1.0"
|
||||
"@babel/polyfill" "^7.4.0"
|
||||
core-js "^3.0.1"
|
||||
dragula "^3.7.0"
|
||||
ramda "^0.26.1"
|
||||
rxjs "^6.5.2"
|
||||
|
||||
tapable@^1.0.0, tapable@^1.1.3:
|
||||
version "1.1.3"
|
||||
|
|
Loading…
Reference in a new issue