diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2649c3ba..dceb1a42 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,9 +6,15 @@ - paths: - "vendor/ruby" - ".bundle" + key: + files: + - "Gemfile.lock" .cache-node: &cache-node - paths: - "node_modules" + key: + files: + - "yarn.lock" .cache-task: &cache-task - paths: - ".task" @@ -90,7 +96,7 @@ rubocop: - *apk-add - *disable-hainish script: - - "./bin/modified_files | ./bin/with_extension rb | xargs -r go-task bundle -- exec rubocop" + - "go-task rubocop" haml: stage: "test" cache: @@ -101,4 +107,4 @@ haml: - *apk-add - *disable-hainish script: - - "./bin/modified_files | ./bin/with_extension haml | xargs -r go-task bundle -- exec haml-lint" + - "go-task haml-lint" diff --git a/Taskfile.yaml b/Taskfile.yaml index c2d72472..796ab721 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -183,3 +183,15 @@ tasks: - "{{.HAINISH}} gem install bundler-audit" status: - "test -f ../hain/usr/bin/bundler-audit" + rubocop: + desc: "Ruby linting" + deps: + - "gems" + cmds: + - "./bin/modified_files | ./bin/with_extension rb | xargs -r {{.HAINISH}} bundle exec rubocop {{.CLI_ARGS}}" + haml-lint: + desc: "HAML linting" + deps: + - "gems" + cmds: + - "./bin/modified_files | ./bin/with_extension haml | xargs -r {{.HAINISH}} bundle exec haml-lint {{.CLI_ARGS}}" diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index b4a41f99..88287d9e 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -577,3 +577,27 @@ $bezier: cubic-bezier(0.75, 0, 0.25, 1); } } } +// details styles + +.details { + summary { + list-style: none; + cursor: default; + position: relative; + } + summary::after { + content: '▶'; + font-size: 1.8rem; + position: absolute; + left: 97%; + bottom: 3%; + transform: rotate(180deg); + } + &[open] { + & > summary { + &::after { + transform: rotate(90deg); + } + } + } +} diff --git a/app/assets/stylesheets/dark.scss b/app/assets/stylesheets/dark.scss index 59e15180..f7f3a09d 100644 --- a/app/assets/stylesheets/dark.scss +++ b/app/assets/stylesheets/dark.scss @@ -8,6 +8,10 @@ $cyan: #13fefe; --color: #{$cyan}; } +.btn { + background-color: $white; +} + .btn-secondary { background-color: $white; color: $black; @@ -26,3 +30,5 @@ $cyan: #13fefe; box-shadow: 0 0 0 0.2rem $cyan; } } + + diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2746ab10..f3c942f5 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? before_action :notify_unconfirmed_email, unless: :devise_controller? around_action :set_locale + after_action :store_location! rescue_from Pundit::NilPolicyError, with: :page_not_found rescue_from ActionController::RoutingError, with: :page_not_found @@ -27,7 +28,17 @@ class ApplicationController < ActionController::Base end private - + # Traer datos de muestra de la cola de moderación + def dummy_data + @moderation_queue = YAML.safe_load(File.read(Rails.root.join('db', 'seeds', 'moderation_queue.yaml'))) + @remote_profile = YAML.safe_load(File.read(Rails.root.join('db', 'seeds', 'remote_profile.yaml'))) + @instances = YAML.safe_load(File.read(Rails.root.join('db', 'seeds', 'instances.yaml'))) + @blocklists= YAML.safe_load(File.read(Rails.root.join('db', 'seeds', 'blocklists.yml'))) + @moderation_queue.each do |activity| + activity['attributedTo'] = @remote_profile + end + end + def notify_unconfirmed_email return unless current_usuarie return if current_usuarie.confirmed? @@ -115,6 +126,17 @@ class ApplicationController < ActionController::Base def after_sign_in_path_for(resource) session[:locale] = nil - sites_path + super end + + # Guardar la ubicación para que devise redirija a donde íbamos, a + # menos que estemos recibiendo información o intentando ingresar. + def store_location! + return if request.xhr? + return unless request.request_method_symbol == :GET + return if devise_controller? && !is_a?(Devise::RegistrationsController) && params[:action] != 'edit' + + session[:usuarie_return_to] = request.fullpath + end + end diff --git a/app/controllers/moderation_queue_controller.rb b/app/controllers/moderation_queue_controller.rb new file mode 100644 index 00000000..eec0c70f --- /dev/null +++ b/app/controllers/moderation_queue_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +# Cola de moderación de ActivityPub +class ModerationQueueController < ApplicationController + # Cola de moderación viendo todo el sitio + def index + dummy_data + end + + # Perfil remoto de usuarie + def remote_profile + dummy_data + end + + # todon.nl está usando /api/v2/instance + # mauve.moe usa /api/v1/instance + def instances + dummy_data + end +end diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 057c3068..99dc6f7d 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -38,6 +38,7 @@ class PostsController < ApplicationController @usuarie = site.usuarie? current_usuarie @site_stat = SiteStat.new(site) + dummy_data end def show @@ -81,6 +82,7 @@ class PostsController < ApplicationController authorize post breadcrumb post.title.value, site_post_path(site, post, locale: locale), match: :exact breadcrumb 'posts.edit', '' + dummy_data end def update diff --git a/app/controllers/sites_controller.rb b/app/controllers/sites_controller.rb index bec42b39..e911daac 100644 --- a/app/controllers/sites_controller.rb +++ b/app/controllers/sites_controller.rb @@ -15,6 +15,19 @@ class SitesController < ApplicationController fresh_when @sites end + # Genera la caja del estado para HTMX + def status + authorize site + + render('sites/status', layout: false) if stale? site + end + + def button + authorize site + + render('sites/build', layout: false) + end + # No tenemos propiedades de un sitio aún, así que vamos al listado de # artículos def show diff --git a/app/javascript/controllers/dropdown_controller.js b/app/javascript/controllers/dropdown_controller.js new file mode 100644 index 00000000..e2b657fd --- /dev/null +++ b/app/javascript/controllers/dropdown_controller.js @@ -0,0 +1,106 @@ +import { Controller } from "stimulus"; + +// https://getbootstrap.com/docs/4.6/components/dropdowns/#single-button +export default class extends Controller { + static targets = ["dropdown", "button", "item"]; + + // Al iniciar el controlador + connect() { + // Llevar la cuenta del item con foco + this.data.set("item", -1); + + // Gestionar las teclas + this.keydownEvent = this.keydown.bind(this); + this.element.addEventListener("keydown", this.keydownEvent); + + // Gestionar el foco + this.focusinEvent = this.focusin.bind(this); + } + + // Al eliminar el controlador (al pasar a otra página) + disconnect() { + // Eliminar la gestión de teclas + this.element.removeEventListener("keydown", this.keydownEvent); + // Eliminar la gestión del foco + document.removeEventListener("focusin", this.focusinEvent); + } + + // Mostrar u ocultar + toggle(event) { + (this.buttonTarget.ariaExpanded === "false") ? this.show() : this.hide(); + } + + // Mostrar + show() { + this.buttonTarget.ariaExpanded = "true"; + this.element.classList.add("show"); + this.dropdownTarget.classList.add("show"); + + // Activar la gestión del foco + document.addEventListener("focusin", this.focusinEvent); + } + + // Ocultar + hide() { + this.buttonTarget.ariaExpanded = "false"; + this.element.classList.remove("show"); + this.dropdownTarget.classList.remove("show"); + // Volver al inicio el foco de items + this.data.set("item", -1); + + // Desactivar la gestión del foco + document.removeEventListener("focusin", this.focusinEvent); + } + + // Gestionar el foco + focusin(event) { + const item = this.itemTargets.find(x => x === event.target); + + // Si el foco se coloca sobre elementos del controlador, no hacer + // nada + if (event.target === this.buttonTarget || item) { + // Si es un item, el comportamiento de las flechas verticales y el + // Tab tiene que ser igual + if (item) this.data.set("item", this.itemTargets.indexOf(item)); + + return; + } + + // De lo contrario, ocultar + this.hide(); + } + + // Gestionar las teclas + keydown(event) { + const initial = parseInt(this.data.get("item")); + let item = initial; + + switch (event.keyCode) { + case 27: + // Esc cierra el menú y devuelve el foco + this.hide(); + this.buttonTarget.focus(); + break; + case 38: + // Moverse hacia arriba con tope en el primer item + if (item > -1) item--; + + break; + case 40: + // Moverse hacia abajo con tope en el último ítem, si el + // dropdown estaba cerrado, abrirlo. + if (item === -1) this.show(); + if (item <= this.itemTargets.length) item++; + + break; + } + + // Si cambió la posición del ítem, darle foco y actualizar el + // contador. + if (initial !== item) { + this.itemTargets[item]?.focus(); + + this.data.set("item", item); + } + } +} diff --git a/app/javascript/etc/htmx_abort.js b/app/javascript/etc/htmx_abort.js new file mode 100644 index 00000000..308d0315 --- /dev/null +++ b/app/javascript/etc/htmx_abort.js @@ -0,0 +1,7 @@ +// Cancela las peticiones pendientes de htmx para todos los elementos al +// cambiar de página. +document.addEventListener("turbolinks:click", () => { + for (const hx of document.querySelectorAll("[hx-get]")) { + window.htmx.trigger(hx, "htmx:abort"); + } +}); diff --git a/app/javascript/etc/index.js b/app/javascript/etc/index.js index d4b9f7a3..9ee6a95a 100644 --- a/app/javascript/etc/index.js +++ b/app/javascript/etc/index.js @@ -7,3 +7,4 @@ import './timezone' import './turbolinks-anchors' import './validation' import './new_editor' +import './htmx_abort' diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 9cbc30bf..e10e2b5d 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -9,9 +9,16 @@ try { host: window.env.PANEL_URL }); + const ignoredErrors = ["htmx:afterRequest", "htmx:sendAbort"]; + console.originalError = console.error; console.error = (...e) => { - window.airbrake.notify(e.join(" ")); + const msg = e.join(" "); + + if (!ignoredErrors.some(x => msg.includes(x))) { + window.airbrake.notify(e.join(" ")); + } + return console.originalError(...e); }; } catch(e) { @@ -33,3 +40,5 @@ import 'chartkick/chart.js' Rails.start() Turbolinks.start() ActiveStorage.start() + +window.htmx = require('htmx.org/dist/htmx.js') diff --git a/app/lib/jekyll/readers/data_reader_decorator.rb b/app/lib/jekyll/readers/data_reader_decorator.rb index 9fed7ac7..2a2a8fc2 100644 --- a/app/lib/jekyll/readers/data_reader_decorator.rb +++ b/app/lib/jekyll/readers/data_reader_decorator.rb @@ -14,6 +14,8 @@ module Jekyll extend ActiveSupport::Concern included do + DATA_EXTENSIONS = %w[.yaml .yml .json .csv .tsv].freeze + def read_data_to(dir, data) return unless File.directory?(dir) && !@entry_filter.symlink?(dir) @@ -24,7 +26,7 @@ module Jekyll if File.directory?(path) read_data_to(path, data[sanitize_filename(entry)] = {}) - else + elsif DATA_EXTENSIONS.include?(File.extname(entry)) key = sanitize_filename(File.basename(entry, ".*")) data[key] = read_data_file(path) end diff --git a/app/models/deploy_distributed_press.rb b/app/models/deploy_distributed_press.rb index f80024e8..678d7083 100644 --- a/app/models/deploy_distributed_press.rb +++ b/app/models/deploy_distributed_press.rb @@ -87,7 +87,7 @@ class DeployDistributedPress < Deploy # @return [Array] def gateway_urls remote_info.dig(:distributed_press, :links)&.values&.map do |protocol| - [ protocol[:link], protocol[:gateway] ] + [protocol[:link]] end&.flatten&.compact&.select do |link| link.include? '://' end || [] diff --git a/app/models/site.rb b/app/models/site.rb index c47a14a2..e3068734 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -389,8 +389,10 @@ class Site < ApplicationRecord end def reload - super - reload_jekyll! + super.tap do |s| + reload_jekyll! + end + self end def configuration diff --git a/app/policies/site_policy.rb b/app/policies/site_policy.rb index 2ca96256..ce56a2e7 100644 --- a/app/policies/site_policy.rb +++ b/app/policies/site_policy.rb @@ -14,6 +14,10 @@ class SitePolicy true end + def status? + true + end + # Puede ver la versión privada del sitio? def private? edit? && site.deploys.find_by_type('DeployPrivate') @@ -57,6 +61,10 @@ class SitePolicy show? && usuarie? end + def button? + show? + end + def enqueue? build? end diff --git a/app/views/components/_block_list.haml b/app/views/components/_block_list.haml new file mode 100644 index 00000000..0c7a7b04 --- /dev/null +++ b/app/views/components/_block_list.haml @@ -0,0 +1,9 @@ +-# Componente Listas de bloqueo de Instancias +.card.mt-3.mb-3 + .card-body + .d-flex.flex-row + = render 'components/checkbox', id: blocklist['id'] do + %span.h4= blocklist["title"] + + %p.mb-0 + %a{ href: blocklist['link'] }= blocklist['title'] diff --git a/app/views/components/_block_lists.haml b/app/views/components/_block_lists.haml new file mode 100644 index 00000000..1e9cd76f --- /dev/null +++ b/app/views/components/_block_lists.haml @@ -0,0 +1,2 @@ +- @blocklists.each do |blocklist| + = render 'components/block_list', blocklist: blocklist diff --git a/app/views/components/_btn_base.haml b/app/views/components/_btn_base.haml new file mode 100644 index 00000000..7fa507ca --- /dev/null +++ b/app/views/components/_btn_base.haml @@ -0,0 +1,3 @@ +-# Componente Botón general Moderación + +%button.btn{ href: href, class: local_assigns[:class] }= text diff --git a/app/views/components/_checkbox.haml b/app/views/components/_checkbox.haml new file mode 100644 index 00000000..27f9a776 --- /dev/null +++ b/app/views/components/_checkbox.haml @@ -0,0 +1,4 @@ +-# Componente Checkbox +.custom-control.custom-checkbox + %input.custom-control-input{ type: 'checkbox', id: id, name: id, class: local_assigns[:class] } + %label.custom-control-label{ for: id }= yield diff --git a/app/views/components/_comments_btn_box.haml b/app/views/components/_comments_btn_box.haml new file mode 100644 index 00000000..8b8d7268 --- /dev/null +++ b/app/views/components/_comments_btn_box.haml @@ -0,0 +1,8 @@ +-# Componente Botonera de Comentarios + +- btn_class = 'btn-secondary py-1 px-2' += render 'components/btn_base', text: t('.text_pause'), class: btn_class, href: '' += render 'components/btn_base', text: t('.text_reject'), class: btn_class, href: '' += render 'components/btn_base', text: t('.text_accept'), class: btn_class, href: '' += render 'components/btn_base', text: t('.text_reply'), class: btn_class, href: '' += render 'components/btn_base', text: t('.text_report'), class: btn_class, href: '' \ No newline at end of file diff --git a/app/views/components/_comments_checked_submenu.haml b/app/views/components/_comments_checked_submenu.haml new file mode 100644 index 00000000..4998e5c7 --- /dev/null +++ b/app/views/components/_comments_checked_submenu.haml @@ -0,0 +1,3 @@ += render 'components/dropdown_item', text: t('.submenu_pause'), path: '/' += render 'components/dropdown_item', text: t('.submenu_accept'), path: '/' += render 'components/dropdown_item', text: t('.submenu_reject'), path: '/' \ No newline at end of file diff --git a/app/views/components/_comments_filters.haml b/app/views/components/_comments_filters.haml new file mode 100644 index 00000000..7c453088 --- /dev/null +++ b/app/views/components/_comments_filters.haml @@ -0,0 +1,6 @@ +.d-flex.py-2 + = render 'components/dropdown', text: t('.text_checked') do + = render 'components/comments_checked_submenu' + + = render 'components/dropdown', text: t('.text_show') do + = render 'components/comments_show_submenu' \ No newline at end of file diff --git a/app/views/components/_comments_show_submenu.haml b/app/views/components/_comments_show_submenu.haml new file mode 100644 index 00000000..0308b926 --- /dev/null +++ b/app/views/components/_comments_show_submenu.haml @@ -0,0 +1,4 @@ += render 'components/dropdown_item', text: t('.submenu_pause'), path: '/' += render 'components/dropdown_item', text: t('.submenu_accept'), path: '/' += render 'components/dropdown_item', text: t('.submenu_report'), path: '/' += render 'components/dropdown_item', text: t('.submenu_reject'), path: '/' \ No newline at end of file diff --git a/app/views/components/_dropdown.haml b/app/views/components/_dropdown.haml new file mode 100644 index 00000000..54ddcffb --- /dev/null +++ b/app/views/components/_dropdown.haml @@ -0,0 +1,34 @@ +-# + @param :text [String] Contenido del botón + @param :button_classes [Array] Clases para el botón + @param :dropdown_classes [Array] Clases para el listado + @yield Un bloque que renderiza components/dropdown_item +- button_classes = local_assigns[:button_classes]&.join(' ') +- dropdown_classes = local_assigns[:dropdown_classes]&.join(' ') + +.btn-group{ + data: { + controller: 'dropdown' + } + } + %button.btn.dropdown-toggle{ + type: 'button', + class: button_classes, + data: { + toggle: 'true', + display: 'static', + action: 'dropdown#toggle', + target: 'dropdown.button' + }, + aria: { + expanded: 'false' + } + } + = text + .dropdown-menu{ + class: dropdown_classes, + data: { + target: 'dropdown.dropdown' + } + } + = yield diff --git a/app/views/components/_dropdown_item.haml b/app/views/components/_dropdown_item.haml new file mode 100644 index 00000000..3f79403d --- /dev/null +++ b/app/views/components/_dropdown_item.haml @@ -0,0 +1,4 @@ +-# + @param :text [String] Contenido del link + @param :path [String] Link += link_to text, path, class: 'dropdown-item', data: { target: 'dropdown.item' } diff --git a/app/views/components/_instances_btn_box.haml b/app/views/components/_instances_btn_box.haml new file mode 100644 index 00000000..854262c0 --- /dev/null +++ b/app/views/components/_instances_btn_box.haml @@ -0,0 +1,6 @@ +-# Componente botonera de moderación de Instancias + +- btn_class = 'btn btn-secondary' += render 'components/btn_base', text: t('.text_check'), class: btn_class, href: '' += render 'components/btn_base', text: t('.text_allow'), class: btn_class, href: '' += render 'components/btn_base', text: t('.text_deny'), class: btn_class, href: '' \ No newline at end of file diff --git a/app/views/components/_instances_checked_submenu.haml b/app/views/components/_instances_checked_submenu.haml new file mode 100644 index 00000000..f0b76185 --- /dev/null +++ b/app/views/components/_instances_checked_submenu.haml @@ -0,0 +1,3 @@ += render 'components/dropdown_item', text: t('.submenu_case'), path: '/' += render 'components/dropdown_item', text: t('.submenu_allow'), path: '/' += render 'components/dropdown_item', text: t('.submenu_reject'), path: '/' \ No newline at end of file diff --git a/app/views/components/_instances_filters.haml b/app/views/components/_instances_filters.haml new file mode 100644 index 00000000..213bb7c0 --- /dev/null +++ b/app/views/components/_instances_filters.haml @@ -0,0 +1,6 @@ +.d-flex.py-2 + = render 'components/dropdown', text: t('.text_checked') do + = render 'components/instances_checked_submenu' + + = render 'components/dropdown', text: t('.text_show') do + = render 'components/comments_show_submenu' diff --git a/app/views/components/_instances_show_submenu.haml b/app/views/components/_instances_show_submenu.haml new file mode 100644 index 00000000..1074cc3f --- /dev/null +++ b/app/views/components/_instances_show_submenu.haml @@ -0,0 +1,2 @@ += render 'components/dropdown_item', text: t('.submenu_allow'), path: '/' += render 'components/dropdown_item', text: t('.submenu_reject'), path: '/' \ No newline at end of file diff --git a/app/views/components/_profiles_btn_box.haml b/app/views/components/_profiles_btn_box.haml new file mode 100644 index 00000000..06faa8a1 --- /dev/null +++ b/app/views/components/_profiles_btn_box.haml @@ -0,0 +1,7 @@ +-# Componente Botonera de Moderación de Cuentas (Remote_profile) + +- btn_class = 'btn-secondary' += render 'components/btn_base', text: t('.text_approve'), class: btn_class, href: '' += render 'components/btn_base', text: t('.text_check'), class: btn_class, href: '' += render 'components/btn_base', text: t('.text_deny'), class: btn_class, href: '' += render 'components/btn_base', text: t('.text_report'), class: btn_class, href: '' \ No newline at end of file diff --git a/app/views/components/_profiles_checked_submenu.haml b/app/views/components/_profiles_checked_submenu.haml new file mode 100644 index 00000000..8d8f8940 --- /dev/null +++ b/app/views/components/_profiles_checked_submenu.haml @@ -0,0 +1,4 @@ += render 'components/dropdown_item', text: t('.submenu_pause'), path: '/' += render 'components/dropdown_item', text: t('.submenu_accept'), path: '/' += render 'components/dropdown_item', text: t('.submenu_reject'), path: '/' += render 'components/dropdown_item', text: t('.submenu_block'), path: '/' diff --git a/app/views/components/_profiles_filters.haml b/app/views/components/_profiles_filters.haml new file mode 100644 index 00000000..0088afef --- /dev/null +++ b/app/views/components/_profiles_filters.haml @@ -0,0 +1,6 @@ +.d-flex.py-2 + = render 'components/dropdown', text: t('.text_checked') do + = render 'components/profiles_checked_submenu' + + = render 'components/dropdown', text: t('.text_show') do + = render 'components/profiles_show_submenu' \ No newline at end of file diff --git a/app/views/components/_profiles_show_submenu.haml b/app/views/components/_profiles_show_submenu.haml new file mode 100644 index 00000000..2ba949b1 --- /dev/null +++ b/app/views/components/_profiles_show_submenu.haml @@ -0,0 +1,3 @@ += render 'components/dropdown_item', text: t('.submenu_accept'), path: '/' += render 'components/dropdown_item', text: t('.submenu_reject'), path: '/' += render 'components/dropdown_item', text: t('.submenu_block'), path: '/' \ No newline at end of file diff --git a/app/views/layouts/_details.haml b/app/views/layouts/_details.haml new file mode 100644 index 00000000..a40d0403 --- /dev/null +++ b/app/views/layouts/_details.haml @@ -0,0 +1,6 @@ +-# Detail Cola de Moderación + +%details.details.py-2 + %summary + %h3.py-2.pr-2= summary + = yield diff --git a/app/views/moderation_queue/_account.haml b/app/views/moderation_queue/_account.haml new file mode 100644 index 00000000..2c929402 --- /dev/null +++ b/app/views/moderation_queue/_account.haml @@ -0,0 +1,11 @@ +.row.no-gutters.pt-2 + .col-1 + = render 'components/checkbox', id: profile['id'] + .col-11 + %h4 + %a{href: profile['id']}= profile['preferredUsername'] + =profile['summary'].html_safe + + -# Botones de Moderación + .d-flex.pb-4 + = render 'components/profiles_btn_box' \ No newline at end of file diff --git a/app/views/moderation_queue/_accounts.haml b/app/views/moderation_queue/_accounts.haml new file mode 100644 index 00000000..2d8e1420 --- /dev/null +++ b/app/views/moderation_queue/_accounts.haml @@ -0,0 +1,12 @@ +.row.no-gutters.pt-2 + .col-1.d-flex.align-items-center + = render 'components/checkbox', id: moderation_queue.first['id'] + .col-11 + -# Filtros + = render 'components/profiles_filters' + +- @moderation_queue.map{ |c| c['attributedTo'] }.uniq.each do |remote_profile| + %hr + = render 'account', profile: remote_profile + + diff --git a/app/views/moderation_queue/_block_instances_textarea.haml b/app/views/moderation_queue/_block_instances_textarea.haml new file mode 100644 index 00000000..9b388a0d --- /dev/null +++ b/app/views/moderation_queue/_block_instances_textarea.haml @@ -0,0 +1,5 @@ +.form-group + = label_tag "custom_blocklist", t('moderation_queue.instances.custom_block') + = text_area_tag "custom_blocklist", nil, class: 'form-control' + %button.btn.btn-secondary.mt-3{ type: 'submit' }= t('moderation_queue.instances.submit') + diff --git a/app/views/moderation_queue/_comment.haml b/app/views/moderation_queue/_comment.haml new file mode 100644 index 00000000..afd2a335 --- /dev/null +++ b/app/views/moderation_queue/_comment.haml @@ -0,0 +1,27 @@ +-# Componente Comentario + +.row.no-gutters + .col-1 + = render 'components/checkbox', id: comment['id'] + .col-11 + .row.no-gutters + .col.col-lg-10.d-inline-flex.justify-content-between + %h4 + %a{ href: comment['attributedTo'] }= profile['preferredUsername'] + = render 'layouts/time', time: comment['published'] + - if comment['inReplyTo'] + .row.no-gutters + .col.p-0 + %p + %span= t('.reply_to') + %span + %a{ href: comment['inReplyTo'] }= comment['inReplyTo'] + .row.no-gutters + .col.p-0 + - if comment['summary'] + - summary = comment['summary'] + = render 'layouts/details', summary: summary do + %p= comment['content'].html_safe + - else + %p= comment['content'].html_safe + diff --git a/app/views/moderation_queue/_comments.haml b/app/views/moderation_queue/_comments.haml new file mode 100644 index 00000000..eadfd78b --- /dev/null +++ b/app/views/moderation_queue/_comments.haml @@ -0,0 +1,14 @@ +.row.no-gutters.pt-2 + .col-1.d-flex.align-items-center + = render 'components/checkbox', id: moderation_queue.first['id'] + .col-md-9 + -# Filtros + = render 'components/comments_filters' + +- moderation_queue.each do |comment| + %hr + = render 'comment', comment: comment, profile: comment['attributedTo'] + + -# Botones moderación + .d-flex.justify-content-center + = render 'components/comments_btn_box', comment: comment \ No newline at end of file diff --git a/app/views/moderation_queue/_instance.haml b/app/views/moderation_queue/_instance.haml new file mode 100644 index 00000000..ec887d2d --- /dev/null +++ b/app/views/moderation_queue/_instance.haml @@ -0,0 +1,20 @@ +- host = instance['domain'] +- host ||= instance['uri'] +- hosthttps = "https://#{host}" + +.row.no-gutters.pt-2 + .col-1 + = render 'components/checkbox', id: host + .col-11 + %h4 + %a{ href: hosthttps }= instance['title'] + %p= instance['description'].html_safe + %dl.d-inline-flex + %dt.pr-2= t('.users') + %dd + = instance.dig('usage', 'users', 'active_month') + = instance.dig('stats', 'user_count') + + -# Botones moderación + .d-flex.pb-4 + = render 'components/instances_btn_box' diff --git a/app/views/moderation_queue/_instances.haml b/app/views/moderation_queue/_instances.haml new file mode 100644 index 00000000..72be522f --- /dev/null +++ b/app/views/moderation_queue/_instances.haml @@ -0,0 +1,16 @@ +.row.no-gutters.pt-2 + .col-1.d-flex.align-items-center + = render 'components/checkbox', id: moderation_queue.first['id'] + .col-11 + -# Filtros + = render 'components/instances_filters' + +- @instances.each do |instance| + %hr + = render 'moderation_queue/instance', instance: instance + +%hr +%h3.mt-5= t('moderation_queue.instances.title') +%lead= t('moderation_queue.instances.description') += render 'components/block_lists', blocklists: @blocklists += render 'moderation_queue/block_instances_textarea' diff --git a/app/views/moderation_queue/_remote_profile.haml b/app/views/moderation_queue/_remote_profile.haml new file mode 100644 index 00000000..263f9c6b --- /dev/null +++ b/app/views/moderation_queue/_remote_profile.haml @@ -0,0 +1,28 @@ +-# Componente Remote_Profile + +.flex.py-2.mx-2 + %dl + %dt= t('.profile_name') + %dd= remote_profile['name'] + + %dt= t('.preferred_name') + %dd= remote_profile['preferredUsername'] + + %dt= t('.profile_id') + %dd + %a{ href: 'https://mastodon.mauve.moe/users/mauve' }= remote_profile['id'] + + %dt= t('.profile_published') + %dd + = render 'layouts/time', time: remote_profile['published'] + %dt= t('.profile_summary') + %dd= remote_profile['summary'].html_safe + + = render 'moderation_queue/comments', moderation_queue: @moderation_queue + +%dl.mt-5 + %dt= t('.profile_name') + %dd= remote_profile['name'] + +-# Botones de Moderación += render 'components/profiles_btn_box' diff --git a/app/views/moderation_queue/index.haml b/app/views/moderation_queue/index.haml new file mode 100644 index 00000000..ab98ee30 --- /dev/null +++ b/app/views/moderation_queue/index.haml @@ -0,0 +1,16 @@ +.row.justify-content-center + .col-md-8 + %h1= t('.title') + .row + .col + - summary = t('.instances') + = render 'layouts/details', summary: summary do + = render 'moderation_queue/instances', site: @site, post: @post, moderation_queue: @moderation_queue + %hr + - summary = t('.accounts') + = render 'layouts/details', summary: summary do + = render 'moderation_queue/accounts', site: @site, post: @post, moderation_queue: @moderation_queue + %hr + - summary = t('.comments') + = render 'layouts/details', summary: summary do + = render 'moderation_queue/comments', site: @site, post: @post, moderation_queue: @moderation_queue diff --git a/app/views/moderation_queue/remote_profile.haml b/app/views/moderation_queue/remote_profile.haml new file mode 100644 index 00000000..ba0fc257 --- /dev/null +++ b/app/views/moderation_queue/remote_profile.haml @@ -0,0 +1,4 @@ +.row.justify-content-center + .col-md-8 + %h1= t('.profile') + = render 'moderation_queue/remote_profile', remote_profile: @remote_profile diff --git a/app/views/posts/_moderation_queue.haml b/app/views/posts/_moderation_queue.haml new file mode 100644 index 00000000..a72e8abd --- /dev/null +++ b/app/views/posts/_moderation_queue.haml @@ -0,0 +1,14 @@ +.row.no-gutters.pt-2 + .col-1 + = render 'components/checkbox', id: moderation_queue.first['id'] + .col-11 + -# Filtros + = render 'components/comments_filters' + +- moderation_queue.each do |comment| + %hr + = render 'moderation_queue/comment', comment: comment, profile: comment['attributedTo'] + + -# Botones moderación + .d-flex + = render 'components/comments_btn_box' diff --git a/app/views/posts/edit.haml b/app/views/posts/edit.haml index c792ac93..2e46590e 100644 --- a/app/views/posts/edit.haml +++ b/app/views/posts/edit.haml @@ -1,6 +1,8 @@ .row.justify-content-center .col-md-8 - = render 'layouts/details', summary: "Post" do + - summary = t('posts.edit.post') + = render 'layouts/details', summary: summary do = render 'posts/form', site: @site, post: @post - = render 'layouts/details', summary: t('.moderation_queue') do + - summary = t('posts.edit.moderation_queue') + = render 'layouts/details', summary: summary do = render 'posts/moderation_queue', site: @site, post: @post, moderation_queue: @moderation_queue diff --git a/app/views/posts/index.haml b/app/views/posts/index.haml index 374f06ee..af3f0a79 100644 --- a/app/views/posts/index.haml +++ b/app/views/posts/index.haml @@ -3,7 +3,6 @@ = render 'sites/header', site: @site = render 'sites/status', site: @site - = render 'sites/build', site: @site, class: 'btn-block' %h3= t('posts.new') @@ -126,7 +125,7 @@ = post.order %td.text-nowrap - if @usuarie || policy(post).edit? - = link_to t('posts.edit'), edit_site_post_path(@site, post.path), class: 'btn btn-secondary btn-block' + = link_to t('posts.edit_post'), edit_site_post_path(@site, post.path), class: 'btn btn-secondary btn-block' - if @usuarie || policy(post).destroy? = link_to t('posts.destroy'), site_post_path(@site, post.path), class: 'btn btn-secondary btn-block', method: :delete, data: { confirm: t('posts.confirm_destroy') } diff --git a/app/views/posts/show.haml b/app/views/posts/show.haml index 10900d67..ec191d87 100644 --- a/app/views/posts/show.haml +++ b/app/views/posts/show.haml @@ -2,7 +2,7 @@ .row.justify-content-center .col-md-8 %article.content.table-responsive-md - = link_to t('posts.edit'), + = link_to t('posts.edit_post'), edit_site_post_path(@site, @post.id), class: 'btn btn-secondary btn-block' diff --git a/app/views/sites/_build.haml b/app/views/sites/_build.haml index b0961e31..8db4d370 100644 --- a/app/views/sites/_build.haml +++ b/app/views/sites/_build.haml @@ -1,9 +1,10 @@ - if policy(site).build? - = form_tag site_enqueue_path(site), - method: :post, - class: 'form-inline inline' do - = submit_tag site.enqueued? ? t('sites.enqueued') : t('sites.enqueue'), - class: "btn btn-secondary #{local_assigns[:class]}", - title: site.enqueued? ? t('help.sites.enqueued') : t('help.sites.enqueue'), - data: { disable_with: t('sites.enqueued') }, - disabled: site.enqueued? + %div{ 'hx-get': site_button_path(site, class: local_assigns[:class]), 'hx-trigger': 'every 10s', 'hx-swap': 'outerHTML' } + = form_tag site_enqueue_path(site), + method: :post, + class: 'form-inline inline' do + = submit_tag site.enqueued? ? t('sites.enqueued') : t('sites.enqueue'), + class: "btn btn-secondary #{local_assigns[:class]}", + title: site.enqueued? ? t('help.sites.enqueued') : t('help.sites.enqueue'), + data: { disable_with: t('sites.enqueued') }, + disabled: !site.waiting? diff --git a/app/views/sites/_status.haml b/app/views/sites/_status.haml index 6a610e73..a3dfd4ad 100644 --- a/app/views/sites/_status.haml +++ b/app/views/sites/_status.haml @@ -1,21 +1,24 @@ -- link = nil -- if site.not_published_yet? - - message = t('.not_published_yet') -- elsif site.awaiting_publication? - - message = t('.awaiting_publication') -- elsif site.building? - - if site.average_publication_time_calculable? - - average_building_time = site.average_publication_time - - elsif !site.similar_sites? - - average_building_time = 60 +- cache site do + - link = nil + - if site.not_published_yet? + - message = t('.not_published_yet') + - elsif site.awaiting_publication? + - message = t('.awaiting_publication') + - elsif site.building? + - if site.average_publication_time_calculable? + - average_building_time = site.average_publication_time + - elsif !site.similar_sites? + - average_building_time = 60 + - else + - average_building_time = site.average_publication_time_for_similar_sites + + - average_publication_time_human = distance_of_time_in_words average_building_time + - message = t('.building', average_time: average_publication_time_human, seconds: average_building_time) - else - - average_building_time = site.average_publication_time_for_similar_sites + - message = t('.available') + - link = true - - average_publication_time_human = distance_of_time_in_words average_building_time - - message = t('.building', average_time: average_publication_time_human, seconds: average_building_time) -- else - - message = t('.available') - - link = true - -= render 'bootstrap/alert' do - = link_to_if link, message.html_safe, site_build_stats_path(site), class: 'alert-link' + -# TODO: Calcular cada cuánto sería óptimo recargar + %div{ 'hx-get': site_status_path(site), 'hx-trigger': 'every 10s', 'hx-swap': 'outerHTML' } + = render 'bootstrap/alert' do + = link_to_if link, message.html_safe, site_build_stats_path(site), class: 'alert-link' diff --git a/app/views/sites/build.haml b/app/views/sites/build.haml new file mode 100644 index 00000000..c2becec0 --- /dev/null +++ b/app/views/sites/build.haml @@ -0,0 +1 @@ += render 'sites/build', site: @site, class: params.permit(:class)[:class] diff --git a/app/views/sites/status.haml b/app/views/sites/status.haml new file mode 100644 index 00000000..3d9793a5 --- /dev/null +++ b/app/views/sites/status.haml @@ -0,0 +1 @@ += render 'sites/status', site: @site diff --git a/bin/modified_files b/bin/modified_files index d26e71f3..4d06b4c5 100755 --- a/bin/modified_files +++ b/bin/modified_files @@ -1,7 +1,7 @@ #!/bin/sh set -e -test -n "${CI_MERGE_REQUEST_DIFF_BASE_SHA}" +CI_MERGE_REQUEST_DIFF_BASE_SHA="${CI_MERGE_REQUEST_DIFF_BASE_SHA:-origin/rails}" git diff --name-status ${CI_MERGE_REQUEST_DIFF_BASE_SHA} \ | grep -v "^D" \ diff --git a/config/locales/en.yml b/config/locales/en.yml index fc9d4894..7b2971da 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,4 +1,130 @@ en: + date: + format: '%m/%d/%Y' + published_at: "Published at" + last_modified_at: "Last modification" + abbr_day_names: + - Mon + - Tue + - Wed + - Thu + - Fri + - Sat + - Sun + day_names: + - Monday + - Tuesday + - Wednesday + - Thursday + - Friday + - Saturday + - Sunday + abbr_month_names: + - Jan + - Feb + - Mar + - Apr + - May + - Jun + - Jul + - Aug + - Sep + - Oct + - Nov + - Dec + month_names: + - January + - February + - March + - April + - May + - June + - July + - August + - September + - October + - November + - December + time: + am: am + pm: pm + format: '%-I:%M %p' + components: + instances_filters: + text_show: Show + text_checked: With selected + instances_checked_submenu: + submenu_case: Check case by case + submenu_allow: Allow everything + submenu_reject: Reject + instances_show_submenu: + submenu_allow: Allow + submenu_reject: Reject + comments_filters: + text_show: Show + text_checked: With selected + comments_checked_submenu: + submenu_pause: Pause + submenu_accept: Accept + submenu_reject: Reject + comments_show_submenu: + submenu_pause: Pause + submenu_accept: Accept + submenu_report: Report + submenu_reject: Reject + profiles_filters: + text_show: Show + text_checked: With selected + profiles_checked_submenu: + submenu_pause: Pause + submenu_accept: Accept + submenu_reject: Reject + submenu_block: Block + profiles_show_submenu: + submenu_accept: Accept + submenu_block: Block + submenu_reject: Reject + block_lists: + title: Block lists + comments_btn_box: + text_pause: Pause + text_reject: Reject + text_accept: Accept + text_reply: Reply + text_report: Report + instances_btn_box: + text_check: Check case by case + text_allow: Allow everything + text_deny: Block instance + profiles_btn_box: + text_approve: Always approve + text_check: Always check + text_deny: Block + text_report: Report + moderation_queue: + index: + title: Moderation + instances: Instances + accounts: Accounts + comments: Comments + comment: + source_profile: Source Profile + reply_to: Reply to + remote_profile: + user: Username + profile: Profile + profile_name: Profile name + preferred_name: Name in Fediverse + profile_id: ID + profile_published: Published + profile_summary: Summary + instances: + title: My block lists + description: Description + custom_block: Custom block lists + submit: Save block lists + instance: + users: "Users:" dark: Dark dir: ltr en: English @@ -389,7 +515,7 @@ en: static_file_migration: 'File migration' find_and_replace: 'Search and replace' status: - building: "Your site is building, refresh this page in ." + building: "Your site is building, it will be ready in ." not_published_yet: "Your site is being published for the first time, please wait up to 1 minute..." available: "Your site is available! Click here to find all the different ways to visit it." awaiting_publication: "There are unpublished changes. Click the button below and wait a moment to find them on your site." @@ -578,7 +704,10 @@ en: categories: 'Everything' index: search: 'Search' - edit: 'Edit' + edit_post: 'Edit' + edit: + moderation_queue: Moderation Queue + post: Post preview: btn: 'Preliminary version' alert: 'Not every article type has a preliminary version' diff --git a/config/locales/es.yml b/config/locales/es.yml index 4bda4982..2142492a 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1,4 +1,130 @@ es: + date: + format: '%d/%m/%Y' + published_at: "Publicado en" + last_modified_at: "Última modificación" + abbr_day_names: + - Lun + - Mar + - Mié + - Jue + - Vie + - Sáb + - Dom + day_names: + - Lunes + - Martes + - Miércoles + - Jueves + - Viernes + - Sábado + - Domingo + abbr_month_names: + - Ene + - Feb + - Mar + - Abr + - May + - Jun + - Jul + - Ago + - Sep + - Oct + - Nov + - Dic + month_names: + - Enero + - Febrero + - Marzo + - Abril + - Mayo + - Junio + - Julio + - Agosto + - Septiembre + - Octubre + - Noviembre + - Diciembre + time: + am: am + pm: pm + format: '%-H:%M' + components: + instances_filters: + text_show: Ver + text_checked: Con los marcados + instances_checked_submenu: + submenu_case: Moderar caso por caso + submenu_allow: Permitir todo + submenu_reject: Rechazado + instances_show_submenu: + submenu_allow: Permitido + submenu_reject: Rechazado + comments_filters: + text_show: Ver + text_checked: Con los marcados + comments_checked_submenu: + submenu_pause: Pausado + submenu_accept: Aceptado + submenu_reject: Rechazado + comments_show_submenu: + submenu_pause: Pausado + submenu_accept: Aceptado + submenu_report: Reportado + submenu_reject: Rechazado + profiles_filters: + text_show: Ver + text_checked: Con los marcados + profiles_checked_submenu: + submenu_pause: Pausado + submenu_accept: Aceptado + submenu_reject: Rechazado + submenu_block: Bloqueado + profiles_show_submenu: + submenu_accept: Aceptado + submenu_block: Bloqueado + submenu_reject: Rechazado + block_lists: + title: Listas de bloqueo + comments_btn_box: + text_pause: Pausa + text_reject: Rechazar + text_accept: Aceptar Publicación + text_reply: Responder + text_report: Reportar + instances_btn_box: + text_check: Moderar caso por caso + text_allow: Permitir todo + text_deny: Bloquear instancia + profiles_btn_box: + text_approve: Aprobar siempre + text_check: Revisar siempre + text_deny: Bloquear + text_report: Reportar + moderation_queue: + index: + title: Actividades de moderación + instances: Instancias + accounts: Cuentas + comments: Comentarios + comment: + source_profile: Cuenta de Origen + reply_to: En respuesta a + remote_profile: + user: Nombre de usuario + profile: Cuenta de Origen + profile_name: Nombre de la Cuenta + preferred_name: Nombre en el Fediverso + profile_id: ID + profile_published: Publicada + profile_summary: Presentación + instances: + title: Mis listas de bloqueo + description: Descripción de listas de bloqueo + custom_block: Lista personalizada de bloqueo + submit: Guardar listas de bloqueo + instance: + users: "Usuaries:" dark: Oscuro es: Castellano en: English @@ -394,7 +520,7 @@ es: static_file_migration: 'Migración de archivos' find_and_replace: 'Búsqueda y reemplazo' status: - building: "Tu sitio se está publicando, recargá esta página en ." + building: "Tu sitio se está publicando, estará listo en ." not_published_yet: "Tu sitio se está publicando por primera vez, por favor espera hasta un minuto..." available: "¡Tu sitio está disponible! Cliqueá aquí para encontrar todas las formas en que podés visitarlo." awaiting_publication: "Hay cambios sin publicar, cliqueá el botón debajo y espera un momento para encontrarlos en tu sitio." @@ -520,6 +646,9 @@ es: en: 'inglés' ar: 'árabe' posts: + edit: + moderation_queue: Comentarios + post: Contenido prev: Página anterior next: Página siguiente empty: No hay artículos con estos parámetros de búsqueda. @@ -586,7 +715,7 @@ es: remove_filter_help: 'Quitar este filtro: %{filter}' index: search: 'Buscar' - edit: 'Editar' + edit_post: 'Editar' preview: btn: 'Versión preliminar' alert: 'No todos los tipos de artículos poseen vista preliminar :)' diff --git a/config/routes.rb b/config/routes.rb index 635be07a..1899c656 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -37,6 +37,9 @@ Rails.application.routes.draw do get 'pull', to: 'sites#fetch' post 'pull', to: 'sites#merge' + get 'status', to: 'sites#status' + get 'button', to: 'sites#button' + # Gestionar usuaries get 'usuaries/invite', to: 'usuaries#invite' post 'usuaries/invite', to: 'usuaries#send_invitations' @@ -50,6 +53,10 @@ Rails.application.routes.draw do get 'collaborate', to: 'collaborations#collaborate' post 'collaborate', to: 'collaborations#accept_collaboration' + get 'moderation_queue', to: 'moderation_queue#index' + get 'remote_profile', to: 'moderation_queue#remote_profile' + get 'instances', to: 'moderation_queue#instances' + # Gestionar artículos según idioma nested do scope '/(:locale)', constraint: /[a-z]{2}(-[A-Z]{2})?/ do diff --git a/db/seeds/blocklists.yml b/db/seeds/blocklists.yml new file mode 100644 index 00000000..54dfe6c9 --- /dev/null +++ b/db/seeds/blocklists.yml @@ -0,0 +1,7 @@ +--- +- id: gardenfence + title: Gardenfence + link: 'https://gardenfence.github.io/' +- id: lista + title: Lista + link: '#' diff --git a/db/seeds/instances.yaml b/db/seeds/instances.yaml new file mode 100644 index 00000000..bf326832 --- /dev/null +++ b/db/seeds/instances.yaml @@ -0,0 +1,285 @@ +--- +- domain: todon.nl + title: Todon.nl + version: 4.2.3 + source_url: https://github.com/mastodon/mastodon + description: Radicaal linkse anti-autoritaire server. Voor anarchisten, socialisten, + (klimaat)activisten, LHBTQIA+, antiracisten, antifascisten, antikapitalisten, + intersectionelen, veganisten, mensenrechten, enz. + usage: + users: + active_month: 372 + thumbnail: + url: https://todon.nl/system/site_uploads/files/000/000/004/@1x/297e509bc8a81f62.png + blurhash: UXAw3zN4M|xsoga#WBay9DxntQRmITocofWE + versions: + "@1x": https://todon.nl/system/site_uploads/files/000/000/004/@1x/297e509bc8a81f62.png + "@2x": https://todon.nl/system/site_uploads/files/000/000/004/@2x/297e509bc8a81f62.png + languages: + - en + configuration: + urls: + streaming: wss://todon.nl + status: https://status.todon.eu + accounts: + max_featured_tags: 10 + statuses: + max_characters: 1312 + max_media_attachments: 4 + characters_reserved_per_url: 23 + media_attachments: + supported_mime_types: + - image/jpeg + - image/png + - image/gif + - image/heic + - image/heif + - image/webp + - image/avif + - video/webm + - video/mp4 + - video/quicktime + - video/ogg + - audio/wave + - audio/wav + - audio/x-wav + - audio/x-pn-wave + - audio/vnd.wave + - audio/ogg + - audio/vorbis + - audio/mpeg + - audio/mp3 + - audio/webm + - audio/flac + - audio/aac + - audio/m4a + - audio/x-m4a + - audio/mp4 + - audio/3gpp + - video/x-ms-asf + image_size_limit: 16777216 + image_matrix_limit: 33177600 + video_size_limit: 103809024 + video_frame_rate_limit: 120 + video_matrix_limit: 8294400 + polls: + max_options: 4 + max_characters_per_option: 50 + min_expiration: 300 + max_expiration: 2629746 + translation: + enabled: true + registrations: + enabled: false + approval_required: false + message: | +

¡No pasarán!

+ +

Je kunt tijdelijk geen nieuw account op Todon.nl aanvragen.

+ + + +

Ga naar joinmastodon.org of FediDB Network om een andere server te vinden.

+ +

It is temporary not possible to request on account on Todon.nl.

+ + + +

Go to joinmastodon.org or FediDB Network to find another server.

+ url: + max_toot_chars: 1312 + contact: + email: todon@posteo.eu + account: + id: '1' + username: admin + acct: admin + display_name: "Admin \U0001F913 Todon.nl (mod)" + locked: false + bot: false + discoverable: false + group: false + created_at: '2017-04-28T00:00:00.000Z' + note: "

This account is used for \U0001F399 Todon.nl announcements and ⚖️ + moderation.

\U0001F6AB Don't follow this account when you are not + on Todon.nl.

New? First read our \U0001F469‍\U0001F3EB Todon 101 \U0001F469‍\U0001F393 + at https://wiki.todon.eu/todon/101

⚖️ + For all our moderators go to https://wiki.todon.nl/todon/moderators

\U0001F4DD Public toots from this account + are in English.

\U0001F515 Criticism is fine, but people who do false + accusations are muted.

✉ todon@posteo.eu

#nobot

" + url: https://todon.nl/@admin + uri: https://todon.nl/users/admin + avatar: https://todon.nl/system/accounts/avatars/000/000/001/original/2db61726225ed3e6.png + avatar_static: https://todon.nl/system/accounts/avatars/000/000/001/original/2db61726225ed3e6.png + header: https://todon.nl/system/accounts/headers/000/000/001/original/fb3a846cbc20aa09.png + header_static: https://todon.nl/system/accounts/headers/000/000/001/original/fb3a846cbc20aa09.png + followers_count: 3164 + following_count: 8 + statuses_count: 724 + last_status_at: '2024-01-12' + noindex: true + emojis: [] + roles: + - id: '3' + name: Admin + color: "#595aff" + fields: + - name: "\U0001F4DC Terms of Service" + value: wiki.todon.nl/todon/terms_en + verified_at: '2018-11-01T14:39:45.465+00:00' + - name: ℹ️ Wiki + value: wiki.todon.nl/todon/informatio + verified_at: '2018-11-01T14:40:54.679+00:00' + - name: "\U0001F4CA Status" + value: status.todon.eu + verified_at: '2023-10-26T20:38:30.185+00:00' + - name: "\U0001F4B3️ Donations" + value: wiki.todon.eu/todon/donations + verified_at: '2022-11-02T00:06:31.865+00:00' + rules: + - id: '1' + text: We do not accept racism (in all its forms, incl. hate against Muslims, antisemitism, + apartheid and casteism - see our Terms of Service for our complete definition). + - id: '2' + text: We do not accept hate against lesbians, gays, bisexuals, pansexuals, transgenders, + non-binary people, intersexual people, queer people in general, etc. + - id: '4' + text: Sexism, misogyny and hate against black women (misogynoir). + - id: '6' + text: We do not accept ableism (incl. COVID-19 denial/downplaying and anti-vax) + and body-shaming. + - id: '8' + text: We do not accept harassment and trolling. + - id: '10' + text: We also do not accept other forms of hate speech. + - id: '11' + text: We do not accept (sexual) abuse of minors, adults and animals (also not + virtual). + - id: '13' + text: We do not accept glorification of violence, calls for murder, death threats, + terrorism and militarism. + - id: '15' + text: We do not accept (neo)colonialism (incl. Zionism), imperialism in all forms + and nationalism (above all nationalism of nation states, incl. flags/symbols + of those on Todon.*, see our Terms of Service). + - id: '16' + text: We do not accept fascism, right-wing populism, and right-wing and religious + extremism. + - id: '17' + text: We do not accept evangelisation and other forms of religious propaganda + [local only], and extreme sects and cults. + - id: '19' + text: We do not accept Marxist-Leninists, Stalinists, Maoists or other followers + of extreme authoritarian (so called) communist/socialist ideologies/regimes + (aka tankies). + - id: '20' + text: We do not accept capitalists, including so called 'anarcho-capitalists' + (aka ancaps) and neoliberals. + - id: '21' + text: We do not accept anthropogenic climate change denial, downplaying the climate + crisis, greenwashing and deceptive climate solutions (like nuclear energy). + - id: '27' + text: We do not accept (right-wing) conspiracy 'theories', hoaxes, fake news and + other forms of disinformation. + - id: '28' + text: Another rule in our terms of service at wiki.todon.eu/todon/terms_en. Explain + in the final step. +- uri: mastodon.mauve.moe + title: Mauvestodon + short_description: Escape ship from centralized social media run by Mauve. + description: Chat about random techie and anarchist stuff. + email: contact@mauve.moe + version: 3.5.10 + urls: + streaming_api: wss://mastodon.mauve.moe + stats: + user_count: 12 + status_count: 3287 + domain_count: 11625 + thumbnail: https://mastodon.mauve.moe/system/site_uploads/files/000/000/001/original/mauvesoftwareinc.png + languages: + - en + registrations: false + approval_required: false + invites_enabled: true + configuration: + statuses: + max_characters: 500 + max_media_attachments: 4 + characters_reserved_per_url: 23 + media_attachments: + supported_mime_types: + - image/jpeg + - image/png + - image/gif + - video/webm + - video/mp4 + - video/quicktime + - video/ogg + - audio/wave + - audio/wav + - audio/x-wav + - audio/x-pn-wave + - audio/ogg + - audio/vorbis + - audio/mpeg + - audio/mp3 + - audio/webm + - audio/flac + - audio/aac + - audio/m4a + - audio/x-m4a + - audio/mp4 + - audio/3gpp + - video/x-ms-asf + image_size_limit: 10485760 + image_matrix_limit: 16777216 + video_size_limit: 41943040 + video_frame_rate_limit: 60 + video_matrix_limit: 2304000 + polls: + max_options: 4 + max_characters_per_option: 50 + min_expiration: 300 + max_expiration: 2629746 + contact_account: + id: '1' + username: admin + acct: admin + display_name: '' + locked: false + bot: false + discoverable: true + group: false + created_at: '2022-04-25T00:00:00.000Z' + note: '' + url: https://mastodon.mauve.moe/@admin + avatar: https://mastodon.mauve.moe/system/accounts/avatars/000/000/001/original/8c21e71667b48a95.png + avatar_static: https://mastodon.mauve.moe/system/accounts/avatars/000/000/001/original/8c21e71667b48a95.png + header: https://mastodon.mauve.moe/headers/original/missing.png + header_static: https://mastodon.mauve.moe/headers/original/missing.png + followers_count: 0 + following_count: 0 + statuses_count: 0 + last_status_at: '2023-01-30' + emojis: [] + fields: + - name: Alternatel Contact + value: @mauve + verified_at: + rules: [] diff --git a/db/seeds/moderation_queue.yaml b/db/seeds/moderation_queue.yaml new file mode 100644 index 00000000..c7075c7e --- /dev/null +++ b/db/seeds/moderation_queue.yaml @@ -0,0 +1,153 @@ +--- +- "@context": + - https://www.w3.org/ns/activitystreams + - ostatus: http://ostatus.org# + atomUri: ostatus:atomUri + inReplyToAtomUri: ostatus:inReplyToAtomUri + conversation: ostatus:conversation + sensitive: as:sensitive + toot: http://joinmastodon.org/ns# + votersCount: toot:votersCount + Hashtag: as:Hashtag + id: https://mastodon.mauve.moe/users/mauve/statuses/111462305634770041 + type: Note + summary: + inReplyTo: https://mastodon.mauve.moe/users/mauve/statuses/111461923538534886 + published: '2023-11-23T22:50:10Z' + url: https://mastodon.mauve.moe/@mauve/111462305634770041 + attributedTo: https://mastodon.mauve.moe/users/mauve + to: + - https://www.w3.org/ns/activitystreams#Public + cc: + - https://mastodon.mauve.moe/users/mauve/followers + - https://hypha.coop/about.jsonld + sensitive: false + atomUri: https://mastodon.mauve.moe/users/mauve/statuses/111462305634770041 + inReplyToAtomUri: https://mastodon.mauve.moe/users/mauve/statuses/111461923538534886 + conversation: tag:mastodon.mauve.moe,2023-11-23:objectId=551471:objectType=Conversation + content:

Follow @HyphaCoop@hypha.coop for our announcement post on the 5th!

+

+ contentMap: + en:

Follow @HyphaCoop@hypha.coop for our announcement post on the 5th!

+

+ attachment: [] + tag: + - type: Mention + href: https://hypha.coop/about.jsonld + name: "@dripline@hypha.coop" + - type: Hashtag + href: https://mastodon.mauve.moe/tags/p2p + name: "#p2p" + - type: Hashtag + href: https://mastodon.mauve.moe/tags/activitypub + name: "#activitypub" + - type: Hashtag + href: https://mastodon.mauve.moe/tags/fediverse + name: "#fediverse" + replies: + id: https://mastodon.mauve.moe/users/mauve/statuses/111462305634770041/replies + type: Collection + first: + type: CollectionPage + next: https://mastodon.mauve.moe/users/mauve/statuses/111462305634770041/replies?only_other_accounts=true&page=true + partOf: https://mastodon.mauve.moe/users/mauve/statuses/111462305634770041/replies + items: [] +- "@context": + - https://www.w3.org/ns/activitystreams + - "@language": es + sensitive: as:sensitive + type: Note + id: https://sutty.nl/lanzamiento-de-publicaciones-distribuidas-en-el-fediverso-a-trav%C3%A9s-de-sutty/ + summary: Lanzamiento de publicaciones distribuidas en el Fediverso a través de Sutty + published: '2023-12-04T21:53:05+00:00' + updated: '2023-12-05T20:41:34+00:00' + attributedTo: https://sutty.nl/about.jsonld + to: + - https://www.w3.org/ns/activitystreams#Public + cc: + - https://social.distributed.press/v1/@sutty@sutty.nl/followers + inReplyTo: https://hypha.coop/dripline/announcing-dp-social-inbox/ + sensitive: true + content: | +

Estamos felices y orgulloses de anunciar el lanzamiento de la funcionalidad que permite la publicación en el Fediverso de los artículos de todos los sitios creados a través de Sutty.

Gracias al trabajo conjunto con Distributed Press, Hypha y apoyado por la Filecoin Foundation for the Distributed Web, Sutty hace posible que la seguridad de tu sitio estático se combine con la rápida difusión de tu contenido a través de las redes sociales libres y descentralizadas que constituyen el Fediverso.

Esto se logró a través del desarollo y la integración de dos componentes, trabajados en forma conjunta y colaborativa:

    +
  1. Social Inbox, desarrollado principalmente Distributed Press. Aporta la funcionalidad de recibir artículos, responder y mencionar otras cuentas en el Fediverso.

  2. +
  3. Jekyll Activity Pub Plugin, desarrollado principalmente por Sutty. Permite integrar Social Inbox en todos los sitios estáticos generados en Jekyll, admitiendo así la publicación automática de contenido del sitio en el Fediverso.

  4. +

Sutty integra la funcionalidad completa en su CMS para sitios estáticos en Jekyll, permitiendo gestionarla desde una interfaz en continua mejora de su usabilidad.

Si todavía no estás familiarizade con estos nombres y conceptos, te invitamos a conocer más a continuación, en la sección “Para tecno-curioses”.

Qué significa

Qué permite hacer

Qué se viene

¡Quiero usarlo!

Te invitamos a dar tus primeros pasos de la mano de nuestro tutorial.

Para tecno-curioses

Cómo funciona

Los sitios web y las redes sociales parecen ser especies distintas dentro del Universo de Internet. Al mismo tiempo, las redes sociales corporativas y concentradas como Instagram, Facebook, X (ex Twitter), entre otras, demostraron ser hostiles con algunos grupos o colectivos sociales en particular (censurando contenido, persiguiendo pezones, ocultando publicaciones por color de piel y de pelo, etc.) y con todes sus usuaries en general (vendiendo data en forma masiva, violando acuerdos de privacidad, eligiendo diseños de interfaz y uso que generan ansiedad y adicción, etc.). Pese a esto, siguen funcionando como espacios obligados a la hora de publicitar un emprendimiento o difundir noticias urgentes.

El Fediverso es una red federada, descentralizada y distribuida de redes sociales libres, cada una con sus características, preferencias, grupos de usuaries. Están diseñadas para facilitar el diálogo entre todas ellas. Es decir, para que los contenidos puedan ser visibles y se puedan generar respuestas entre usuaries, fomentando una cultura de participación y pluralidad de voces, basadas en estándares de desarrollo libre y que buscan ser éticos antes que con fines de lucro sin fin.

Los sitios web siguen siendo formatos para medios de comunicación que, debido a sus características, favorecen la difusión de contenidos como artículos multimedia. Permiten adecuar un estilo a una identidad visual del medio, mantener secciones y contenido institucional variado, entre otras cosas.

Las redes sociales se destacan por sus características de inmediatez, favoreciendo un flujo dialógico en tiempo real con otros tiempos de atención y características de navegación que lo hacen más breve, rápido, a veces efímero. Los medios de comunicación (personas o emprendimientos mediáticos) suelen utilizarlos para llamar la atención sobre contenidos publicados en sus sitios, apostando a la divulgación rápida y las discusiones que puedan darse entre usuaries.

La funcionalidad que desarrollamos en Sutty contempla los casos de uso en los que un contenido quiera ser compartido a más personas, en menor tiempo, con la posibilidad de generar diálogos. Las particularidades de nuestros sitios y redes sociales libres generan condiciones favorables para la libertad de expresión, que preferimos llamar Derecho a la Comunicación, evadiendo las variadas y cada vez más sofisticadas formas de censura de las plataformas corporativas tradicionales. Un contenido reproducido en varios lugares al mismo tiempo ayuda a su divulgación y es ideal para aquellas voces y discursos contrahegemónicos en la web y su supervivencia al paso del tiempo, preservando la memoria popular.

Cómo funciona el Fediverso en la moderación

El Fediverso intenta funcionar como comunidades en línea interconectadas que se autogobiernan en las formas de cuidados colectivos. Así, cada instancia podría ser algo así como un municipio que aloja diferentes cuentas/usuaries bajo unas reglas consensuadas y que pueden ser puestas en discusión si fuera necesario. De esta forma, es posible regular la circulación de contenidos fascistas y discursos de odio que puedan dañar no solamente la participación de diverses usuaries sino también su salud.

Para ello, cada instancia elige sus formas de moderación y puede excluir otras instancias con denuncias previas de contenidos antidemocráticos, odiantes o contrarios a los valores y cuidados de sus habitantes.

En Sutty en particular, nos interesan las estrategias y los mecanismos de cuidados colectivos, por lo que seguimos diseñando modelos que permitan sostenerlos en nuestras tecnologías. Podés revisar nuestros términos y condiciones, política de privacidad y acuerdos de convivencia para más información.

¿Te interesa participar?

Si sos parte de una organización social, grupo de activismo o colectivo social que pensás que podría beneficiarse de estas características, te invitamos a contactarnos a través de nuestro formulario. Estamos busando mejorar los usos de las tecnologías para ustedes y valoramos sus experiencias.

Otras posibilidades de integración de Social Inbox en sitios estáticos

Si te interesa incorporar esta funcionalidad para otros gestores de sitios estáticos, no dudes en contactarnos. Además, mantenete al tanto de las novedades que compartimos en https://dweb.sutty.nl y en nuestro blog https://sutty.nl/blog

Recomendado para saber más

+ name: Lanzamiento de publicaciones distribuidas en el Fediverso a través de Sutty + contentMap: + es: | +

Estamos felices y orgulloses de anunciar el lanzamiento de la funcionalidad que permite la publicación en el Fediverso de los artículos de todos los sitios creados a través de Sutty.

Gracias al trabajo conjunto con Distributed Press, Hypha y apoyado por la Filecoin Foundation for the Distributed Web, Sutty hace posible que la seguridad de tu sitio estático se combine con la rápida difusión de tu contenido a través de las redes sociales libres y descentralizadas que constituyen el Fediverso.

Esto se logró a través del desarollo y la integración de dos componentes, trabajados en forma conjunta y colaborativa:

    +
  1. Social Inbox, desarrollado principalmente Distributed Press. Aporta la funcionalidad de recibir artículos, responder y mencionar otras cuentas en el Fediverso.

  2. +
  3. Jekyll Activity Pub Plugin, desarrollado principalmente por Sutty. Permite integrar Social Inbox en todos los sitios estáticos generados en Jekyll, admitiendo así la publicación automática de contenido del sitio en el Fediverso.

  4. +

Sutty integra la funcionalidad completa en su CMS para sitios estáticos en Jekyll, permitiendo gestionarla desde una interfaz en continua mejora de su usabilidad.

Si todavía no estás familiarizade con estos nombres y conceptos, te invitamos a conocer más a continuación, en la sección “Para tecno-curioses”.

Qué significa

Qué permite hacer

Qué se viene

¡Quiero usarlo!

Te invitamos a dar tus primeros pasos de la mano de nuestro tutorial.

Para tecno-curioses

Cómo funciona

Los sitios web y las redes sociales parecen ser especies distintas dentro del Universo de Internet. Al mismo tiempo, las redes sociales corporativas y concentradas como Instagram, Facebook, X (ex Twitter), entre otras, demostraron ser hostiles con algunos grupos o colectivos sociales en particular (censurando contenido, persiguiendo pezones, ocultando publicaciones por color de piel y de pelo, etc.) y con todes sus usuaries en general (vendiendo data en forma masiva, violando acuerdos de privacidad, eligiendo diseños de interfaz y uso que generan ansiedad y adicción, etc.). Pese a esto, siguen funcionando como espacios obligados a la hora de publicitar un emprendimiento o difundir noticias urgentes.

El Fediverso es una red federada, descentralizada y distribuida de redes sociales libres, cada una con sus características, preferencias, grupos de usuaries. Están diseñadas para facilitar el diálogo entre todas ellas. Es decir, para que los contenidos puedan ser visibles y se puedan generar respuestas entre usuaries, fomentando una cultura de participación y pluralidad de voces, basadas en estándares de desarrollo libre y que buscan ser éticos antes que con fines de lucro sin fin.

Los sitios web siguen siendo formatos para medios de comunicación que, debido a sus características, favorecen la difusión de contenidos como artículos multimedia. Permiten adecuar un estilo a una identidad visual del medio, mantener secciones y contenido institucional variado, entre otras cosas.

Las redes sociales se destacan por sus características de inmediatez, favoreciendo un flujo dialógico en tiempo real con otros tiempos de atención y características de navegación que lo hacen más breve, rápido, a veces efímero. Los medios de comunicación (personas o emprendimientos mediáticos) suelen utilizarlos para llamar la atención sobre contenidos publicados en sus sitios, apostando a la divulgación rápida y las discusiones que puedan darse entre usuaries.

La funcionalidad que desarrollamos en Sutty contempla los casos de uso en los que un contenido quiera ser compartido a más personas, en menor tiempo, con la posibilidad de generar diálogos. Las particularidades de nuestros sitios y redes sociales libres generan condiciones favorables para la libertad de expresión, que preferimos llamar Derecho a la Comunicación, evadiendo las variadas y cada vez más sofisticadas formas de censura de las plataformas corporativas tradicionales. Un contenido reproducido en varios lugares al mismo tiempo ayuda a su divulgación y es ideal para aquellas voces y discursos contrahegemónicos en la web y su supervivencia al paso del tiempo, preservando la memoria popular.

Cómo funciona el Fediverso en la moderación

El Fediverso intenta funcionar como comunidades en línea interconectadas que se autogobiernan en las formas de cuidados colectivos. Así, cada instancia podría ser algo así como un municipio que aloja diferentes cuentas/usuaries bajo unas reglas consensuadas y que pueden ser puestas en discusión si fuera necesario. De esta forma, es posible regular la circulación de contenidos fascistas y discursos de odio que puedan dañar no solamente la participación de diverses usuaries sino también su salud.

Para ello, cada instancia elige sus formas de moderación y puede excluir otras instancias con denuncias previas de contenidos antidemocráticos, odiantes o contrarios a los valores y cuidados de sus habitantes.

En Sutty en particular, nos interesan las estrategias y los mecanismos de cuidados colectivos, por lo que seguimos diseñando modelos que permitan sostenerlos en nuestras tecnologías. Podés revisar nuestros términos y condiciones, política de privacidad y acuerdos de convivencia para más información.

¿Te interesa participar?

Si sos parte de una organización social, grupo de activismo o colectivo social que pensás que podría beneficiarse de estas características, te invitamos a contactarnos a través de nuestro formulario. Estamos busando mejorar los usos de las tecnologías para ustedes y valoramos sus experiencias.

Otras posibilidades de integración de Social Inbox en sitios estáticos

Si te interesa incorporar esta funcionalidad para otros gestores de sitios estáticos, no dudes en contactarnos. Además, mantenete al tanto de las novedades que compartimos en https://dweb.sutty.nl y en nuestro blog https://sutty.nl/blog

Recomendado para saber más

+ attachment: + - type: Document + mediaType: image/png + url: https://sutty.nl/public/8r7b6ohqy6xzgngxbol6337q8jj9/milestone_2_activity_pub_2.png + name: Botones de colores para activar la "Web Disribuida" y el "Fediverso". diff --git a/db/seeds/remote_profile.yaml b/db/seeds/remote_profile.yaml new file mode 100644 index 00000000..1a670d6b --- /dev/null +++ b/db/seeds/remote_profile.yaml @@ -0,0 +1,106 @@ +--- +"@context": +- https://www.w3.org/ns/activitystreams +- https://w3id.org/security/v1 +- manuallyApprovesFollowers: as:manuallyApprovesFollowers + toot: http://joinmastodon.org/ns# + featured: + "@id": toot:featured + "@type": "@id" + featuredTags: + "@id": toot:featuredTags + "@type": "@id" + alsoKnownAs: + "@id": as:alsoKnownAs + "@type": "@id" + movedTo: + "@id": as:movedTo + "@type": "@id" + schema: http://schema.org# + PropertyValue: schema:PropertyValue + value: schema:value + discoverable: toot:discoverable + Device: toot:Device + Ed25519Signature: toot:Ed25519Signature + Ed25519Key: toot:Ed25519Key + Curve25519Key: toot:Curve25519Key + EncryptedMessage: toot:EncryptedMessage + publicKeyBase64: toot:publicKeyBase64 + deviceId: toot:deviceId + claim: + "@type": "@id" + "@id": toot:claim + fingerprintKey: + "@type": "@id" + "@id": toot:fingerprintKey + identityKey: + "@type": "@id" + "@id": toot:identityKey + devices: + "@type": "@id" + "@id": toot:devices + messageFranking: toot:messageFranking + messageType: toot:messageType + cipherText: toot:cipherText + suspended: toot:suspended + focalPoint: + "@container": "@list" + "@id": toot:focalPoint +id: https://mastodon.mauve.moe/users/mauve +type: Person +following: https://mastodon.mauve.moe/users/mauve/following +followers: https://mastodon.mauve.moe/users/mauve/followers +inbox: https://mastodon.mauve.moe/users/mauve/inbox +outbox: https://mastodon.mauve.moe/users/mauve/outbox +featured: https://mastodon.mauve.moe/users/mauve/collections/featured +featuredTags: https://mastodon.mauve.moe/users/mauve/collections/tags +preferredUsername: mauve +name: "Mauve \U0001F441\U0001F49C" +summary: "

Occult Enby that's making local-first software with peer to peer + protocols, mesh networks, and the web.

Also exploring what a local-first + cyberspace might look like in my spare time.

" +url: https://mastodon.mauve.moe/@mauve +manuallyApprovesFollowers: false +discoverable: true +published: '2022-04-25T00:00:00Z' +devices: https://mastodon.mauve.moe/users/mauve/collections/devices +alsoKnownAs: +- https://infosec.exchange/users/RangerMauve +publicKey: + id: https://mastodon.mauve.moe/users/mauve#main-key + owner: https://mastodon.mauve.moe/users/mauve + publicKeyPem: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxjxu6bRQOjH4caQu7JgZ + umIWFeX0ZdbVnofElev2d9JByqcDoWhmaks3RYdW71RDPNrr0JxqZvUbIw9kQBng + 7iQ9YTcXTdJ/N9CQoB22msffYkEIw4ilehCDXdchNs4aoVAUwI8IhkM0p/itz6gK + 75C3CQv74Y7rHUJC8ob2p4KUwRUyhgzyhp8QWwCAn/RZ28wP8EbjWF9IskMRo9vq + WUX+Io6hpADRkSwZGoOSW2zxCEBVco6tRmABTte8I0WcAucLyMEyfGMlUvxRew4D + zAWoEBS8SyqM68vUabbZYLns6kya34tvsf1NkvajDGrfgU3D0LlGX++tOa6N9Pkf + XwIDAQAB + -----END PUBLIC KEY----- +tag: [] +attachment: +- type: PropertyValue + name: Pronouns + value: they/them/it +- type: PropertyValue + name: Email + value: mauve@mauve.moe +- type: PropertyValue + name: Matrix + value: @mauve:mauve.moe +- type: PropertyValue + name: Github/Twitter + value: "@RangerMauve" +endpoints: + sharedInbox: https://mastodon.mauve.moe/inbox +icon: + type: Image + mediaType: image/png + url: https://mastodon.mauve.moe/system/accounts/avatars/000/000/002/original/e4b910cee121b1b8.png +image: + type: Image + mediaType: image/png + url: https://mastodon.mauve.moe/system/accounts/headers/000/000/002/original/a96f990025091662.png diff --git a/package.json b/package.json index 7901ad41..74ff491a 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@rails/activestorage": "^6.1.3-1", "@rails/ujs": "^6.1.3-1", "@rails/webpacker": "5.4.4", - "@suttyweb/editor": "^0.1.25", + "@suttyweb/editor": "^0.1.27", "babel-loader": "^8.2.2", "chart.js": "^3.5.1", "chartkick": "^4.0.5", @@ -21,6 +21,7 @@ "commonmark": "^0.29.0", "fork-awesome": "^1.1.7", "fork-ts-checker-webpack-plugin": "^6.1.0", + "htmx.org": "^1.9.11", "input-map": "git+https://0xacab.org/sutty/input-map.git", "input-tag": "git+https://0xacab.org/sutty/input-tag.git", "leaflet": "^1.7.1", diff --git a/yarn.lock b/yarn.lock index 0c52b9d3..829b7ed1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1821,6 +1821,26 @@ resolved "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz" integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== +"@floating-ui/core@^1.0.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.0.tgz#fa41b87812a16bf123122bf945946bae3fdf7fc1" + integrity sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g== + dependencies: + "@floating-ui/utils" "^0.2.1" + +"@floating-ui/dom@^1.5.1": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.3.tgz#954e46c1dd3ad48e49db9ada7218b0985cee75ef" + integrity sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw== + dependencies: + "@floating-ui/core" "^1.0.0" + "@floating-ui/utils" "^0.2.0" + +"@floating-ui/utils@^0.2.0", "@floating-ui/utils@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2" + integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q== + "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.3" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" @@ -1955,11 +1975,13 @@ resolved "https://registry.npmjs.org/@stimulus/webpack-helpers/-/webpack-helpers-1.1.1.tgz" integrity sha512-XOkqSw53N9072FLHvpLM25PIwy+ndkSSbnTtjKuyzsv8K5yfkFB2rv68jU1pzqYa9FZLcvZWP4yazC0V38dx9A== -"@suttyweb/editor@^0.1.25": - version "0.1.25" - resolved "https://registry.yarnpkg.com/@suttyweb/editor/-/editor-0.1.25.tgz#37b38560642a49b24383473543c28be943695f9f" - integrity sha512-fxOO9LpdntWzgNZch4cZB6QL0u+jEw0NqsNahKcGBbiJaS0GNGLRrT2LUd/Djc6O8HWkQguPLcquVT5eHq2h9g== +"@suttyweb/editor@^0.1.27": + version "0.1.27" + resolved "https://registry.yarnpkg.com/@suttyweb/editor/-/editor-0.1.27.tgz#9415a0b767e72dbe4fbf42ce87e62fb8f5125c31" + integrity sha512-Ts9TZtGiRIaHm+ffVBRl+/nuVcANWZNtFsrGacoajgEsagaIyA1cq8qjiNpPoM5ne9vTba3cAaLP04V/uEIhBw== dependencies: + "@floating-ui/dom" "^1.5.1" + linkifyjs "^4.1.1" prosemirror-svelte-nodeview "^1.0.2" "@types/caseless@*": @@ -4548,6 +4570,11 @@ html-entities@^1.3.1: resolved "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz" integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA== +htmx.org@^1.9.11: + version "1.9.11" + resolved "https://registry.yarnpkg.com/htmx.org/-/htmx.org-1.9.11.tgz#00192041ee682d6ca7146d0fbd901169ffe72d87" + integrity sha512-WlVuICn8dfNOOgYmdYzYG8zSnP3++AdHkMHooQAzGZObWpVXYathpz/I37ycF4zikR6YduzfCvEcxk20JkIUsw== + http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" @@ -5190,6 +5217,11 @@ linkify-it@^2.0.0: dependencies: uc.micro "^1.0.1" +linkifyjs@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.1.3.tgz#0edbc346428a7390a23ea2e5939f76112c9ae07f" + integrity sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg== + loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz"