diff --git a/lua/pending/sync/gcal.lua b/lua/pending/sync/gcal.lua index 9158ca1..2eabc3e 100644 --- a/lua/pending/sync/gcal.lua +++ b/lua/pending/sync/gcal.lua @@ -15,13 +15,10 @@ local client = oauth.new({ config_key = 'gcal', }) ----@return string? calendar_id +---@param access_token string +---@return table? name_to_id ---@return string? err -local function find_or_create_calendar(access_token) - local cfg = config.get() - local gc = (cfg.sync and cfg.sync.gcal) or {} - local cal_name = gc.calendar or 'Pendings' - +local function get_all_calendars(access_token) local data, err = oauth.curl_request( 'GET', BASE_URL .. '/users/me/calendarList', @@ -30,27 +27,41 @@ local function find_or_create_calendar(access_token) if err then return nil, err end - + local result = {} for _, item in ipairs(data and data.items or {}) do - if item.summary == cal_name then - return item.id, nil + if item.summary then + result[item.summary] = item.id end end + return result, nil +end - local body = vim.json.encode({ summary = cal_name }) - local created, create_err = - oauth.curl_request('POST', BASE_URL .. '/calendars', oauth.auth_headers(access_token), body) - if create_err then - return nil, create_err +---@param access_token string +---@param name string +---@param existing table +---@return string? calendar_id +---@return string? err +local function find_or_create_calendar(access_token, name, existing) + if existing[name] then + return existing[name], nil end - - return created and created.id, nil + local body = vim.json.encode({ summary = name }) + local created, err = + oauth.curl_request('POST', BASE_URL .. '/calendars', oauth.auth_headers(access_token), body) + if err then + return nil, err + end + local id = created and created.id + if id then + existing[name] = id + end + return id, nil end ---@param date_str string ---@return string local function next_day(date_str) - local y, m, d = date_str:match('^(%d%d%d%d)-(%d%d)-(%d%d)$') + local y, m, d = date_str:match('^(%d%d%d%d)-(%d%d)-(%d%d)') local t = os.time({ year = tonumber(y) or 0, month = tonumber(m) or 0, day = tonumber(d) or 0 }) + 86400 return os.date('%Y-%m-%d', t) --[[@as string]] @@ -128,74 +139,99 @@ function M.auth() client:auth() end -function M.sync() - local access_token = client:get_access_token() - if not access_token then - return - end +function M.push() + oauth.async(function() + local access_token = client:get_access_token() + if not access_token then + return + end - local calendar_id, err = find_or_create_calendar(access_token) - if err or not calendar_id then - vim.notify('pending.nvim: ' .. (err or 'calendar not found'), vim.log.levels.ERROR) - return - end + local calendars, cal_err = get_all_calendars(access_token) + if cal_err or not calendars then + vim.notify('pending.nvim: ' .. (cal_err or 'failed to fetch calendars'), vim.log.levels.ERROR) + return + end - local tasks = require('pending').store():tasks() - local created, updated, deleted = 0, 0, 0 + local s = require('pending').store() + local created, updated, deleted = 0, 0, 0 - for _, task in ipairs(tasks) do - local extra = task._extra or {} - local event_id = extra['_gcal_event_id'] --[[@as string?]] + for _, task in ipairs(s:tasks()) do + local extra = task._extra or {} + local event_id = extra['_gcal_event_id'] --[[@as string?]] + local cal_id = extra['_gcal_calendar_id'] --[[@as string?]] - local should_delete = event_id ~= nil - and ( - task.status == 'done' - or task.status == 'deleted' - or (task.status == 'pending' and not task.due) - ) + local should_delete = event_id ~= nil + and cal_id ~= nil + and ( + task.status == 'done' + or task.status == 'deleted' + or (task.status == 'pending' and not task.due) + ) - if should_delete and event_id then - local del_err = delete_event(access_token, calendar_id, event_id) --[[@as string]] - if not del_err then - extra['_gcal_event_id'] = nil - if next(extra) == nil then - task._extra = nil + if should_delete then + local del_err = delete_event(access_token, cal_id, event_id) + if del_err then + vim.notify('pending.nvim: gcal delete failed: ' .. del_err, vim.log.levels.WARN) else - task._extra = extra - end - task.modified = os.date('!%Y-%m-%dT%H:%M:%SZ') --[[@as string]] - deleted = deleted + 1 - end - elseif task.status == 'pending' and task.due then - if event_id then - local upd_err = update_event(access_token, calendar_id, event_id, task) - if not upd_err then - updated = updated + 1 - end - else - local new_id, create_err = create_event(access_token, calendar_id, task) - if not create_err and new_id then - if not task._extra then - task._extra = {} + extra['_gcal_event_id'] = nil + extra['_gcal_calendar_id'] = nil + if next(extra) == nil then + task._extra = nil + else + task._extra = extra end - task._extra['_gcal_event_id'] = new_id task.modified = os.date('!%Y-%m-%dT%H:%M:%SZ') --[[@as string]] - created = created + 1 + deleted = deleted + 1 + end + elseif task.status == 'pending' and task.due then + local cat = task.category or config.get().default_category + if event_id and cal_id then + local upd_err = update_event(access_token, cal_id, event_id, task) + if upd_err then + vim.notify('pending.nvim: gcal update failed: ' .. upd_err, vim.log.levels.WARN) + else + updated = updated + 1 + end + else + local lid, lid_err = find_or_create_calendar(access_token, cat, calendars) + if lid_err or not lid then + vim.notify( + 'pending.nvim: gcal calendar failed: ' .. (lid_err or 'unknown'), + vim.log.levels.WARN + ) + else + local new_id, create_err = create_event(access_token, lid, task) + if create_err then + vim.notify('pending.nvim: gcal create failed: ' .. create_err, vim.log.levels.WARN) + elseif new_id then + if not task._extra then + task._extra = {} + end + task._extra['_gcal_event_id'] = new_id + task._extra['_gcal_calendar_id'] = lid + task.modified = os.date('!%Y-%m-%dT%H:%M:%SZ') --[[@as string]] + created = created + 1 + end + end end end end - end - require('pending').store():save() - require('pending')._recompute_counts() - vim.notify( - string.format( - 'pending.nvim: Synced to Google Calendar (created: %d, updated: %d, deleted: %d)', - created, - updated, - deleted + s:save() + require('pending')._recompute_counts() + local buffer = require('pending.buffer') + if buffer.bufnr() and vim.api.nvim_buf_is_valid(buffer.bufnr()) then + buffer.render(buffer.bufnr()) + end + vim.notify( + string.format( + 'pending.nvim: Google Calendar pushed — +%d ~%d -%d', + created, + updated, + deleted + ) ) - ) + end) end ---@return nil