irongit

A Neovim plugin enabling you to solve LeetCode problems.

feat: sign in menu

kawreauthored
parent 7db1dcdcommit 7b7ffedce506e5c202e50c1a0309ae505b966171Browse files

18 files changed, +274 -165

+31-0lua/leetcode-menu/components/footer.lua
@@ -0,0 +1,31 @@
1+local Text = require("leetcode-ui.component.text")
2+local config = require("leetcode.config")
3+
4+---@class lc-menu.Footer
5+---@field text lc-ui.Text
6+local footer = {}
7+footer.__index = footer
8+
9+function footer:content() return self.text end
10+
11+---@param opts? any
12+function footer:init(opts)
13+ opts = vim.tbl_deep_extend("force", {
14+ position = "center",
15+ hl = "Keyword",
16+ }, opts or {})
17+
18+ local text = Text:init({
19+ opts = opts,
20+ })
21+
22+ if config.auth.name then text:append("Signed in as: " .. config.auth.name, opts.hl) end
23+
24+ local obj = setmetatable({
25+ text = text,
26+ }, self)
27+
28+ return obj
29+end
30+
31+return footer
+43-0lua/leetcode-menu/components/header.lua
@@ -0,0 +1,43 @@
1+local Text = require("leetcode-ui.component.text")
2+
3+---@class lc-menu.Header
4+---@field text lc-ui.Text
5+local header = {}
6+header.__index = header
7+
8+function header:content() return self.text end
9+
10+---@param lines? any
11+---@param opts? any
12+function header:init(lines, opts)
13+ opts = vim.tbl_deep_extend("force", {
14+ position = "center",
15+ hl = "Keyword",
16+ padding = {
17+ bot = 2,
18+ },
19+ }, opts or {})
20+
21+ local text = Text:init({
22+ lines = lines
23+ or {
24+ [[ /$$ /$$ /$$$$$$ /$$ ]],
25+ [[| $$ | $$ /$$__ $$ | $$ ]],
26+ [[| $$ /$$$$$$ /$$$$$$ /$$$$$$ | $$ \__/ /$$$$$$ /$$$$$$$ /$$$$$$ ]],
27+ [[| $$ /$$__ $$/$$__ $|_ $$_/ | $$ /$$__ $$/$$__ $$/$$__ $$]],
28+ [[| $$ | $$$$$$$| $$$$$$$$ | $$ | $$ | $$ \ $| $$ | $| $$$$$$$$]],
29+ [[| $$ | $$_____| $$_____/ | $$ /$| $$ $| $$ | $| $$ | $| $$_____/]],
30+ [[| $$$$$$$| $$$$$$| $$$$$$$ | $$$$| $$$$$$| $$$$$$| $$$$$$| $$$$$$$]],
31+ [[|________/\_______/\_______/ \___/ \______/ \______/ \_______/\_______/]],
32+ },
33+ opts = opts,
34+ })
35+
36+ local obj = setmetatable({
37+ text = text,
38+ }, self)
39+
40+ return obj
41+end
42+
43+return header
+26-0lua/leetcode-menu/components/notifications.lua
@@ -0,0 +1,26 @@
1+local Text = require("leetcode-ui.component.text")
2+
3+---@class lc-menu.Notifications
4+---@field text lc-ui.Text
5+local notifications = {}
6+notifications.__index = notifications
7+
8+---@param opts? any
9+function notifications:init(opts)
10+ opts = vim.tbl_deep_extend("force", {
11+ position = "center",
12+ hl = "DiagnosticInfo",
13+ }, opts or {})
14+
15+ local text = Text:init({
16+ opts = opts,
17+ })
18+
19+ local obj = setmetatable({
20+ text = text,
21+ }, self)
22+
23+ return obj
24+end
25+
26+return notifications
+30-0lua/leetcode-menu/components/title.lua
@@ -0,0 +1,30 @@
1+local Text = require("leetcode-ui.component.text")
2+
3+---@class lc-menu.Title
4+---@field text lc-ui.Text
5+local title = {}
6+title.__index = title
7+
8+function title:content() return self.text end
9+
10+---@param str string
11+---@param opts? any
12+function title:init(str, opts)
13+ opts = vim.tbl_deep_extend("force", {
14+ position = "center",
15+ hl = "Comment",
16+ }, opts or {})
17+
18+ local text = Text:init({
19+ lines = { str },
20+ opts = opts,
21+ })
22+
23+ local obj = setmetatable({
24+ text = text,
25+ }, self)
26+
27+ return obj
28+end
29+
30+return title
+10-5lua/leetcode-menu/init.lua
@@ -1,11 +1,12 @@
11 local component = require("leetcode-ui.component")
22 local log = require("leetcode.logger")
33 local utils = require("leetcode-menu.utils")
4+local config = require("leetcode.config")
45
56 local Text = require("nui.text")
67 local Line = require("nui.line")
78
8----@class lc-db.Dashboard
9+---@class lc-menu
910 ---@field layout lc-ui.Layout
1011 ---@field bufnr integer
1112 ---@field winid integer
@@ -13,8 +14,8 @@ local Line = require("nui.line")
1314 local menu = {} ---@diagnostic disable-line
1415 menu.__index = menu
1516
16----@type table<bufnr, lc-db.Dashboard>
17-db = {}
17+---@type lc-menu
18+_LC_MENU = {} ---@diagnostic disable-line
1819
1920 function menu:clear() vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, false, {}) end
2021
@@ -94,14 +95,18 @@ function menu:init()
9495 signcolumn = "no",
9596 })
9697
98+ local layout = config.auth.is_signed_in and "menu" or "signin"
99+ local ok, l = pcall(require, "leetcode-menu.theme." .. layout)
100+ assert(ok)
101+
97102 local obj = setmetatable({
98103 bufnr = bufnr,
99104 winid = winid,
100105 tabpage = tabpage,
101- layout = require("leetcode-menu.theme.menu"),
106+ layout = l,
102107 }, self)
103- db[bufnr] = obj
104108
109+ _LC_MENU = obj
105110 return obj:mount()
106111 end
107112
+1-1lua/leetcode-menu/theme/cache.lua
@@ -20,7 +20,7 @@ section.buttons.val = {
2020 template.button("q", "󰩈 " .. " Exit LeetCode", "<cmd>qa<CR>"),
2121 }
2222
23-section.footer.val = "Signed in as: " .. auth.username
23+section.footer.val = "Signed in as: " .. auth.name
2424
2525 return {
2626 layout = {
+31-29lua/leetcode-menu/theme/menu.lua
@@ -1,33 +1,35 @@
11 local log = require("leetcode.logger")
22 local layout = require("leetcode-ui.layout")
3-local template = require("leetcode-menu.theme.template")
3+local Title = require("leetcode-menu.components.title")
4+
45 local padding = require("leetcode-ui.component.padding")
56 local button = require("leetcode-ui.component.button")
67 local group = require("leetcode-ui.component.group")
78
8-local section = template.get()
9-
10-local problems = button:init({ icon = "", src = "Problems" }, "p", function()
11- local bufnr = vim.api.nvim_get_current_buf()
12- db[bufnr]:set_layout("problems")
13- -- "<cmd>lua require('leetcode.api').cmd.dashboard('problems')<CR>"
14-end, true)
9+local problems = button:init(
10+ { icon = "", src = "Problems" },
11+ "p",
12+ function() _LC_MENU:set_layout("problems") end,
13+ true
14+)
1515
16-local statistics = button:init({ icon = "󰄪", src = "Statistics" }, "s", function()
17- -- "<cmd>lua require('leetcode.api').cmd.dashboard('stats')<cr>"
18-end, true)
16+local statistics = button:init({ icon = "󰄪", src = "Statistics" }, "s", function() end, true)
1917
20-local cookie = button:init({ src = "Cookie", icon = "󰆘" }, "c", function()
21- -- "<cmd>lua require('leetcode.api').cmd.dashboard('cookie')<cr>"
22- local bufnr = vim.api.nvim_get_current_buf()
23- db[bufnr]:set_layout("cookie")
24-end, true)
18+local cookie = button:init(
19+ { src = "Cookie", icon = "󰆘" },
20+ "c",
21+ function() _LC_MENU:set_layout("cookie") end,
22+ true
23+)
2524
26-local cache = button:init({ src = "Cache", icon = "" }, "n", function()
27- -- "<cmd>lua require('leetcode.api').cmd.dashboard('cache')<cr>"
28-end, true)
25+local cache = button:init(
26+ { src = "Cache", icon = "" },
27+ "n",
28+ function() _LC_MENU:set_layout("cache") end,
29+ true
30+)
2931
30-local exit = button:init({ src = "Exit", icon = "󰩈" }, "q", function() end)
32+local exit = button:init({ src = "Exit", icon = "󰩈" }, "q", function() vim.cmd.quitall() end)
3133
3234 local buttons = group:init({
3335 components = {
@@ -42,26 +44,26 @@ local buttons = group:init({
4244 },
4345 })
4446
45-section.title:append("Menu", "Comment")
47+-- section.title:append("Menu", "Comment")
48+
49+-- section.footer:append("Signed in as: " .. "kawre", "Keyword")
4650
47-section.footer:append("Signed in as: " .. "kawre", "Keyword")
51+local Header = require("leetcode-menu.components.header")
52+local Footer = require("leetcode-menu.components.footer")
4853
4954 return layout:init({
5055 contents = {
51- padding:init(2),
52- section.header,
53- padding:init(2),
56+ Header:init():content(),
5457
5558 -- section.notifications,
5659 -- padding:init(2),
5760
58- section.title,
61+ -- section.title,
62+ Title:init("Menu"):content(),
5963
60- padding:init(1),
6164 buttons,
62- padding:init(1),
6365
64- section.footer,
66+ Footer:init():content(),
6567 },
6668 opts = {
6769 margin = 5,
+10-17lua/leetcode-menu/theme/problems.lua
@@ -1,14 +1,14 @@
11 local log = require("leetcode.logger")
2+local api = require("leetcode.api")
3+
4+local Title = require("leetcode-menu.components.title")
5+local Footer = require("leetcode-menu.components.footer")
6+local Header = require("leetcode-menu.components.header")
7+
28 local group = require("leetcode-ui.component.group")
39 local padding = require("leetcode-ui.component.padding")
410 local button = require("leetcode-ui.component.button")
511 local layout = require("leetcode-ui.layout")
6-local api = require("leetcode.api")
7-
8-local template = require("leetcode-menu.theme.template")
9-local section = template.get()
10-
11-section.title:append("Problems")
1212
1313 local list_btn = button:init({ src = "List", icon = "" }, "p", function() api.cmd.problems() end)
1414
@@ -26,7 +26,7 @@ local qot_btn = button:init(
2626
2727 local back_btn = button:init({ src = "Back", icon = "" }, "q", function()
2828 local bufnr = vim.api.nvim_get_current_buf()
29- db[bufnr]:set_layout("menu")
29+ _LC_MENU:set_layout("menu")
3030 end)
3131
3232 local buttons = group:init({
@@ -44,26 +44,19 @@ local buttons = group:init({
4444 opts = { spacing = 1 },
4545 })
4646
47-section.footer:append("Signed in as: ")
48-
4947 return layout:init({
5048 contents = {
5149 -- header
52- padding:init(2),
53- section.header,
54- padding:init(2),
55-
56- -- notifications
57- section.notifications,
50+ Header:init():content(),
5851
5952 -- title
60- section.title,
53+ Title:init("Problems"):content(),
6154
6255 -- buttons
6356 buttons,
6457
6558 --footer
66- section.footer,
59+ Footer:init():content(),
6760 },
6861 opts = {},
6962 })
+49-0lua/leetcode-menu/theme/signin.lua
@@ -0,0 +1,49 @@
1+local log = require("leetcode.logger")
2+local layout = require("leetcode-ui.layout")
3+local Title = require("leetcode-menu.components.title")
4+local cmd = require("leetcode.api.command")
5+
6+local padding = require("leetcode-ui.component.padding")
7+local button = require("leetcode-ui.component.button")
8+local group = require("leetcode-ui.component.group")
9+
10+local problems = button:init(
11+ { icon = "󱛖", src = "Sign in" },
12+ "s",
13+ function() cmd.cookie_prompt() end,
14+ false
15+)
16+
17+local exit = button:init({ src = "Exit", icon = "󰩈" }, "q", function() vim.cmd.quitall() end)
18+
19+local buttons = group:init({
20+ components = {
21+ problems,
22+ exit,
23+ },
24+ opts = {
25+ spacing = 1,
26+ },
27+})
28+
29+-- section.title:append("Menu", "Comment")
30+
31+-- section.footer:append("Signed in as: " .. "kawre", "Keyword")
32+
33+local Header = require("leetcode-menu.components.header")
34+local Footer = require("leetcode-menu.components.footer")
35+
36+return layout:init({
37+ contents = {
38+ Header:init():content(),
39+
40+ Title:init("Sign in"):content(),
41+
42+ buttons,
43+
44+ Footer:init():content(),
45+ },
46+ opts = {
47+ margin = 5,
48+ },
49+})
+1-1lua/leetcode-menu/theme/stats.lua
@@ -25,7 +25,7 @@ section.buttons.val = {
2525 template.button("q", "󰩈 " .. " Exit LeetCode", "<cmd>qa<CR>"),
2626 }
2727
28-section.footer.val = "Signed in as: " .. auth.username
28+section.footer.val = "Signed in as: " .. auth.name
2929
3030 return {
3131 layout = {
+0-61lua/leetcode-menu/theme/template.lua
@@ -1,61 +0,0 @@
1-local text = require("leetcode-ui.component.text")
2-local group = require("leetcode-ui.component.group")
3-
4----@alias section table<string, lc-ui.Component>
5-
6----@class template
7----@field section section
8-local template = {}
9-
10----@return section
11-function template.get()
12- local header = text:init({
13- lines = {
14- [[ /$$ /$$ /$$$$$$ /$$ ]],
15- [[| $$ | $$ /$$__ $$ | $$ ]],
16- [[| $$ /$$$$$$ /$$$$$$ /$$$$$$ | $$ \__/ /$$$$$$ /$$$$$$$ /$$$$$$ ]],
17- [[| $$ /$$__ $$/$$__ $|_ $$_/ | $$ /$$__ $$/$$__ $$/$$__ $$]],
18- [[| $$ | $$$$$$$| $$$$$$$$ | $$ | $$ | $$ \ $| $$ | $| $$$$$$$$]],
19- [[| $$ | $$_____| $$_____/ | $$ /$| $$ $| $$ | $| $$ | $| $$_____/]],
20- [[| $$$$$$$| $$$$$$| $$$$$$$ | $$$$| $$$$$$| $$$$$$| $$$$$$| $$$$$$$]],
21- [[|________/\_______/\_______/ \___/ \______/ \______/ \_______/\_______/]],
22- },
23- opts = {
24- position = "center",
25- hl = "Keyword",
26- padding = {
27- bot = 2,
28- },
29- },
30- })
31-
32- local notifications = text:init({
33- opts = {
34- position = "center",
35- hl = "DiagnosticInfo",
36- },
37- })
38-
39- local title = text:init({
40- opts = {
41- position = "center",
42- hl = "Comment",
43- },
44- })
45-
46- local footer = text:init({
47- opts = {
48- position = "center",
49- hl = "Keyword",
50- },
51- })
52-
53- return {
54- header = header,
55- notifications = notifications,
56- title = title,
57- footer = footer,
58- }
59-end
60-
61-return template
+4-2lua/leetcode/api/command/init.lua
@@ -51,8 +51,8 @@ end
5151
5252 ---Merge configurations into default configurations and set it as user configurations.
5353 ---
54----@return lc.UserStatus | nil
55-function cmd.authenticate() gql.auth.user_status() end
54+---@return lc.UserAuth | nil
55+function cmd.authenticate() gql.auth.user() end
5656
5757 ---Merge configurations into default configurations and set it as user configurations.
5858 ---
@@ -84,6 +84,8 @@ function cmd.start()
8484 local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
8585 if #lines > 1 or (#lines == 1 and lines[1]:len() > 0) then return true end
8686
87+ gql.auth.user()
88+
8789 menu:init()
8890 end
8991
+19-25lua/leetcode/api/graphql/auth.lua
@@ -1,38 +1,32 @@
11 local utils = require("leetcode.api.graphql.utils")
22 local config = require("leetcode.config")
3+local log = require("leetcode.logger")
34
45 ---@class lc.AuthApi
56 local M = {}
67
7----@class lc.UserStatus
8----@field username string
9----@field is_signed_in boolean
10----@field is_premium boolean
11----@field user_id integer
12-
13----@return lc.UserStatus | nil
14-function M.user_status()
8+---@return config_auth
9+function M.user()
1510 local query = [[
16- query globalData {
17- userStatus {
18- userId
19- is_signed_in: isSignedIn
20- isMockUser
21- is_premium: isPremium
22- isVerified
23- username
24- activeSessionId
25- }
26- }
27- ]]
11+ query globalData {
12+ userStatus {
13+ id: userId
14+ is_signed_in: isSignedIn
15+ is_premium: isPremium
16+ name: username
2817
29- local ok, res = pcall(utils.query, query)
30- if not ok then return end
18+ isVerified
19+ activeSessionId
20+ isMockUser
21+ }
22+ }
23+ ]]
3124
32- local status = res["userStatus"]
33- config.authenticate(status)
25+ local ok, res = pcall(utils.query, query)
26+ if not ok then return {} end
3427
35- return res["userStatus"]
28+ config.auth = res["userStatus"]
29+ return config.auth
3630 end
3731
3832 return M
+2-2lua/leetcode/api/rest/utils.lua
@@ -47,11 +47,11 @@ function utils._get(url, cb)
4747 Job:new({
4848 command = "curl",
4949 args = args,
50- on_exit = function(self, val)
50+ on_exit = vim.schedule_wrap(function(self, val)
5151 local result = table.concat(self:result(), "\n")
5252 local decoded = vim.json.decode(result)
5353 cb(decoded)
54- end,
54+ end),
5555 on_stdout = function(error, data, self)
5656 -- log.info(data)
5757 end,
+14-19lua/leetcode/config/init.lua
@@ -2,31 +2,31 @@ local template = require("leetcode.config.template")
22
33 ---@class lc.Settings
44 local config = {
5+ default = template, ---@type lc.UserConfig Default User configuration
6+ user = template, ---@type lc.UserConfig User configuration
7+
58 name = "leetcode.nvim",
9+ domain = "https://leetcode.com",
610 debug = true,
711 notify = false,
812 lang = "cpp",
9- domain = "https://leetcode.com",
1013 }
1114
12----Default configurations.
13----
14----@type lc.Config
15-config.default = template
16-
17----User configurations.
18----
19----@type lc.Config
20-config.user = config.default
21-
22----@type lc.UserStatus|nil
15+---@class lc.UserAuth
16+---@field name string
17+---@field is_signed_in boolean
18+---@field is_premium boolean
19+---@field id integer
2320 config.auth = nil
2421
22+config.default = template ---@type lc.UserConfig Default User configuration
23+config.user = template ---@type lc.UserConfig User configuration
24+
2525 ---Merge configurations into default configurations and set it as user configurations.
2626 ---
27----@param cfg lc.Config Configurations to be merged.
27+---@param cfg lc.UserConfig Configurations to be merged.
2828 function config.apply(cfg)
29- config.user = vim.tbl_deep_extend("force", template, cfg)
29+ config.user = vim.tbl_deep_extend("force", config.default, cfg)
3030
3131 local ok, notify = pcall(require, "notify")
3232 if ok then
@@ -45,9 +45,4 @@ function config.apply(cfg)
4545 vim.api.nvim_set_hl(0, "LeetCodePreTag", { link = "@text" })
4646 end
4747
48----Merge configurations into default configurations and set it as user configurations.
49----
50----@param status lc.UserStatus | nil
51-function config.authenticate(status) config.auth = status end
52-
5348 return config
+1-1lua/leetcode/config/template.lua
@@ -1,4 +1,4 @@
1----@class lc.Config
1+---@class lc.UserConfig
22 local M = {
33 ---@type string
44 domain = "com",
+1-1lua/leetcode/init.lua
@@ -6,7 +6,7 @@ local cmd = require("leetcode.api.command")
66
77 local leetcode = {}
88
9----@param cfg? lc.Config
9+---@param cfg? lc.UserConfig
1010 function leetcode.setup(cfg)
1111 config.apply(cfg or {})
1212
+1-1lua/leetcode/runner/init.lua
@@ -28,7 +28,7 @@ function runner:run()
2828 rest.interpreter.interpret_solution(
2929 question.q.title_slug,
3030 body,
31- vim.schedule_wrap(function(item) self:callback(item) end)
31+ function(item) self:callback(item) end
3232 )
3333 end
3434