diff --git a/plugins/goodreads.koplugin/doublekeyvaluepage.lua b/plugins/goodreads.koplugin/doublekeyvaluepage.lua new file mode 100644 index 000000000..1d920eeae --- /dev/null +++ b/plugins/goodreads.koplugin/doublekeyvaluepage.lua @@ -0,0 +1,352 @@ +local InputContainer = require("ui/widget/container/inputcontainer") +local FrameContainer = require("ui/widget/container/framecontainer") +local VerticalGroup = require("ui/widget/verticalgroup") +local VerticalSpan = require("ui/widget/verticalspan") +local OverlapGroup = require("ui/widget/overlapgroup") +local LeftContainer = require("ui/widget/container/topcontainer") +local HorizontalSpan = require("ui/widget/horizontalspan") +local LineWidget = require("ui/widget/linewidget") +local Blitbuffer = require("ffi/blitbuffer") +local CloseButton = require("ui/widget/closebutton") +local UIManager = require("ui/uimanager") +local TextWidget = require("ui/widget/textwidget") +local GestureRange = require("ui/gesturerange") +local RenderText = require("ui/rendertext") +local Geom = require("ui/geometry") +local Font = require("ui/font") +local Device = require("device") +local Screen = Device.screen +local GoodReadersApi = require("goodreadsapi") +local LuaSettings = require("luasettings") +local DataStorage = require("datastorage") +local _ = require("gettext") +local InfoMessage = require("ui/widget/infomessage") + +local DoubleKeyValueTitle = VerticalGroup:new{ + kv_page = nil, + title = "", + tface = Font:getFace("tfont"), + align = "left", +} + +function DoubleKeyValueTitle:init() + self.close_button = CloseButton:new{ window = self } + local btn_width = self.close_button:getSize().w + local title_txt_width = RenderText:sizeUtf8Text( + 0, self.width, self.tface, self.title).x + local show_title_txt + if self.width < (title_txt_width + btn_width) then + show_title_txt = RenderText:truncateTextByWidth( + self.title, self.tface, self.width - btn_width) + else + show_title_txt = self.title + end + -- title and close button + table.insert(self, OverlapGroup:new{ + dimen = { w = self.width }, + TextWidget:new{ + text = show_title_txt, + face = self.tface, + }, + self.close_button, + }) + -- page count and separation line + self.page_cnt = FrameContainer:new{ + padding = 4, + margin = 0, + bordersize = 0, + background = Blitbuffer.COLOR_WHITE, + -- overlap offset x will be updated in setPageCount method + overlap_offset = {0, -15}, + TextWidget:new{ + text = "", -- page count + fgcolor = Blitbuffer.COLOR_GREY, + face = Font:getFace("ffont", 20), + }, + } + self.title_bottom = OverlapGroup:new{ + dimen = { w = self.width, h = Screen:scaleBySize(2) }, + LineWidget:new{ + dimen = Geom:new{ w = self.width, h = Screen:scaleBySize(2) }, + background = Blitbuffer.COLOR_GREY, + style = "solid", + }, + self.page_cnt, + } + table.insert(self, self.title_bottom) + table.insert(self, VerticalSpan:new{ width = Screen:scaleBySize(5) }) +end + +function DoubleKeyValueTitle:setPageCount(curr, total) + if total == 1 then + -- remove page count if there is only one page + table.remove(self.title_bottom, 2) + return + end + self.page_cnt[1]:setText(curr .. "/" .. total) + self.page_cnt.overlap_offset[1] = (self.width - self.page_cnt:getSize().w - 10) + self.title_bottom[2] = self.page_cnt +end + +function DoubleKeyValueTitle:onClose() + self.kv_page:onClose() + return true +end + +local DoubleKeyValueItem = InputContainer:new{ + key = nil, + value = nil, + cface_up = Font:getFace("cfont", 22), + cface_down = Font:getFace("cfont", 18), + width = nil, + height = nil, + align = "left", +} + +function DoubleKeyValueItem:init() + self.dimen = Geom:new{align = "left", w = self.width, h = self.height} + if self.callback and Device:isTouchDevice() then + self.ges_events.Tap = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + } + } + end + local key_w = RenderText:sizeUtf8Text(0, self.width, self.cface_down, self.key).x + local value_w = RenderText:sizeUtf8Text(0, self.width, self.cface_up, self.value).x + if key_w > self.width then + self.show_key = RenderText:truncateTextByWidth(self.key, self.cface_down, self.width) + else + self.show_key = self.key + end + if value_w > self.width then + self.show_value = RenderText:truncateTextByWidth(self.value, self.cface_up, self.width) + else + self.show_value = self.value + end + local h = self.dimen.h / 2 + local w = self.dimen.w + self[1] = FrameContainer:new{ + padding = 10, + bordersize = 0, + VerticalGroup:new{ + dimen = Geom:new{ h = h, w = w }, + padding = 10, + LeftContainer:new{ + padding = 10, + dimen = Geom:new{ h = h, w = w }, + TextWidget:new{ + text = self.show_value, + padding = 10, + face = self.cface_up, + } + }, + LeftContainer:new{ + padding = 10, + dimen = Geom:new{ h = h / 5 , w = w }, + HorizontalSpan:new{ width = Screen:scaleBySize(15), height = 3 } + }, + LeftContainer:new{ + padding = 10, + dimen = Geom:new{ h = h, w = w }, + TextWidget:new{ + text = self.show_key, + padding = 10, + face = self.cface_down, + } + } + } + } +end + +function DoubleKeyValueItem:onTap() + local info = InfoMessage:new{text = _("Please wait...")} + UIManager:show(info) + UIManager:forceRePaint() + self.callback() + UIManager:close(info) + return true +end + +local DoubleKeyValuePage = InputContainer:new{ + title = "", + width = nil, + height = nil, + show_page = 1, + text_input = "", + pages = 1, + goodreadersKey = "", +} + +function DoubleKeyValuePage:readGRSettings() + self.gr_settings = LuaSettings:open(DataStorage:getSettingsDir().."/goodreadssettings.lua") + return self.gr_settings +end + +function DoubleKeyValuePage:saveGRSettings(setting) + if not self.gr_settings then self:readGRSettings() end + self.gr_settings:saveSetting("goodreads", setting) + self.gr_settings:flush() +end + +function DoubleKeyValuePage:init() + self.screen_width = Screen:getSize().w + self.screen_height = Screen:getSize().h + local gr_sett = self:readGRSettings().data + if gr_sett.goodreads then + self.goodreadersKey = gr_sett.goodreads.key + self.goodreadersSecret = gr_sett.goodreads.secret + end + self.kv_pairs = GoodReadersApi:showData(self.text_input, self.search_type, 1, self.goodreadersKey) + self.total_res = GoodReadersApi:getTotalResults() + if self.total_res == nil then + self.total_res = 0 + end + self.total_res = tonumber(self.total_res) + if self.kv_pairs == nil then + self.kv_pairs = {} + end + self.dimen = Geom:new{ + w = self.width or self.screen_width, + h = self.height or self.screen_height, + } + if Device:isTouchDevice() then + self.ges_events.Swipe = { + GestureRange:new{ + ges = "swipe", + range = self.dimen, + } + } + end + local padding = Screen:scaleBySize(10) + self.item_width = self.dimen.w - 2 * padding + self.item_height = Screen:scaleBySize(45) + -- setup title bar + self.title_bar = DoubleKeyValueTitle:new{ + title = self.title, + width = self.item_width, + height = self.item_height, + kv_page = self, + } + -- setup main content + self.item_margin = self.item_height / 4 + local line_height = self.item_height + 2 * self.item_margin + local content_height = self.dimen.h - self.title_bar:getSize().h + self.max_loaded_pages = 1 + self.items_per_page = math.floor(content_height / line_height) + self.pages = math.ceil(self.total_res / self.items_per_page) + self.main_content = VerticalGroup:new{} + self:_populateItems() + -- assemble page + self[1] = FrameContainer:new{ + height = self.dimen.h, + padding = padding, + bordersize = 0, + background = Blitbuffer.COLOR_WHITE, + VerticalGroup:new{ + self.title_bar, + self.main_content, + }, + } +end + +function DoubleKeyValuePage:nextPage() + local new_page = math.min(self.show_page + 1, self.pages) + if (new_page * self.items_per_page > #self.kv_pairs) and (self.max_loaded_pages < new_page) + and #self.kv_pairs < self.total_res then + local api_page = math.floor(new_page * self.items_per_page / 20 ) + 1 + -- load new portion of data + local new_pair = GoodReadersApi:showData(self.text_input, self.search_type, api_page, self.goodreadersKey ) + if new_pair == nil then return end + for _, v in pairs(new_pair) do + table.insert(self.kv_pairs, v) + end + self.refresh = true + end + if new_page > self.show_page then + if self.max_loaded_pages == self.show_page then + self.max_loaded_pages = self.max_loaded_pages + 1 + end + self.show_page = new_page + self:_populateItems() + end +end + +function DoubleKeyValuePage:prevPage() + local new_page = math.max(self.show_page - 1, 1) + if new_page < self.show_page then + self.show_page = new_page + self:_populateItems() + end +end + +-- make sure self.item_margin and self.item_height are set before calling this +function DoubleKeyValuePage:_populateItems() + self.main_content:clear() + local idx_offset = (self.show_page - 1) * self.items_per_page + for idx = 1, self.items_per_page do + local entry = self.kv_pairs[idx_offset + idx] + if entry == nil then break end + table.insert(self.main_content, + VerticalSpan:new{ align = "left", width = self.item_margin }) + if type(entry) == "table" then + table.insert( + self.main_content, + DoubleKeyValueItem:new{ + height = self.item_height, + width = self.item_width, + key = entry[1], + value = entry[2], + align = "left", + callback = entry.callback, + } + ) + elseif type(entry) == "string" then + local c = string.sub(entry, 1, 1) + if c == "-" then + table.insert(self.main_content, LineWidget:new{ + background = Blitbuffer.COLOR_LIGHT_GREY, + dimen = Geom:new{ + w = self.item_width, + h = Screen:scaleBySize(2) + }, + style = "solid", + }) + end + end + table.insert(self.main_content, + VerticalSpan:new{ width = self.item_margin }) + end + self.title_bar:setPageCount(self.show_page, self.pages) + UIManager:setDirty(self, function() + return "ui", self.dimen + end) +end + +function DoubleKeyValuePage:onSwipe(arg, ges_ev) + if ges_ev.direction == "west" then + local new_page = math.min(self.show_page + 1, self.pages) + if (new_page * self.items_per_page > #self.kv_pairs) and (self.max_loaded_pages < new_page) + and #self.kv_pairs < self.total_res then + local info = InfoMessage:new{text = _("Please wait...")} + UIManager:show(info) + UIManager:forceRePaint() + self:nextPage() + UIManager:close(info) + else + self:nextPage() + end + return true + elseif ges_ev.direction == "east" then + self:prevPage() + return true + end +end + +function DoubleKeyValuePage:onClose() + UIManager:close(self) + return true +end + +return DoubleKeyValuePage diff --git a/plugins/goodreads.koplugin/goodreaderbook.lua b/plugins/goodreads.koplugin/goodreaderbook.lua new file mode 100644 index 000000000..6d2871825 --- /dev/null +++ b/plugins/goodreads.koplugin/goodreaderbook.lua @@ -0,0 +1,259 @@ +local InputContainer = require("ui/widget/container/inputcontainer") +local FrameContainer = require("ui/widget/container/framecontainer") +local CenterContainer = require("ui/widget/container/centercontainer") +local LeftContainer = require("ui/widget/container/leftcontainer") +local HorizontalGroup = require("ui/widget/horizontalgroup") +local OverlapGroup = require("ui/widget/overlapgroup") +local VerticalGroup = require("ui/widget/verticalgroup") +local HorizontalSpan = require("ui/widget/horizontalspan") +local VerticalSpan = require("ui/widget/verticalspan") +local LineWidget = require("ui/widget/linewidget") +local TextWidget = require("ui/widget/textwidget") +local ScrollTextWidget = require("ui/widget/scrolltextwidget") +local ImageWidget = require("ui/widget/imagewidget") +local TextBoxWidget = require("ui/widget/textboxwidget") +local CloseButton = require("ui/widget/closebutton") +local UIManager = require("ui/uimanager") +local Geom = require("ui/geometry") +local Blitbuffer = require("ffi/blitbuffer") +local Screen = require("device").screen +local Font = require("ui/font") +local _ = require("gettext") +local T = require("ffi/util").template +local Pic = require("ffi/pic") + +local GoodReaderBook = InputContainer:new{ + padding = Screen:scaleBySize(15), +} + +function GoodReaderBook:init() + self.small_font_face = Font:getFace("ffont", 16) + self.medium_font_face = Font:getFace("ffont", 18) + self.large_font_face = Font:getFace("ffont", 22) + self.screen_width = Screen:getSize().w + self.screen_height = Screen:getSize().h + UIManager:setDirty(self, function() + return "ui", self.dimen + end) + self[1] = FrameContainer:new{ + width = self.screen_width, + height = self.screen_height, + background = Blitbuffer.COLOR_WHITE, + bordersize = 0, + padding = 0, + self:getStatusContent(self.screen_width), + } +end + +function GoodReaderBook:getStatusContent(width) + return VerticalGroup:new{ + align = "left", + OverlapGroup:new{ + dimen = Geom:new{ w = width, h = Screen:scaleBySize(30) }, + CloseButton:new{ window = self }, + }, + self:genHeader(_("Book info")), + self:genBookInfoGroup(), + self:genHeader(_("Review")), + self:bookReview(), + } +end + +function GoodReaderBook:genHeader(title) + local header_title = TextWidget:new{ + text = title, + face = self.medium_font_face, + fgcolor = Blitbuffer.gray(0.4), + } + local padding_span = HorizontalSpan:new{ width = self.padding} + local line_width = (self.screen_width - header_title:getSize().w) / 2 - self.padding * 2 + local line_container = LeftContainer:new{ + dimen = Geom:new{ w = line_width, h = self.screen_height / 25 }, + LineWidget:new{ + background = Blitbuffer.gray(0.2), + dimen = Geom:new{ + w = line_width, + h = 2, + } + } + } + + return VerticalGroup:new{ + VerticalSpan:new{ width = Screen:scaleBySize(5) }, + HorizontalGroup:new{ + align = "center", + padding_span, + line_container, + padding_span, + header_title, + padding_span, + line_container, + padding_span, + }, + VerticalSpan:new{ width = Screen:scaleBySize(5) }, + } +end + +function GoodReaderBook:genBookInfoGroup() + local split_span_width = self.screen_width * 0.05 + local img_width, img_height + if Screen:getScreenMode() == "landscape" then + img_width = Screen:scaleBySize(132) + img_height = Screen:scaleBySize(184) + else + img_width = Screen:scaleBySize(132 * 1.5) + img_height = Screen:scaleBySize(184 * 1.5) + end + local height = img_height + local width = self.screen_width - 1.5 * split_span_width - img_width + -- title + local book_meta_info_group = VerticalGroup:new{ + align = "center", + TextBoxWidget:new{ + text = self.dates.title, + face = self.medium_font_face, + padding = 2, + alignment = "center", + width = width, + }, + } + -- author + local text_author = TextBoxWidget:new{ + text = self.dates.author, + width = width, + face = self.large_font_face, + alignment = "center", + } + table.insert(book_meta_info_group, + CenterContainer:new{ + dimen = Geom:new{ w = width, h = text_author:getSize().h }, + text_author + } + ) + --span + local span_author = VerticalSpan:new{ width = height * 0.1 } + table.insert(book_meta_info_group, + CenterContainer:new{ + dimen = Geom:new{ w = width, h = Screen:scaleBySize(10) }, + span_author + } + ) + -- series + local text_series = TextWidget:new{ + text = T(_("Series: %1"), self.dates.series), + face = self.small_font_face, + padding = 2, + } + table.insert(book_meta_info_group, + CenterContainer:new{ + dimen = Geom:new{ w = width, h = text_series:getSize().h }, + text_series + } + ) + -- rating + local text_rating = TextWidget:new{ + text = T(_("Rating: %1"), self.dates.rating), + face = self.small_font_face, + padding = 2, + } + table.insert(book_meta_info_group, + CenterContainer:new{ + dimen = Geom:new{ w = width, h = text_rating:getSize().h }, + text_rating + } + ) + -- pages + local text_pages = TextWidget:new{ + text = T(_("Pages: %1"), self.dates.pages), + face = self.small_font_face, + padding = 2, + } + table.insert(book_meta_info_group, + CenterContainer:new{ + dimen = Geom:new{ w = width, h = text_pages:getSize().h }, + text_pages + } + ) + -- relesse date + local text_release = TextWidget:new{ + text = T(_("Release date: %1"), self.dates.release), + face = self.small_font_face, + padding = 2, + } + table.insert(book_meta_info_group, + CenterContainer:new{ + dimen = Geom:new{ w = width, h = text_release:getSize().h }, + text_release + } + ) + local book_info_group = HorizontalGroup:new{ + align = "top", + HorizontalSpan:new{ width = split_span_width } + } + --thumbnail + local http = require("socket.http") + local body = http.request(self.dates.image) + local image = false + if body then image = Pic.openJPGDocumentFromMem(body) end + if image then + table.insert(book_info_group, ImageWidget:new{ + image = image.image_bb, + width = img_width, + height = img_height, + autoscale = false, + }) + else + table.insert(book_info_group, ImageWidget:new{ + file = "resources/goodreadsnophoto.png", + width = img_width, + height = img_height, + autoscale = false, + }) + end + + local book_info_group_span = HorizontalGroup:new{ + align = "top", + HorizontalSpan:new{ width = split_span_width / 2 } + } + table.insert(book_info_group, book_info_group_span) + table.insert(book_info_group, CenterContainer:new{ + dimen = Geom:new{ w = width , h = height }, + book_meta_info_group, + }) + return CenterContainer:new{ + dimen = Geom:new{ w = self.screen_width, h = self.screen_height * 0.35 }, + book_info_group, + } +end + +function GoodReaderBook:bookReview() + local book_meta_info_group = VerticalGroup:new{ + align = "center", + padding = 0, + bordersize = 0, + ScrollTextWidget:new{ + text = self.dates.description, + face = self.medium_font_face, + padding = 0, + width = self.screen_width * 0.9, + height = self.screen_height * 0.48, + dialog = self, + } + } + return CenterContainer:new{ + dimen = Geom:new{ w = self.screen_width, h = self.screen_height * 0.50 }, + book_meta_info_group, + } +end + +function GoodReaderBook:onAnyKeyPressed() + return self:onClose() +end + +function GoodReaderBook:onClose() + UIManager:setDirty("all") + UIManager:close(self) + return true +end + +return GoodReaderBook diff --git a/plugins/goodreads.koplugin/goodreadsapi.lua b/plugins/goodreads.koplugin/goodreadsapi.lua new file mode 100644 index 000000000..ffec436e4 --- /dev/null +++ b/plugins/goodreads.koplugin/goodreadsapi.lua @@ -0,0 +1,212 @@ +local InputContainer = require("ui/widget/container/inputcontainer") +local GoodReaderBook = require("goodreaderbook") +local InfoMessage = require("ui/widget/infomessage") +local UIManager = require("ui/uimanager") +local url = require('socket.url') +local socket = require('socket') +local http = require('socket.http') +local https = require('ssl.https') +local ltn12 = require('ltn12') +local _ = require("gettext") + +local GoodReadsApi = InputContainer:new { + goodreaders_key = "", + goodreaders_secret = "", + total_result = 0, +} + +function GoodReadsApi:init() +end + +local function genSearchURL(text_search, userApi, search_type, npage) + if (text_search) then + text_search = string.gsub (text_search, "\n", "\r\n") + text_search = string.gsub (text_search, "([^%w %-%_%.%~])", + function (c) return string.format ("%%%02X", string.byte(c)) end) + text_search = string.gsub (text_search, " ", "+") + end + return (string.format( + "http://www.goodreads.com/search?q=%s&search[field]=%s&format=xml&key=%s&page=%s", + text_search, + search_type, + userApi, + npage + )) +end + +local function genIdUrl(id, userApi) + return (string.format( + "https://www.goodreads.com/book/show/%s?format=xml&key=%s", + id, + userApi + )) +end + +function GoodReadsApi:fetchXml(s_url) + local request, sink = {}, {} + local parsed = url.parse(s_url) + request['url'] = s_url + request['method'] = 'GET' + request['sink'] = ltn12.sink.table(sink) + http.TIMEOUT = 5 + https.TIMEOUT = 5 + local httpRequest = parsed.scheme == 'http' and http.request or https.request + local headers = socket.skip(1, httpRequest(request)) + if headers == nil then + return nil + end + local xml = table.concat(sink) + if xml ~= "" then + return xml + end +end + +function GoodReadsApi:showSearchTable(data) + local books = {} + if data == nil then + UIManager:show(InfoMessage:new{text =_("Network problem.\nCheck connection.")}) + return books + end + self.total_result = data:match("(.*)") + + for work in data:gmatch("(.-)") do + local book = work:match("]+>(.*)") + local id = book:match("]+>([^<]+)") + local title = book:match("([^<]+)"):gsub(" %(.*#%d+%)$", "") + local author = book:match("([^<]+)") + table.insert(books, { + author = author, + title = title, + id = id, + }) + end + if #books == 0 then + UIManager:show(InfoMessage:new{text =_("Search not found!")}) + end + return books +end + +function GoodReadsApi:getTotalResults() + return self.total_result +end + +local function cleanHTMLTags(str_html) + local cleaner = { + { "&", "&" }, + { "—", "-" }, + { "’", "'" }, + { " ", " " }, + { "", "%1" }, + { "", "\n" }, + { "%-%-", "%-" }, + { "

", "\n" }, + { "(%b<>)", "" }, + { "\n\n*", "\n" }, + { "\n*$", "" }, + { "^\n*", "" }, + } + for i=1, #cleaner do + local cleans = cleaner[i] + str_html = string.gsub(str_html, cleans[1], cleans[2]) + end + return str_html +end + +local function showIdTable(data) + if data == nil then + UIManager:show(InfoMessage:new{text =_("Network problem.\nCheck connection.")}) + return {} + end + local data1 = data:match("(.*)") + local title_all = data1:match("(.*)"):gsub("$", "") + local title = title_all:gsub(" %(.*#%d+%)$", "") + local average_rating = data1:match("([^<]+)") + local series = title_all:match("%(.*#%d+%)$") + if series ~= nil then + series = series:match("[(](.*)[)]") + else + series = _("N/A") + end + local num_pages = data1:match("(.*)"):gsub("$", "") + if num_pages == nil or num_pages =="" then + num_pages = _("N/A") + end + local id = data1:match("([^<]+)"):gsub("$", "") + local author = data1:match("([^<]+)") + local description = data1:match("(.*)") + description = cleanHTMLTags(description) + --change format from medium to large + local image = data1:match("([^<]+)"):gsub("([0-9]+)m/", "%1l/") + local day = data1:match("]+>([^<]+)") + local month = data1:match("]+>([^<]+)") + local year = data1:match("]+>([^<]+)") + + local release = {} + if (year) then + table.insert(release, year) + end + if (month) then + table.insert(release, string.format("%02d", month)) + end + if (day) then + table.insert(release, string.format("%02d", day)) + end + release = table.concat(release, "-") + if release == "" then + release = _("N/A") + end + local book_info = { + title = title, + author = author, + series = series, + rating = average_rating, + pages = num_pages, + release = release, + description = description, + image = image, + id = id, + } + if id == nil then + UIManager:show(InfoMessage:new{text = _("Search not found!")}) + end + return book_info +end + +-- search_type = all - search all +-- search_type = author - serch book by author +-- search_type = title - search book by title +function GoodReadsApi:showData(search_text, search_type, page, goodreaders_key) + local stats = {} + local gen_url = genSearchURL(search_text, goodreaders_key, search_type, page) + local gen_xml = self:fetchXml(gen_url) + local tbl = self:showSearchTable(gen_xml) + if #tbl == 0 then + return nil + end + for _, v in pairs(tbl) do + local author = v.author + local title = v.title + local id = v.id + table.insert(stats, { author, + title, + callback = function() + local dates = self:showIdData(id, goodreaders_key) + if dates.id ~= nil then + UIManager:show(GoodReaderBook:new{ + dates = dates, + }) + end + end, + }) + end + return stats +end + +function GoodReadsApi:showIdData(id, goodreaders_key) + local gen_url = genIdUrl(id, goodreaders_key) + local gen_xml = self:fetchXml(gen_url) + local tbl = showIdTable(gen_xml) + return tbl +end + +return GoodReadsApi diff --git a/plugins/goodreads.koplugin/main.lua b/plugins/goodreads.koplugin/main.lua new file mode 100644 index 000000000..ff67a16f0 --- /dev/null +++ b/plugins/goodreads.koplugin/main.lua @@ -0,0 +1,224 @@ +local InputContainer = require("ui/widget/container/inputcontainer") +local InputDialog = require("ui/widget/inputdialog") +local DoubleKeyValuePage = require("doublekeyvaluepage") +local MultiInputDialog = require("ui/widget/multiinputdialog") +local InfoMessage = require("ui/widget/infomessage") +local UIManager = require("ui/uimanager") +local Screen = require("device").screen +local _ = require("gettext") + +local GoodReads = InputContainer:new { + goodreaders_key = "", + goodreaders_secret = "", +} + +function GoodReads:init() + local gr_sett = DoubleKeyValuePage:readGRSettings().data + if gr_sett.goodreads then + self.goodreaders_key = gr_sett.goodreads.key + self.goodreaders_secret = gr_sett.goodreads.secret + end + self.ui.menu:registerToMainMenu(self) +end + +function GoodReads:addToMainMenu(tab_item_table) + table.insert(tab_item_table.plugins, { + text = _("GoodReads"), + sub_item_table = { + { + text = _("Settings"), + callback = function() self:updateSettings() end, + }, + { + text = _("Search book all"), + callback = function() + if self.goodreaders_key ~= "" then + self:search("all") + else + UIManager:show(InfoMessage:new{ + text = _("Please set up GoodReads key in settings"), + }) + end + end, + }, + { + text = _("Search book by title"), + callback = function() + if self.goodreaders_key ~= "" then + self:search("title") + else + UIManager:show(InfoMessage:new{ + text = _("Please set up GoodReads key in settings"), + }) + end + end, + }, + { + text = _("Search book by author"), + callback = function() + if self.goodreaders_key ~= "" then + self:search("author") + + else + UIManager:show(InfoMessage:new{ + text = _("Please set up GoodReads key in settings"), + }) + end + end, + }, + }, + }) +end + +function GoodReads:updateSettings() + local hint_top + local text_top + local hint_bottom + local text_bottom + local text_info = "How to generate key and secret key:\n".. + "1. Go to https://www.goodreads.com/user/sign_up and create account\n" .. + "2. Register development key on page: https://www.goodreads.com/user/sign_in?rd=true\n" .. + "3. Your key and secret key are on https://www.goodreads.com/api/keys\n" .. + "4. Enter your generated key and secret key in settings (Login to GoodReads window)" + if self.goodreaders_key == "" then + hint_top = _("GoodReaders Key Not Set") + text_top = "" + else + hint_top = "" + text_top = self.goodreaders_key + end + + if self.goodreaders_secret == "" then + hint_bottom = _("GoodReaders Secret Key Not Set (optional)") + text_bottom = "" + else + hint_bottom = "" + text_bottom = self.goodreaders_key + end + self.settings_dialog = MultiInputDialog:new { + title = _("Login to GoodReads"), + fields = { + { + text = text_top, + input_type = "string", + hint = hint_top , + }, + { + text = text_bottom, + input_type = "string", + hint = hint_bottom, + }, + }, + buttons = { + { + { + text = _("Cancel"), + callback = function() + self.settings_dialog:onClose() + UIManager:close(self.settings_dialog) + end + }, + { + text = _("Info"), + callback = function() + UIManager:show(InfoMessage:new{text = text_info }) + end + }, + { + text = _("Apply"), + callback = function() + self:saveSettings(MultiInputDialog:getFields()) + self.settings_dialog:onClose() + UIManager:close(self.settings_dialog) + end + }, + }, + }, + width = Screen:getWidth() * 0.95, + height = Screen:getHeight() * 0.2, + input_type = "text", + } + self.settings_dialog:onShowKeyboard() + UIManager:show(self.settings_dialog) +end + +function GoodReads:saveSettings(fields) + if fields then + self.goodreaders_key = fields[1] + self.goodreaders_secret = fields[2] + end + local settings = { + key = self.goodreaders_key, + secret = self.goodreaders_secret, + } + DoubleKeyValuePage:saveGRSettings(settings) +end + +-- search_type = all - search all +-- search_type = author - serch book by author +-- search_type = title - search book by title +function GoodReads:search(search_type) + local title_header + local hint + local search_input + local text_input + local info + local result + if search_type == "all" then + title_header = _("Search book all in GoodReads") + hint = _("Title, author or ISBN") + elseif search_type == "author" then + title_header = _("Search book by author in GoodReads") + hint = _("Author") + elseif search_type == "title" then + title_header = _("Search book by title in GoodReads") + hint = _("Title") + end + + search_input = InputDialog:new{ + title = title_header, + input = "", + input_hint = hint, + input_type = "string", + buttons = { + { + { + text = _("Cancel"), + callback = function() + UIManager:close(search_input) + end, + }, + { + text = _("Find"), + is_enter_default = true, + callback = function() + text_input = search_input:getInputText() + if text_input ~= nil and text_input ~= "" then + info = InfoMessage:new{text = _("Please wait..."), timeout = 0.0} + UIManager:show(info) + UIManager:nextTick(function() + result = DoubleKeyValuePage:new{ + title = _("Select book"), + text_input = text_input, + search_type = search_type, + } + if #result.kv_pairs > 0 then + UIManager:show(result) + end + end) + UIManager:close(search_input) + else + UIManager:show(InfoMessage:new{ + text =_("Please input text"), + }) + end + end, + }, + } + }, + } + search_input:onShowKeyboard() + UIManager:show(search_input) +end + +return GoodReads diff --git a/resources/goodreadsnophoto.png b/resources/goodreadsnophoto.png new file mode 100644 index 000000000..4db7258b6 Binary files /dev/null and b/resources/goodreadsnophoto.png differ