diff --git a/lua/leetcode-ui/group/page/problems.lua b/lua/leetcode-ui/group/page/problems.lua index 8e0cfcd..72d12c4 100644 --- a/lua/leetcode-ui/group/page/problems.lua +++ b/lua/leetcode-ui/group/page/problems.lua @@ -21,6 +21,12 @@ local list = Button("List", { on_press = cmd.problems, }) +local favorite = Button("Favorite", { + icon = "★", + sc = "f", + on_press = cmd.favorite_list, +}) + local random = Button("Random", { icon = "", sc = "r", @@ -37,6 +43,7 @@ local back = BackButton("menu") page:insert(Buttons({ list, + favorite, random, daily, back, diff --git a/lua/leetcode-ui/types.lua b/lua/leetcode-ui/types.lua index c2536ec..3bf0d63 100644 --- a/lua/leetcode-ui/types.lua +++ b/lua/leetcode-ui/types.lua @@ -32,6 +32,7 @@ ---| "cache" ---| "signin" ---| "loading" +---| "favorite" -------------------------------------------- --- Leet Component diff --git a/lua/leetcode/api/problems.lua b/lua/leetcode/api/problems.lua index 9049bb8..b34f57c 100644 --- a/lua/leetcode/api/problems.lua +++ b/lua/leetcode/api/problems.lua @@ -110,6 +110,35 @@ function Problems.question_of_today(cb) }) end +function Problems.favorite_list(cb) + local query = queries.favorite_list + + utils.query(query, {}, { + callback = function(res, err) + if err then + return cb(nil, err) + end + + local data = res.data + cb(data.myCollectedFavoriteList.favorites) + end, + }) +end + +function Problems.favorite_question_list(favorite_slug, cb) + local query = queries.favorite_question_list + + utils.query(query, { favoriteSlug = favorite_slug }, { + callback = function(res, err) + if err then + return cb(nil, err) + end + local normalized = utils.normalize_favorites(res.data.favoriteQuestionList.questions) + cb(normalized) + end, + }) +end + function Problems.translated_titles(cb) local query = queries.translations diff --git a/lua/leetcode/api/queries.lua b/lua/leetcode/api/queries.lua index b835620..7ea0701 100644 --- a/lua/leetcode/api/queries.lua +++ b/lua/leetcode/api/queries.lua @@ -179,4 +179,56 @@ queries.session_progress = [[ } ]] +queries.favorite_list = [[ + query myFavoriteList { + myCreatedFavoriteList { + favorites { + coverUrl + coverEmoji + coverBackgroundColor + hasCurrentQuestion + isPublicFavorite + lastQuestionAddedAt + name + slug + } + hasMore + totalLength + } + myCollectedFavoriteList { + hasMore + totalLength + favorites { + coverUrl + coverEmoji + coverBackgroundColor + hasCurrentQuestion + isPublicFavorite + name + slug + lastQuestionAddedAt + } + } + } + ]] + +queries.favorite_question_list = [[ + query favoriteQuestionList($favoriteSlug: String!) { + favoriteQuestionList(favoriteSlug: $favoriteSlug) { + questions { + id + questionFrontendId + paidOnly + title + titleSlug + difficulty + topicTags { + name + slug + } + } + } + } + ]] + return queries diff --git a/lua/leetcode/api/types.lua b/lua/leetcode/api/types.lua index 7cd2f84..98f0c44 100644 --- a/lua/leetcode/api/types.lua +++ b/lua/leetcode/api/types.lua @@ -393,6 +393,15 @@ ---@field lvl integer ---@alias lc.err lc.Api.err|nil +-- +-------------------------------------------- +--- Favorite +-------------------------------------------- +---@class lc.Favorite +---@field name string +---@field slug string +---@field isPublicFavorite boolean +---@field hasCurrentQuestion boolean -------------------------------------------- --- Sessions diff --git a/lua/leetcode/api/utils.lua b/lua/leetcode/api/utils.lua index 0121074..25d71dd 100644 --- a/lua/leetcode/api/utils.lua +++ b/lua/leetcode/api/utils.lua @@ -185,6 +185,29 @@ function utils.lvl_to_name(lvl) return ({ "Easy", "Medium", "Hard" })[lvl] end +function utils.normalize_favorites(questions) + local diff = { + EASY = 1, + MEDIUM = 2, + HARD = 3, + } + return vim.tbl_map(function(q) + return { + id = q.id, + frontend_id = tonumber(q.questionFrontendId), + title = q.title, + title_slug = q.titleSlug, + title_cn = "", + paid_only = q.paidOnly, + link = ("https://leetcode.%s/problems/%s/"):format(config.domain, q.titleSlug), + ac_rate = 0, + difficulty = utils.lvl_to_name(diff[q.difficulty]), + starred = true, + topic_tags = {}, + } + end, questions) +end + ---@return lc.cache.Question[] function utils.normalize_problems(problems) problems = vim.tbl_filter(function(p) diff --git a/lua/leetcode/command/init.lua b/lua/leetcode/command/init.lua index fd56ae7..ee3ffd1 100644 --- a/lua/leetcode/command/init.lua +++ b/lua/leetcode/command/init.lua @@ -140,6 +140,30 @@ function cmd.qot() end) end +function cmd.favorite_list() + require("leetcode.utils").auth_guard() + + local problems = require("leetcode.api.problems") + + problems.favorite_list(function(favorites, fav_err) + if fav_err then + return log.err(fav_err) + end + require("leetcode.pickers.favorite").pick(favorites, function(selection) + if not selection then + return + end + local slug = selection.slug + problems.favorite_question_list(slug, function(data, fav_q_err) + if fav_q_err then + return log.err(fav_q_err) + end + require("leetcode.pickers.question").pick(data) + end) + end) + end) +end + function cmd.random_question(opts) require("leetcode.utils").auth_guard() diff --git a/lua/leetcode/pickers/favorite.lua b/lua/leetcode/pickers/favorite.lua new file mode 100644 index 0000000..8dd0ba8 --- /dev/null +++ b/lua/leetcode/pickers/favorite.lua @@ -0,0 +1,79 @@ +local t = require("leetcode.translator") + +local pickers = require("telescope.pickers") +local finders = require("telescope.finders") +local conf = require("telescope.config").values + +local entry_display = require("telescope.pickers.entry_display") +local actions = require("telescope.actions") +local action_state = require("telescope.actions.state") + +---@param f lc.Favorite +--- +---@return string +local function favorite_formatter(f) + return string.format("%s - %s", f.slug, f.name) +end + +---@param favorite lc.Favorite +local function display_favorite(favorite) + return { + favorite.name, + favorite.slug, + } +end + +local displayer = entry_display.create({ + separator = " ", + items = { + { remaining = true }, + }, +}) + +-- @param entry lc.Favorite +local function make_display(entry) + ---@type lc.Favorite + local f = entry.value + + return displayer({ + display_favorite(f), + }) +end + +-- @param entry lc.Favorite +local function entry_maker(entry) + return { + value = entry, + display = make_display, + ordinal = favorite_formatter(entry), + } +end + +local opts = require("telescope.themes").get_dropdown() + +return { + pick = function(favorites, cb) + pickers + .new(opts, { + prompt_title = t("Select a Favorite List"), + finder = finders.new_table({ + results = favorites, + entry_maker = entry_maker, + }), + sorter = conf.generic_sorter(theme), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + + if not selection then + return + end + cb(selection.value) + end) + return true + end, + }) + :find() + end, +}