From 14f2591c1cdc37c7b7e593b922414fa542f6ac91 Mon Sep 17 00:00:00 2001 From: Neetpone <132411956+Neetpone@users.noreply.github.com> Date: Tue, 30 Jul 2024 09:53:39 -0400 Subject: [PATCH] feat(downloads): add text downloads --- .../stories/downloads_controller.rb | 17 +++++++++ app/lib/story_renderer.rb | 35 ++++++++++++++++++ app/views/stories/_card.html.slim | 3 +- app/views/stories/show.html.slim | 3 ++ config/routes.rb | 3 ++ public/img/icons/epub32.png | Bin 0 -> 1692 bytes public/img/icons/html32.png | Bin 0 -> 2462 bytes public/img/icons/mobi32.png | Bin 0 -> 2181 bytes public/img/icons/txt32.png | Bin 0 -> 197 bytes 9 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 app/controllers/stories/downloads_controller.rb create mode 100644 app/lib/story_renderer.rb create mode 100644 public/img/icons/epub32.png create mode 100644 public/img/icons/html32.png create mode 100644 public/img/icons/mobi32.png create mode 100644 public/img/icons/txt32.png diff --git a/app/controllers/stories/downloads_controller.rb b/app/controllers/stories/downloads_controller.rb new file mode 100644 index 0000000..f6f471f --- /dev/null +++ b/app/controllers/stories/downloads_controller.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +class Stories::DownloadsController < ApplicationController + MIME_TYPES = { + text: 'text/plain', + html: 'text/html', + epub: 'application/x-epub' + }.freeze + + def show + @story = Story.find(params[:story_id]) + + format = MIME_TYPES.keys.detect { |fmt| fmt.to_s == params[:fmt] } || :text + renderer = StoryRenderer.new @story + + render inline: renderer.send(format), content_type: MIME_TYPES[format], content_disposition: 'attachment' + end +end diff --git a/app/lib/story_renderer.rb b/app/lib/story_renderer.rb new file mode 100644 index 0000000..ede5955 --- /dev/null +++ b/app/lib/story_renderer.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true +require 'redcarpet/render_strip' + +class StoryRenderer + def initialize(story) + @story = story + end + + def text + markdown = Redcarpet::Markdown.new(Redcarpet::Render::StripDown) + separator = '//-------------------------------------------------------//' + + # Title and author + text = "#{separator}\n".dup + text += "#{@story.title.center(separator.length, ' ')}\n" + text += "#{"-by #{@story.author.name}-".center(separator.length, ' ')}\n" + text += "#{separator}\n" + + # Chapters + @story.chapters.each do |chapter| + text += "#{separator}\n" + text += "#{chapter.title.center(separator.length, ' ')}\n" + text += "#{separator}\n" + text += "#{markdown.render chapter.body}\n" # FIXME: My Markdown still has the title at the top for no reason. + end + + text + end + + def html + end + + def epub + end +end diff --git a/app/views/stories/_card.html.slim b/app/views/stories/_card.html.slim index d071030..2fde933 100644 --- a/app/views/stories/_card.html.slim +++ b/app/views/stories/_card.html.slim @@ -14,8 +14,7 @@ section.fic-cell span.popular span.stats - if story.rating - span.likes - | Rating: #{story.rating}% + span.likes Rating: #{story.rating}% - if story.short_description p.description= story.short_description - else diff --git a/app/views/stories/show.html.slim b/app/views/stories/show.html.slim index 404ddac..c7a5cc0 100644 --- a/app/views/stories/show.html.slim +++ b/app/views/stories/show.html.slim @@ -40,6 +40,9 @@ div.story span.cached .dl-links span Download: + = link_to story_download_path(@story, fmt: 'txt') do + img src="/img/icons/txt32.png" alt="text" title="Download condensed text format" + .chapterlist h3= pluralize(@chapters.count, 'Chapter') + ':' ol diff --git a/config/routes.rb b/config/routes.rb index 087acbc..58c8b84 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,6 +11,9 @@ Rails.application.routes.draw do # using singular-named routes to match FiMFetch/FiMFiction. resources :stories, only: [:show], path: :story do resources :chapters, only: [:show], path: :chapter + scope module: :stories do + resource :download, only: :show + end end # Different route name again to match FiMFetch diff --git a/public/img/icons/epub32.png b/public/img/icons/epub32.png new file mode 100644 index 0000000000000000000000000000000000000000..a6da5da830dc878256cb0f5f083104e6709cb983 GIT binary patch literal 1692 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 Hu0mjf2%Z>t literal 0 HcmV?d00001 diff --git a/public/img/icons/txt32.png b/public/img/icons/txt32.png new file mode 100644 index 0000000000000000000000000000000000000000..4c782601c1fb64504278f79d0b2ba63fb50be6ad GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvxd5LK*Z=?jv#_unIB=k?t?lvS z#~(j_ynFZVrcIlsO`Eo0!4m%3c``tiNuDl_AsXkC6A}bcL>QC|6b~@4oIAsI%z;I1 zlB3cxrXCNr|JqK!;!9E@xo49rX#LYfCOG&qdrObndRz!k)^v`x}MH^5+n)`<_? tj-tyvn1s0