Move deprecation code in its own file (the code was getting big and not very interesting)

Orionik [06-04-22 - 18:43]
Move deprecation code in its own file (the code was getting big and not very interesting)

Some books are not in the eidetic memory so we can't access them thanks to ESO API.
So, in order to still be able to read them, I have added the possibility for some books to still hold all the necessary data to be able to display them. The text won't have the right language if the player changed his language since the day he saw the book but it is better than not beeing able to read them.
Filename
Deprecation.lua
Librarian.lua
Librarian.txt
diff --git a/Deprecation.lua b/Deprecation.lua
new file mode 100644
index 0000000..00b6418
--- /dev/null
+++ b/Deprecation.lua
@@ -0,0 +1,270 @@
+
+local function GetNextBookIndex(bookIndexes)
+    if bookIndexes.bookIndex >= bookIndexes.totalBooks then
+        if bookIndexes.collectionIndex >= bookIndexes.numCollections then
+            if bookIndexes.categoryIndex >= GetNumLoreCategories() then
+                return false
+            else
+                bookIndexes.categoryIndex = bookIndexes.categoryIndex + 1
+                bookIndexes.numCollections = select(2, GetLoreCategoryInfo(bookIndexes.categoryIndex))
+                bookIndexes.collectionIndex = 0
+            end
+        end
+        bookIndexes.collectionIndex = bookIndexes.collectionIndex + 1
+        bookIndexes.totalBooks = select(4, GetLoreCollectionInfo(bookIndexes.categoryIndex, bookIndexes.collectionIndex))
+        bookIndexes.bookIndex = 0
+    end
+    bookIndexes.bookIndex = bookIndexes.bookIndex + 1
+    return true
+end
+
+function Librarian:GetBookIdWithTitle(title)
+    -- list of book that aren't part of the eidetic memory
+    if title == "Adventurers Wanted!" then
+        return 4552
+    elseif title == "Behold Khunzar-ri's Ambition" then
+        return 5925
+    elseif title == "Behold Khunzar-ri's Betrayal" then
+        return 5673
+    elseif title == "Behold Khunzar-ri's Guile" then
+        return 5724
+    elseif title == "Behold the Lunar Champion" then
+        return 5669
+    elseif title == "Beldorr's Note" then
+        return 2933
+    elseif title == "Bounty: Dragons!" then
+        return 5708
+    elseif title == "Cadwell's Personal Anthem" then
+        return 2517
+    elseif title == "Crumpled Note" then
+        return 2925
+    elseif title == "Crumpled Nursery Rhyme" then
+        return 5460
+    elseif title == "Details on the Midyear Mayhem" then
+        return 4591
+    elseif title == "Diviner's Journal" then
+        return 4048
+    elseif title == "Engraved Pedestal" then
+        return 4006
+    elseif title == "Excavation Orders" then
+        return 4047
+    elseif title == "Falsehoods and Fallacies of the Eight" then
+        return 1279
+    elseif title == "Firuth's Writ" then
+        return 4042
+    elseif title == "From the Exalted Viper" then -- real name is "From the Regent of Fanged Fury"
+        return 2644
+    elseif title == "The Ghostly Stag" then
+        return 138
+    elseif title == "Guardian's Decree" then
+        return 4050
+    elseif title == "The Indrik's Glade" then
+        return 5027
+    elseif title == "Journal of the King's Seneschal" then
+        return 2015
+    elseif title == "Journal of Ulrich" then
+        return 132
+    elseif title == "Journal's Final Pages" then
+        return 2942
+    elseif title == "Jubilee Cake Voucher" then
+        return 6215
+    elseif title == "Keeper's Letter" then
+        return 4851
+    elseif title == "Khasaad's Treasure Map" then
+        return 727
+    elseif title == "Laughing Moons Ledger" then
+        return 1566
+    elseif title == "Legend of Shalug the Shark" then
+        return 3285
+    elseif title == "Letter From Ember" then
+        return 7292
+    elseif title == "Letter From Isobel" then
+        return 7293
+    elseif title == "Mezhun's Field Journal" then
+        return 2796
+    elseif title == "Nerulean's Guide to Phantoms Vol. II" then
+        return 3049
+    elseif title == "Ode to a Torchbug" then
+        return 2954
+    elseif title == "On Soul Shriven, vol 3" then
+        return 1551
+    elseif title == "An Orc Weaponsmith In Murkmire, Part 1" then -- real name is "From Wrothgar to Lilmoth: A Smith's Tale, Vol 1"
+        return 2850
+    elseif title == "An Orc Weaponsmith In Murkmire, Part 2" then -- real name is "From Wrothgar to Lilmoth: A Smith's Tale, Vol 2"
+        return 2851
+    elseif title == "An Orc Weaponsmith In Murkmire, Part 3" then -- real name is "From Wrothgar to Lilmoth: A Smith's Tale, Vol 3"
+        return 2852
+    elseif title == "Orcthane's Orders" then
+        return 439
+    elseif title == "Plea to Maximinus" then
+        return 2659
+    elseif title == "PRISONER: CLARISSE LAURENT" then
+        return 2908
+    elseif title == "PRISONER: RAYNOR VANOS" then
+        return 2907
+    elseif title == "PRISONER: TELENGER" then
+        return 2909
+    elseif title == "Report on Dominion Activities" then
+        return 147
+    elseif title == "Royal Messenger's Fate" then
+        return 2701
+    elseif title == "Seeking New Members!" then
+        return 7168
+    elseif title == "Six Are the Walking Ways" then
+        return 4445
+    elseif title == "The Song of the Word" then
+        return 4446
+    elseif title == "Southern Elsweyr Needs You!" then
+        return 5697
+    elseif title == "Statue of Amminus Entius" then
+        return 3419
+    elseif title == "Statue of Cavor Merula" then
+        return 3416
+    elseif title == "Statue of Justia Desticus" then
+        return 3417
+    elseif title == "Statue of Rusio Olo" then
+        return 3418
+    elseif title == "Terran's Notes" then
+        return 3047
+    elseif title == "The Tomb of Ja'darri" then
+        return 5737
+    elseif title == "Torag ag Krazak, Uz" then
+        return 3089
+    elseif title == "Tribute Challengers - Novice Tournament" then
+        return 7169
+    elseif title == "Ushutha's Journal" then
+        return 3162
+    elseif title == "The Year 2920, Vol. 28" then
+        return 2945
+    end
+
+    if not self.globalSavedVars.failedDeprecation then
+        self.globalSavedVars.failedDeprecation = { librarianBookId = 100000, bookList = {} }
+    end
+
+    for _, book in ipairs(self.globalSavedVars.failedDeprecation.bookList) do
+        if book.title == title then
+            return book.bookId
+        end
+    end
+
+    local nextBookId = self.globalSavedVars.failedDeprecation.librarianBookId
+    local newFailedDeprecation = { title = title, bookId = nextBookId }
+    table.insert(self.globalSavedVars.failedDeprecation.bookList, newFailedDeprecation)
+    self.globalSavedVars.failedDeprecation.librarianBookId = nextBookId + 1
+
+    return nextBookId
+end
+
+function Librarian:UpdateSavedVariables()
+    -- before version 3.0 there was no saveVersion
+    -- In this version, the bookId is now used as identifier instead of the title (because several book have the same title)
+    -- Also unreadPerCollections was added
+    if not self.globalSavedVars.saveVersion then
+        self.globalSavedVars.saveVersion = 1
+
+        -- if this player doesn't know any book yet, no need to go further
+        if next(self.books) ~= nil then
+            local GetLoreBookInfo, ReadLoreBook = GetLoreBookInfo, ReadLoreBook
+            local bookIndexes = {
+                categoryIndex = 0,
+                collectionIndex = 0,
+                bookIndex = 0,
+                numCollections = 0,
+                totalBooks = 0,
+            }
+            while true do
+                if not GetNextBookIndex(bookIndexes) then
+                    break
+                end
+
+                local title, _, _, bookId = GetLoreBookInfo(bookIndexes.categoryIndex, bookIndexes.collectionIndex, bookIndexes.bookIndex)
+                local book = nil
+                for _,bookIt in pairs(self.books) do
+                    if bookIt.title == title then
+                        book = bookIt
+                        break
+                    end
+                end
+                if book then
+                    if not book.bookId then
+                        book.bookId = bookId
+                    else -- there is a collision (2 books with same name) so we add an entry for the new one
+                        local newBook = { bookId = bookId, title = title }
+                        self:AddBookToGlobalSave(newBook)
+                        newBook.timeStamp = book.timeStamp
+                    end
+                end
+            end
+
+            -- now we can erase all the field that are useless (ESO API is performant enough to get these data only when needed)
+            -- it will also help keeping the save small and avoid issues with corrupted save
+            for _,book in pairs(self.books) do
+                if book.bookId then
+                    book.title = nil
+                    book.body = nil
+                    book.medium = nil
+                    book.showTitle = nil
+                    book.wordCount = nil
+                else
+                    book.bookId = self:GetBookIdWithTitle(book.title)
+                end
+            end
+        end
+    end
+
+    if not self.localSavedVars.saveVersion then
+        self.localSavedVars.saveVersion = 1
+        self.localSavedVars.unreadPerCollections = {} -- erase whatever is already inside (just in case)
+        self.unreadPerCollections = self.localSavedVars.unreadPerCollections
+
+        local GetLoreBookInfo, ReadLoreBook = GetLoreBookInfo, ReadLoreBook
+        local bookIndexes = {
+            categoryIndex = 0,
+            collectionIndex = 0,
+            bookIndex = 0,
+            numCollections = 0,
+            totalBooks = 0,
+        }
+        while true do
+            if not GetNextBookIndex(bookIndexes) then
+                break
+            end
+
+            local title, _, known, bookId = GetLoreBookInfo(bookIndexes.categoryIndex, bookIndexes.collectionIndex, bookIndexes.bookIndex)
+
+            if known then
+                local characterBook = nil
+                for _,characterBookIt in ipairs(self.characterBooks) do
+                    if characterBookIt.title == title then
+                        characterBook = characterBookIt
+                        break
+                    end
+                end
+
+                if characterBook then
+                    if not characterBook.bookId then
+                        characterBook.bookId = bookId
+                    else
+                        local newCharacterBook = { title = title, timeStamp = characterBook.timeStamp, bookId = bookId }
+                        table.insert(self.characterBooks, newCharacterBook)
+                    end
+
+                    local book = self:FindBook(bookId)
+                    if book and book.unread then
+                        self:AddUnreadBookInCollection(bookIndexes.categoryIndex, bookIndexes.collectionIndex)
+                    end
+                end
+            end
+        end
+
+        -- now we can remove the title from the characterBooks and rely only on the bookId (it saves space)
+        for _,characterBook in ipairs(self.characterBooks) do
+            if characterBook.bookId then
+                characterBook.title = nil
+            else
+                characterBook.bookId = self:GetBookIdWithTitle(characterBook.title)
+            end
+        end
+    end
+end
diff --git a/Librarian.lua b/Librarian.lua
index 36f6e82..a0230e1 100644
--- a/Librarian.lua
+++ b/Librarian.lua
@@ -1,4 +1,5 @@
 Librarian = ZO_SortFilterList:Subclass()
+Librarian.name = "Librarian"
 Librarian.defaults = {}
 Librarian.menuSettings = {
     name = "LibrarianOptions",
@@ -99,6 +100,9 @@ function Librarian:AddLoreReaderUnreadToggle()
     end

     local function IsSameBook(book, title)
+        if book.title then
+            return book.title == title
+        end
         local categoryIndex, collectionIndex, bookIndex = GetLoreBookIndicesFromBookId(book.bookId)
         local bookTitle = GetLoreBookInfo(categoryIndex, collectionIndex, bookIndex)
         return bookTitle == title
@@ -122,6 +126,10 @@ function Librarian:AddLoreReaderUnreadToggle()
                     return GetString(LIBRARIAN_MARK_UNREAD)
                 end
             end,
+            visible = function()
+                local book = self:FindBook(self.lastShownBookId)
+                return book and IsSameBook(book, LORE_READER.titleText)
+            end,
             keybind = "UI_SHORTCUT_SECONDARY",
             callback = function()
                 local book = self:FindBook(self.lastShownBookId)
@@ -133,6 +141,8 @@ function Librarian:AddLoreReaderUnreadToggle()
             end
         }
         table.insert(readerKeybinds, toggleKeybind)
+
+        self.loreReaderKeybinds = readerKeybinds
     end

     self.loreReaderUnreadIndicator = WINDOW_MANAGER:CreateControl("LibrarianLoreReaderUnreadIndicator", ZO_LoreReaderBookContainer, CT_TEXTURE)
@@ -238,8 +248,8 @@ function Librarian:AddLoreLibraryIcons()
             if selectedRow then
                 local book = self:FindBook(selectedRow.bookId)
                 if not book then
-                    self:AddBook(selectedRow.bookId, selectedRow.text:GetText(), true)
-                    book = self:FindBook(selectedRow.bookId)
+                    book = { bookId = selectedRow.bookId, title = selectedRow.text:GetText() }
+                    self:AddBook(book, true)
                 end

                 if book then
@@ -283,130 +293,6 @@ function Librarian:AddLoreLibraryIcons()
     LORE_LIBRARY.navigationTree.templateInfo["ZO_LabelHeader"].equalityFunction = navigationEntryTemplateInfo.equalityFunction
 end

-local function GetNextBookIndex(bookIndexes)
-    if bookIndexes.bookIndex >= bookIndexes.totalBooks then
-        if bookIndexes.collectionIndex >= bookIndexes.numCollections then
-            if bookIndexes.categoryIndex >= GetNumLoreCategories() then
-                return false
-            else
-                bookIndexes.categoryIndex = bookIndexes.categoryIndex + 1
-                bookIndexes.numCollections = select(2, GetLoreCategoryInfo(bookIndexes.categoryIndex))
-                bookIndexes.collectionIndex = 0
-            end
-        end
-        bookIndexes.collectionIndex = bookIndexes.collectionIndex + 1
-        bookIndexes.totalBooks = select(4, GetLoreCollectionInfo(bookIndexes.categoryIndex, bookIndexes.collectionIndex))
-        bookIndexes.bookIndex = 0
-    end
-    bookIndexes.bookIndex = bookIndexes.bookIndex + 1
-    return true
-end
-
-function Librarian:UpdateSavedVariables()
-    -- before version 3.0 there was no saveVersion
-    -- In this version, the bookId is now used as identifier instead of the title (because several book have the same title)
-    -- Also unreadPerCollections was added
-    if not self.globalSavedVars.saveVersion then
-        self.globalSavedVars.saveVersion = 1
-
-        -- if this player doesn't know any book yet, no need to go further
-        if next(self.books) ~= nil then
-            local GetLoreBookInfo, ReadLoreBook = GetLoreBookInfo, ReadLoreBook
-            local bookIndexes = {
-                categoryIndex = 0,
-                collectionIndex = 0,
-                bookIndex = 0,
-                numCollections = 0,
-                totalBooks = 0,
-            }
-            while true do
-                if not GetNextBookIndex(bookIndexes) then
-                    break
-                end
-
-                local title, _, _, bookId = GetLoreBookInfo(bookIndexes.categoryIndex, bookIndexes.collectionIndex, bookIndexes.bookIndex)
-                local book = nil
-                for _,bookIt in pairs(self.books) do
-                    if bookIt.title == title then
-                        book = bookIt
-                        break
-                    end
-                end
-                if book then
-                    if not book.bookId then
-                        book.bookId = bookId
-                    else -- there is a collision (2 books with same name) so we add an entry for the new one
-                        self:AddBookToGlobalSave(bookId)
-                    end
-                end
-            end
-
-            -- now we can erase all the field that are useless (ESO API is performant enough to get these data only when needed)
-            -- it will also help keeping the save small and avoid issues with corrupted save
-            for _,book in pairs(self.books) do
-                book.title = nil
-                book.body = nil
-                book.medium = nil
-                book.showTitle = nil
-                book.wordCount = nil
-            end
-        end
-    end
-
-    if not self.localSavedVars.saveVersion then
-        self.localSavedVars.saveVersion = 1
-        self.localSavedVars.unreadPerCollections = {} -- erase whatever is already inside (just in case)
-        self.unreadPerCollections = self.localSavedVars.unreadPerCollections
-
-        local GetLoreBookInfo, ReadLoreBook = GetLoreBookInfo, ReadLoreBook
-        local bookIndexes = {
-            categoryIndex = 0,
-            collectionIndex = 0,
-            bookIndex = 0,
-            numCollections = 0,
-            totalBooks = 0,
-        }
-        while true do
-            if not GetNextBookIndex(bookIndexes) then
-                break
-            end
-
-            local title, _, known, bookId = GetLoreBookInfo(bookIndexes.categoryIndex, bookIndexes.collectionIndex, bookIndexes.bookIndex)
-
-            if known then
-                local characterBook = nil
-                for _,characterBookIt in ipairs(self.characterBooks) do
-                    if characterBookIt.title == title then
-                        characterBook = characterBookIt
-                        break
-                    end
-                end
-
-                if characterBook then
-                    if not characterBook.bookId then
-                        characterBook.bookId = bookId
-                    else
-                        local newCharacterBook = { title = title, timeStamp = characterBook.timeStamp, bookId = bookId }
-                        table.insert(self.characterBooks, newCharacterBook)
-                    end
-
-                    local book = self:FindBook(bookId)
-                    if book and book.unread then
-                        self:AddUnreadBookInCollection(bookIndexes.categoryIndex, bookIndexes.collectionIndex)
-                    end
-                end
-            end
-        end
-
-        -- now we can remove the title from the characterBooks and rely only on the bookId (it saves space)
-        for _,characterBook in ipairs(self.characterBooks) do
-            characterBook.title = nil
-        end
-    end
-
-
-end
-
 function Librarian:InitializeKeybindStripDescriptors()
     self.keybindStripDescriptor =
     {
@@ -512,7 +398,8 @@ function Librarian:ImportFromLoreLibrary()
         local title, _, known, bookId = GetLoreBookInfo(categoryIndex, collectionIndex, bookIndex)
         if known then
             if not self:FindCharacterBook(bookId) then
-                self:AddBook(bookId, title, false)
+                book = { bookId = bookId, title = title }
+                self:AddBook(book, false)
                 hasImportedBooks = true
             end
         end
@@ -537,8 +424,12 @@ function Librarian:BuildMasterList()
     local function ShouldDisplayBook(book)
         if not self.settings.showHiddenBook then
             local category, collection = GetLoreBookIndicesFromBookId(book.bookId)
-            local hiddenCollection = select(5, GetLoreCollectionInfo(category, collection))
-            if hiddenCollection then
+            if category and collection then
+                local hiddenCollection = select(5, GetLoreCollectionInfo(category, collection))
+                if hiddenCollection then
+                    return false
+                end
+            else
                 return false
             end
         end
@@ -553,12 +444,18 @@ function Librarian:BuildMasterList()

             local data = self.masterList[i]
             for k,v in pairs(book) do
-                data[k] = v
+				if k == "body" then
+					data[k] = table.concat(book.body)
+				else
+					data[k] = v
+				end
             end

             -- because we reuse the same list between 2 refresh, the data may already be filled up so no need to recompute it, these won't change
-            if not data.categoryIndex or not data.collectionIndex or not data.bookIndex or not data.title or not data.body or not data.wordCount then
+            if not data.categoryIndex or not data.collectionIndex or not data.bookIndex then
                 data.categoryIndex, data.collectionIndex, data.bookIndex = GetLoreBookIndicesFromBookId(book.bookId)
+            end
+            if data.categoryIndex and data.collectionIndex and data.bookIndex and (not data.title or not data.body or not data.wordCount) then
                 data.title = GetLoreBookInfo(data.categoryIndex, data.collectionIndex, data.bookIndex)
                 data.body = ReadLoreBook(data.categoryIndex, data.collectionIndex, data.bookIndex)

@@ -676,25 +573,38 @@ function Librarian:FindBook(bookId)
     end
 end

-function Librarian:AddBookToGlobalSave(bookId)
-    local book = { bookId = bookId, timeStamp = GetTimeStamp(), unread = true }
+function Librarian:AddBookToGlobalSave(book)
+    book.timeStamp = GetTimeStamp()
+    book.unread = true
+
+    local categoryIndex, collectionIndex, bookIndex = GetLoreBookIndicesFromBookId(book.bookId)
+    if categoryIndex and collectionIndex and bookIndex then
+        -- if these indexes exist in the API, this mean that we will be able to get these info when needed (so no need to save them)
+        book.title = nil
+        book.body = nil
+        book.medium = nil
+        book.showTitle = nil
+        book.wordCount = nil
+    end
+
     table.insert(self.books, book)
-    return book
 end

-function Librarian:AddBook(bookId, bookTitle, refreshDataRightAway)
-    if not self:FindCharacterBook(bookId) then
-        local book = self:FindBook(bookId)
-        if not book then
-            book = self:AddBookToGlobalSave(bookId)
+function Librarian:AddBook(book, refreshDataRightAway)
+    if not self:FindCharacterBook(book.bookId) then
+        local bookTitle = book.title -- storing it because AddBookToGlobalSave will delete it if we can retrieve it thanks to ESO API
+        if not self:FindBook(book.bookId) then
+            self:AddBookToGlobalSave(book)
         end

-        local characterBook = { bookId = bookId, timeStamp = GetTimeStamp() }
+        local characterBook = { bookId = book.bookId, timeStamp = GetTimeStamp() }
         table.insert(self.characterBooks, characterBook)

         if book.unread then
-            local categoryIndex, collectionIndex = GetLoreBookIndicesFromBookId(bookId)
-            self:AddUnreadBookInCollection(categoryIndex, collectionIndex)
+            local categoryIndex, collectionIndex = GetLoreBookIndicesFromBookId(book.bookId)
+            if categoryIndex and collectionIndex then
+                self:AddUnreadBookInCollection(categoryIndex, collectionIndex)
+            end
         end

         if refreshDataRightAway then
@@ -752,8 +662,12 @@ end
 function Librarian:ReadBook(data)
     self.lastShownBookId = data.bookId

-    local body, medium, showTitle = ReadLoreBook(data.categoryIndex, data.collectionIndex, data.bookIndex)
-    LORE_READER:SetupBook(data.title, body, medium, showTitle)
+    if data.categoryIndex and data.collectionIndex and data.bookIndex then
+        local body, medium, showTitle = ReadLoreBook(data.categoryIndex, data.collectionIndex, data.bookIndex)
+        LORE_READER:SetupBook(data.title, body, medium, showTitle)
+    else
+        LORE_READER:SetupBook(data.title, data.body, data.medium, data.showTitle)
+    end
     SCENE_MANAGER:Push("loreReaderInteraction")

     -- PlaySound(LORE_READER.OpenSound)
@@ -804,9 +718,12 @@ function Librarian.SlashCommand(args)
 end

 function Librarian.OnShowBook(eventCode, title, body, medium, showTitle, bookId)
-    LIBRARIAN:AddBook(bookId, title, true)
-
+    local book = { title = title, body = body, medium = medium, showTitle = showTitle, bookId = bookId }
+    LIBRARIAN:AddBook(book, true)
     LIBRARIAN.lastShownBookId = bookId
+    if LIBRARIAN.loreReaderKeybinds then
+        KEYBIND_STRIP:UpdateKeybindButtonGroup(LIBRARIAN.loreReaderKeybinds)
+    end
 end

 function Librarian.OnMouseEnterRow(control)
@@ -822,13 +739,13 @@ function Librarian.OnMouseUpRow(control, button, upInside)
 end

 function Librarian.OnAddonLoaded(event, addon)
-    if addon == "Librarian" then
-        EVENT_MANAGER:UnregisterForEvent("Librarian", EVENT_ADD_ON_LOADED)
+    if addon == Librarian.name then
+        EVENT_MANAGER:UnregisterForEvent(Librarian.name, EVENT_ADD_ON_LOADED)
         LIBRARIAN = Librarian:New()
-        EVENT_MANAGER:RegisterForEvent("Librarian", EVENT_SHOW_BOOK, Librarian.OnShowBook)
+        EVENT_MANAGER:RegisterForEvent(Librarian.name, EVENT_SHOW_BOOK, Librarian.OnShowBook)
     end
 end

 SLASH_COMMANDS[Librarian.slashCommandText] = Librarian.SlashCommand

-EVENT_MANAGER:RegisterForEvent("Librarian", EVENT_ADD_ON_LOADED, Librarian.OnAddonLoaded)
+EVENT_MANAGER:RegisterForEvent(Librarian.name, EVENT_ADD_ON_LOADED, Librarian.OnAddonLoaded)
diff --git a/Librarian.txt b/Librarian.txt
index 7047da9..cef9927 100644
--- a/Librarian.txt
+++ b/Librarian.txt
@@ -11,6 +11,7 @@

 Librarian.lua
 LibrarianSettings.lua
+Deprecation.lua
 Librarian.xml
 Bindings.xml