mirror of
https://github.com/hkalexling/Mango.git
synced 2025-08-03 11:25:29 -04:00
Merge branch 'feature/home' into dev
This commit is contained in:
commit
53c3798691
@ -15,10 +15,14 @@ function showModal(encodedPath, pages, percentage, encodedeTitle, encodedEntryTi
|
|||||||
if (percentage === 100) {
|
if (percentage === 100) {
|
||||||
$('#read-btn').attr('hidden', '');
|
$('#read-btn').attr('hidden', '');
|
||||||
}
|
}
|
||||||
$('#modal-title').find('span').text(entry);
|
|
||||||
$('#modal-title').next().attr('data-id', titleID);
|
$('#modal-title-link').text(title);
|
||||||
$('#modal-title').next().attr('data-entry-id', entryID);
|
$('#modal-title-link').attr('href', '/book/' + titleID);
|
||||||
$('#modal-title').next().find('.title-rename-field').val(entry);
|
|
||||||
|
$('#modal-entry-title').find('span').text(entry);
|
||||||
|
$('#modal-entry-title').next().attr('data-id', titleID);
|
||||||
|
$('#modal-entry-title').next().attr('data-entry-id', entryID);
|
||||||
|
$('#modal-entry-title').next().find('.title-rename-field').val(entry);
|
||||||
$('#path-text').text(zipPath);
|
$('#path-text').text(zipPath);
|
||||||
$('#pages-text').text(pages + ' pages');
|
$('#pages-text').text(pages + ' pages');
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ require "yaml"
|
|||||||
class Config
|
class Config
|
||||||
include YAML::Serializable
|
include YAML::Serializable
|
||||||
|
|
||||||
|
@[YAML::Field(ignore: true)]
|
||||||
|
property path : String = ""
|
||||||
property port : Int32 = 9000
|
property port : Int32 = 9000
|
||||||
property base_url : String = "/"
|
property base_url : String = "/"
|
||||||
property session_secret : String = "mango-session-secret"
|
property session_secret : String = "mango-session-secret"
|
||||||
@ -44,6 +46,7 @@ class Config
|
|||||||
if File.exists? cfg_path
|
if File.exists? cfg_path
|
||||||
config = self.from_yaml File.read cfg_path
|
config = self.from_yaml File.read cfg_path
|
||||||
config.preprocess
|
config.preprocess
|
||||||
|
config.path = path
|
||||||
config.fill_defaults
|
config.fill_defaults
|
||||||
return config
|
return config
|
||||||
end
|
end
|
||||||
@ -54,6 +57,7 @@ class Config
|
|||||||
abort "Aborting..."
|
abort "Aborting..."
|
||||||
end
|
end
|
||||||
default = self.allocate
|
default = self.allocate
|
||||||
|
default.path = path
|
||||||
default.fill_defaults
|
default.fill_defaults
|
||||||
cfg_dir = File.dirname cfg_path
|
cfg_dir = File.dirname cfg_path
|
||||||
unless Dir.exists? cfg_dir
|
unless Dir.exists? cfg_dir
|
||||||
|
153
src/library.cr
153
src/library.cr
@ -17,7 +17,8 @@ end
|
|||||||
class Entry
|
class Entry
|
||||||
property zip_path : String, book : Title, title : String,
|
property zip_path : String, book : Title, title : String,
|
||||||
size : String, pages : Int32, id : String, title_id : String,
|
size : String, pages : Int32, id : String, title_id : String,
|
||||||
encoded_path : String, encoded_title : String, mtime : Time
|
encoded_path : String, encoded_title : String, mtime : Time,
|
||||||
|
date_added : Time
|
||||||
|
|
||||||
def initialize(path, @book, @title_id, storage)
|
def initialize(path, @book, @title_id, storage)
|
||||||
@zip_path = path
|
@zip_path = path
|
||||||
@ -33,6 +34,7 @@ class Entry
|
|||||||
file.close
|
file.close
|
||||||
@id = storage.get_id @zip_path, false
|
@id = storage.get_id @zip_path, false
|
||||||
@mtime = File.info(@zip_path).modification_time
|
@mtime = File.info(@zip_path).modification_time
|
||||||
|
@date_added = load_date_added
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_json(json : JSON::Builder)
|
def to_json(json : JSON::Builder)
|
||||||
@ -87,6 +89,20 @@ class Entry
|
|||||||
end
|
end
|
||||||
img
|
img
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private def load_date_added
|
||||||
|
date_added = nil
|
||||||
|
TitleInfo.new @book.dir do |info|
|
||||||
|
info_da = info.date_added[@title]?
|
||||||
|
if info_da.nil?
|
||||||
|
date_added = info.date_added[@title] = ctime @zip_path
|
||||||
|
info.save
|
||||||
|
else
|
||||||
|
date_added = info_da
|
||||||
|
end
|
||||||
|
end
|
||||||
|
date_added.not_nil! # is it ok to set not_nil! here?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Title
|
class Title
|
||||||
@ -289,6 +305,12 @@ class Title
|
|||||||
else
|
else
|
||||||
info.progress[username][entry] = page
|
info.progress[username][entry] = page
|
||||||
end
|
end
|
||||||
|
# save last_read timestamp
|
||||||
|
if info.last_read[username]?.nil?
|
||||||
|
info.last_read[username] = {entry => Time.utc}
|
||||||
|
else
|
||||||
|
info.last_read[username][entry] = Time.utc
|
||||||
|
end
|
||||||
info.save
|
info.save
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -304,14 +326,14 @@ class Title
|
|||||||
progress
|
progress
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_percetage(username, entry)
|
def load_percentage(username, entry)
|
||||||
page = load_progress username, entry
|
page = load_progress username, entry
|
||||||
entry_obj = @entries.find { |e| e.title == entry }
|
entry_obj = @entries.find { |e| e.title == entry }
|
||||||
return 0.0 if entry_obj.nil?
|
return 0.0 if entry_obj.nil?
|
||||||
page / entry_obj.pages
|
page / entry_obj.pages
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_percetage(username)
|
def load_percentage(username)
|
||||||
return 0.0 if @entries.empty?
|
return 0.0 if @entries.empty?
|
||||||
read_pages = total_pages = 0
|
read_pages = total_pages = 0
|
||||||
@entries.each do |e|
|
@entries.each do |e|
|
||||||
@ -321,11 +343,53 @@ class Title
|
|||||||
read_pages / total_pages
|
read_pages / total_pages
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def load_last_read(username, entry)
|
||||||
|
last_read = nil
|
||||||
|
TitleInfo.new @dir do |info|
|
||||||
|
unless info.last_read[username]?.nil? ||
|
||||||
|
info.last_read[username][entry]?.nil?
|
||||||
|
last_read = info.last_read[username][entry]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
last_read
|
||||||
|
end
|
||||||
|
|
||||||
def next_entry(current_entry_obj)
|
def next_entry(current_entry_obj)
|
||||||
idx = @entries.index current_entry_obj
|
idx = @entries.index current_entry_obj
|
||||||
return nil if idx.nil? || idx == @entries.size - 1
|
return nil if idx.nil? || idx == @entries.size - 1
|
||||||
@entries[idx + 1]
|
@entries[idx + 1]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def previous_entry(current_entry_obj)
|
||||||
|
idx = @entries.index current_entry_obj
|
||||||
|
return nil if idx.nil? || idx == 0
|
||||||
|
@entries[idx - 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_continue_reading_entry(username)
|
||||||
|
in_progress_entries = @entries.select do |e|
|
||||||
|
load_progress(username, e.title) > 0
|
||||||
|
end
|
||||||
|
return nil if in_progress_entries.empty?
|
||||||
|
|
||||||
|
latest_read_entry = in_progress_entries[-1]
|
||||||
|
if load_progress(username, latest_read_entry.title) ==
|
||||||
|
latest_read_entry.pages
|
||||||
|
next_entry latest_read_entry
|
||||||
|
else
|
||||||
|
latest_read_entry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: More concise title?
|
||||||
|
def get_last_read_for_continue_reading(username, entry_obj)
|
||||||
|
last_read = load_last_read username, entry_obj.title
|
||||||
|
if last_read.nil? # grab from previous entry if current entry hasn't been started yet
|
||||||
|
previous_entry = previous_entry(entry_obj)
|
||||||
|
return load_last_read username, previous_entry.title if previous_entry
|
||||||
|
end
|
||||||
|
last_read
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TitleInfo
|
class TitleInfo
|
||||||
@ -337,6 +401,8 @@ class TitleInfo
|
|||||||
property entry_display_name = {} of String => String
|
property entry_display_name = {} of String => String
|
||||||
property cover_url = ""
|
property cover_url = ""
|
||||||
property entry_cover_url = {} of String => String
|
property entry_cover_url = {} of String => String
|
||||||
|
property last_read = {} of String => Hash(String, Time)
|
||||||
|
property date_added = {} of String => Time
|
||||||
|
|
||||||
@[JSON::Field(ignore: true)]
|
@[JSON::Field(ignore: true)]
|
||||||
property dir : String = ""
|
property dir : String = ""
|
||||||
@ -440,4 +506,85 @@ class Library
|
|||||||
end
|
end
|
||||||
Logger.debug "Scan completed"
|
Logger.debug "Scan completed"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_continue_reading_entries(username)
|
||||||
|
# map: get the continue-reading entry or nil for each Title
|
||||||
|
# select: select only entries (and ignore Nil's) from the array
|
||||||
|
# produced by map
|
||||||
|
continue_reading_entries = titles.map { |t|
|
||||||
|
get_continue_reading_entry username, t
|
||||||
|
}.select Entry
|
||||||
|
|
||||||
|
continue_reading = continue_reading_entries.map { |e|
|
||||||
|
{
|
||||||
|
entry: e,
|
||||||
|
percentage: e.book.load_percentage(username, e.title),
|
||||||
|
last_read: get_relevant_last_read(username, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sort by by last_read, most recent first (nils at the end)
|
||||||
|
continue_reading.sort! { |a, b|
|
||||||
|
next 0 if a[:last_read].nil? && b[:last_read].nil?
|
||||||
|
next 1 if a[:last_read].nil?
|
||||||
|
next -1 if b[:last_read].nil?
|
||||||
|
b[:last_read].not_nil! <=> a[:last_read].not_nil!
|
||||||
|
}[0..11]
|
||||||
|
end
|
||||||
|
|
||||||
|
alias RA = NamedTuple(entry: Entry, percentage: Float64, grouped_count: Int32)
|
||||||
|
|
||||||
|
def get_recently_added_entries(username)
|
||||||
|
entries = [] of Entry
|
||||||
|
titles.each do |t|
|
||||||
|
t.entries.each { |e| entries << e }
|
||||||
|
end
|
||||||
|
entries.sort! { |a, b| b.date_added <=> a.date_added }
|
||||||
|
entries.select! { |e| e.date_added > 3.months.ago }
|
||||||
|
|
||||||
|
# Group Entries if neighbour is same Title
|
||||||
|
recently_added = [] of RA
|
||||||
|
entries.each do |e|
|
||||||
|
last = recently_added.last?
|
||||||
|
if last && e.title_id == last[:entry].title_id
|
||||||
|
# A NamedTuple is immutable, so we have to cast it to a Hash first
|
||||||
|
last_hash = last.to_h
|
||||||
|
count = last_hash[:grouped_count].as(Int32)
|
||||||
|
last_hash[:grouped_count] = count + 1
|
||||||
|
recently_added[recently_added.size - 1] = RA.from last_hash
|
||||||
|
else
|
||||||
|
recently_added << {
|
||||||
|
entry: e,
|
||||||
|
percentage: e.book.load_percentage(username, e.title),
|
||||||
|
grouped_count: 1,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
recently_added[0..11]
|
||||||
|
end
|
||||||
|
|
||||||
|
private def get_continue_reading_entry(username, title)
|
||||||
|
in_progress_entries = title.entries.select do |e|
|
||||||
|
title.load_progress(username, e.title) > 0
|
||||||
|
end
|
||||||
|
return nil if in_progress_entries.empty?
|
||||||
|
|
||||||
|
latest_read_entry = in_progress_entries[-1]
|
||||||
|
if title.load_progress(username, latest_read_entry.title) ==
|
||||||
|
latest_read_entry.pages
|
||||||
|
title.next_entry latest_read_entry
|
||||||
|
else
|
||||||
|
latest_read_entry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def get_relevant_last_read(username, entry_obj)
|
||||||
|
last_read = entry_obj.book.load_last_read username, entry_obj.title
|
||||||
|
if last_read.nil? # grab from previous entry if current entry hasn't been started yet
|
||||||
|
previous_entry = entry_obj.book.previous_entry(entry_obj)
|
||||||
|
return entry_obj.book.load_last_read username, previous_entry.title if previous_entry
|
||||||
|
end
|
||||||
|
last_read
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -37,12 +37,12 @@ class MainRouter < Router
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
get "/" do |env|
|
get "/library" do |env|
|
||||||
begin
|
begin
|
||||||
titles = @context.library.titles
|
titles = @context.library.titles
|
||||||
username = get_username env
|
username = get_username env
|
||||||
percentage = titles.map &.load_percetage username
|
percentage = titles.map &.load_percentage username
|
||||||
layout "index"
|
layout "library"
|
||||||
rescue e
|
rescue e
|
||||||
@context.error e
|
@context.error e
|
||||||
env.response.status_code = 500
|
env.response.status_code = 500
|
||||||
@ -54,7 +54,7 @@ class MainRouter < Router
|
|||||||
title = (@context.library.get_title env.params.url["title"]).not_nil!
|
title = (@context.library.get_title env.params.url["title"]).not_nil!
|
||||||
username = get_username env
|
username = get_username env
|
||||||
percentage = title.entries.map { |e|
|
percentage = title.entries.map { |e|
|
||||||
title.load_percetage username, e.title
|
title.load_percentage username, e.title
|
||||||
}
|
}
|
||||||
layout "title"
|
layout "title"
|
||||||
rescue e
|
rescue e
|
||||||
@ -67,5 +67,20 @@ class MainRouter < Router
|
|||||||
mangadex_base_url = Config.current.mangadex["base_url"]
|
mangadex_base_url = Config.current.mangadex["base_url"]
|
||||||
layout "download"
|
layout "download"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
get "/" do |env|
|
||||||
|
begin
|
||||||
|
username = get_username env
|
||||||
|
continue_reading = @context.library.get_continue_reading_entries username
|
||||||
|
recently_added = @context.library.get_recently_added_entries username
|
||||||
|
titles = @context.library.titles
|
||||||
|
new_user = ! titles.any? { |t| t.load_percentage(username) > 0 }
|
||||||
|
empty_library = titles.size == 0
|
||||||
|
layout "home"
|
||||||
|
rescue e
|
||||||
|
@context.error e
|
||||||
|
env.response.status_code = 500
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
13
src/util.cr
13
src/util.cr
@ -135,3 +135,16 @@ end
|
|||||||
macro render_component(filename)
|
macro render_component(filename)
|
||||||
render "src/views/components/#{{{filename}}}.ecr"
|
render "src/views/components/#{{{filename}}}.ecr"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Works in all Unix systems. Follows https://github.com/crystal-lang/crystal/
|
||||||
|
# blob/master/src/crystal/system/unix/file_info.cr#L42-L48
|
||||||
|
def ctime(file_path : String) : Time
|
||||||
|
res = LibC.stat(file_path, out stat)
|
||||||
|
raise "Unable to get ctime of file #{file_path}" if res != 0
|
||||||
|
|
||||||
|
{% if flag?(:darwin) %}
|
||||||
|
Time.new stat.st_ctimespec, Time::Location::UTC
|
||||||
|
{% else %}
|
||||||
|
Time.new stat.st_ctim, Time::Location::UTC
|
||||||
|
{% end %}
|
||||||
|
end
|
||||||
|
132
src/views/home.ecr
Normal file
132
src/views/home.ecr
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<%- if new_user && empty_library -%>
|
||||||
|
|
||||||
|
<div class="uk-container uk-text-center">
|
||||||
|
<i class="fas fa-plus" style="font-size: 80px;"></i>
|
||||||
|
<h2>Add your first manga</h2>
|
||||||
|
<p style="margin-bottom: 40px;">We can't find any files yet. Add some to your library and they'll appear here.</p>
|
||||||
|
<dl class="uk-description-list">
|
||||||
|
<dt style="font-weight: 500;">Current library path</dt>
|
||||||
|
<dd><code><%= Config.current.library_path %></code></dd>
|
||||||
|
<dt style="font-weight: 500;">Want to change your library path?</dt>
|
||||||
|
<dd>Update <code>config.yml</code> located at: <code><%= Config.current.path %></code></dd>
|
||||||
|
<dt style="font-weight: 500;">Can't see your files yet?</dt>
|
||||||
|
<dd>You must wait <%= Config.current.scan_interval %> minutes for the library scan to complete
|
||||||
|
<% if is_admin %>, or manually re-scan from <a href="/admin">Admin</a><% end %>.</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%- elsif new_user && empty_library == false -%>
|
||||||
|
|
||||||
|
<div class="uk-container uk-text-center">
|
||||||
|
<i class="fas fa-book-open" style="font-size: 80px;"></i>
|
||||||
|
<h2>Read your first manga</h2>
|
||||||
|
<p>Once you start reading, Mango will remember where you left off
|
||||||
|
and show your entries here.</p>
|
||||||
|
<a href="/library" class="uk-button uk-button-default">View library</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%- elsif new_user == false && empty_library == false -%>
|
||||||
|
|
||||||
|
<%- if continue_reading.empty? && recently_added.empty? -%>
|
||||||
|
<div class="uk-container uk-text-center">
|
||||||
|
<img src="/img/banner.png" style="max-width: 400px; padding: 0 20px;">
|
||||||
|
<p>A self-hosted manga server and reader</p>
|
||||||
|
<a href="/library" class="uk-button uk-button-default">View library</a>
|
||||||
|
</div>
|
||||||
|
<%- end -%>
|
||||||
|
|
||||||
|
<%- unless continue_reading.empty? -%>
|
||||||
|
<h2 class="uk-title home-headings">Continue Reading</h2>
|
||||||
|
<div id="item-container-continue" class="uk-child-width-1-4@m uk-child-width-1-2" uk-grid>
|
||||||
|
<%- continue_reading.each do |cr| -%>
|
||||||
|
<div class="item" data-mtime="<%= cr[:entry].mtime.to_unix %>" data-progress="<%= cr[:percentage] %>" id="<%= cr[:entry].id %>">
|
||||||
|
<a class="acard">
|
||||||
|
<div class="uk-card uk-card-default" onclick="showModal("<%= cr[:entry].encoded_path %>", '<%= cr[:entry].pages %>', <%= (cr[:percentage] * 100).round(1) %>, "<%= cr[:entry].book.encoded_display_name %>", "<%= cr[:entry].encoded_display_name %>", '<%= cr[:entry].title_id %>', '<%= cr[:entry].id %>')">
|
||||||
|
<div class="uk-card-media-top">
|
||||||
|
<img data-src="<%= cr[:entry].cover_url %>" alt="" data-width data-height uk-img>
|
||||||
|
</div>
|
||||||
|
<div class="uk-card-body">
|
||||||
|
<div class="uk-card-badge uk-label"><%= (cr[:percentage] * 100).round(1) %>%</div>
|
||||||
|
<h3 class="uk-card-title break-word" data-title="<%= cr[:entry].display_name.gsub("\"", """) %>"><%= cr[:entry].display_name %></h3>
|
||||||
|
<p><%= cr[:entry].pages %> pages</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<%- end -%>
|
||||||
|
</div>
|
||||||
|
<%- end -%>
|
||||||
|
|
||||||
|
<%- unless recently_added.empty? -%>
|
||||||
|
<h2 class="uk-title home-headings">Recently Added</h2>
|
||||||
|
<div id="item-container-continue" class="uk-child-width-1-4@m uk-child-width-1-2" uk-grid>
|
||||||
|
<%- recently_added.each do |ra| -%>
|
||||||
|
<%- if ra[:grouped_count] == 1 -%>
|
||||||
|
<div class="item" data-progress="<%= ra[:percentage] %>" id="<%= ra[:entry].id %>">
|
||||||
|
<a class="acard">
|
||||||
|
<div class="uk-card uk-card-default" onclick="showModal("<%= ra[:entry].encoded_path %>", '<%= ra[:entry].pages %>', <%= (ra[:percentage] * 100).round(1) %>, "<%= ra[:entry].book.encoded_display_name %>", "<%= ra[:entry].encoded_display_name %>", '<%= ra[:entry].title_id %>', '<%= ra[:entry].id %>')">
|
||||||
|
<div class="uk-card-media-top">
|
||||||
|
<img data-src="<%= ra[:entry].cover_url %>" alt="" data-width data-height uk-img>
|
||||||
|
</div>
|
||||||
|
<div class="uk-card-body">
|
||||||
|
<div class="uk-card-badge uk-label"><%= (ra[:percentage] * 100).round(1) %>%</div>
|
||||||
|
<h3 class="uk-card-title break-word" data-title="<%= ra[:entry].display_name.gsub("\"", """) %>"><%= ra[:entry].display_name %></h3>
|
||||||
|
<p><%= ra[:entry].pages %> pages</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<%- else -%>
|
||||||
|
<div class="item">
|
||||||
|
<a class="acard" href="/book/<%= ra[:entry].book.id %>">
|
||||||
|
<div class="uk-card uk-card-default">
|
||||||
|
<div class="uk-card-media-top">
|
||||||
|
<img data-src="<%= ra[:entry].cover_url %>" data-width data-height alt="" uk-img>
|
||||||
|
</div>
|
||||||
|
<div class="uk-card-body">
|
||||||
|
<h3 class="uk-card-title break-word" data-title="<%= ra[:entry].book.display_name.gsub("\"", """) %>"><%= ra[:entry].book.display_name %></h3>
|
||||||
|
<p><%= ra[:grouped_count] %> new entries</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<%- end -%>
|
||||||
|
<%- end -%>
|
||||||
|
</div>
|
||||||
|
<%- end -%>
|
||||||
|
|
||||||
|
<!-- TODO: DRY this code with calls in other ecr files? eg. title.ecr -->
|
||||||
|
<div id="modal" class="uk-flex-top" uk-modal>
|
||||||
|
<div class="uk-modal-dialog uk-margin-auto-vertical">
|
||||||
|
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||||
|
<div class="uk-modal-header">
|
||||||
|
<div>
|
||||||
|
<h4 class="uk-margin-remove-bottom"><a id="modal-title-link"></a></h4>
|
||||||
|
<h3 class="uk-modal-title break-word uk-margin-remove-top" id="modal-entry-title"><span></span></h3>
|
||||||
|
</div>
|
||||||
|
<p class="uk-text-meta uk-margin-remove-bottom break-word" id="path-text"></p>
|
||||||
|
<p class="uk-text-meta uk-margin-remove-top" id="pages-text"></p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-modal-body">
|
||||||
|
<p>Read</p>
|
||||||
|
<p uk-margin>
|
||||||
|
<a id="beginning-btn" class="uk-button uk-button-default">From beginning</a>
|
||||||
|
<a id="continue-btn" class="uk-button uk-button-primary"></a>
|
||||||
|
</p>
|
||||||
|
<p>Progress</p>
|
||||||
|
<p uk-margin>
|
||||||
|
<button id="read-btn" class="uk-button uk-button-default">Mark as read (100%)</button>
|
||||||
|
<button id="unread-btn" class="uk-button uk-button-default">Mark as unread (0%)</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%- end -%>
|
||||||
|
|
||||||
|
<% content_for "script" do %>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jQuery.dotdotdot/4.0.11/dotdotdot.js"></script>
|
||||||
|
<script src="/js/dots.js"></script>
|
||||||
|
<script src="/js/alert.js"></script>
|
||||||
|
<script src="/js/title.js"></script>
|
||||||
|
<% end %>
|
@ -23,6 +23,7 @@
|
|||||||
<div class="uk-offcanvas-bar uk-flex uk-flex-column">
|
<div class="uk-offcanvas-bar uk-flex uk-flex-column">
|
||||||
<ul class="uk-nav uk-nav-primary uk-nav-center uk-margin-auto-vertical">
|
<ul class="uk-nav uk-nav-primary uk-nav-center uk-margin-auto-vertical">
|
||||||
<li><a href="<%= base_url %>">Home</a></li>
|
<li><a href="<%= base_url %>">Home</a></li>
|
||||||
|
<li><a href="<%= base_url %>library">Library</a></li>
|
||||||
<% if is_admin %>
|
<% if is_admin %>
|
||||||
<li><a href="<%= base_url %>admin">Admin</a></li>
|
<li><a href="<%= base_url %>admin">Admin</a></li>
|
||||||
<li><a href="<%= base_url %>download">Download</a></li>
|
<li><a href="<%= base_url %>download">Download</a></li>
|
||||||
@ -44,6 +45,7 @@
|
|||||||
<a class="uk-navbar-item uk-logo" href="<%= base_url %>"><img src="<%= base_url %>img/icon.png"></a>
|
<a class="uk-navbar-item uk-logo" href="<%= base_url %>"><img src="<%= base_url %>img/icon.png"></a>
|
||||||
<ul class="uk-navbar-nav">
|
<ul class="uk-navbar-nav">
|
||||||
<li><a href="<%= base_url %>">Home</a></li>
|
<li><a href="<%= base_url %>">Home</a></li>
|
||||||
|
<li><a href="<%= base_url %>library">Library</a></li>
|
||||||
<% if is_admin %>
|
<% if is_admin %>
|
||||||
<li><a href="<%= base_url %>admin">Admin</a></li>
|
<li><a href="<%= base_url %>admin">Admin</a></li>
|
||||||
<li><a href="<%= base_url %>download">Download</a></li>
|
<li><a href="<%= base_url %>download">Download</a></li>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<ul class="uk-breadcrumb">
|
<ul class="uk-breadcrumb">
|
||||||
<li><a href="<%= base_url %>">Library</a></li>
|
<li><a href="<%= base_url %>library">Library</a></li>
|
||||||
<%- title.parents.each do |t| -%>
|
<%- title.parents.each do |t| -%>
|
||||||
<li><a href="<%= base_url %>book/<%= t.id %>"><%= t.display_name %></a></li>
|
<li><a href="<%= base_url %>book/<%= t.id %>"><%= t.display_name %></a></li>
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
@ -47,7 +47,7 @@
|
|||||||
<button class="uk-modal-close-default" type="button" uk-close></button>
|
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||||
<div class="uk-modal-header">
|
<div class="uk-modal-header">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="uk-modal-title break-word" id="modal-title"><span></span>
|
<h3 class="uk-modal-title break-word" id="modal-entry-title"><span></span>
|
||||||
|
|
||||||
<% if is_admin %>
|
<% if is_admin %>
|
||||||
<a class="uk-icon-button" uk-icon="icon:pencil"></a>
|
<a class="uk-icon-button" uk-icon="icon:pencil"></a>
|
||||||
@ -77,7 +77,7 @@
|
|||||||
<button class="uk-modal-close-default" type="button" uk-close></button>
|
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||||
<div class="uk-modal-header">
|
<div class="uk-modal-header">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="uk-modal-title break-word">Edit</h3>
|
<h3 class="uk-modal-title break-word" id="modal-entry-title">Edit</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="uk-modal-body">
|
<div class="uk-modal-body">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user