441 lines
13 KiB
Lua

local computer = require("computer")
local component = require("component")
local GUI = require("GUI")
local buffer = require("doubleBuffering")
local fs = require("filesystem")
local web = require("web")
local json = require("json")
local color = require("color")
local image = require("image")
local base64 = require("base64")
local unicode = require("unicode")
local MineOSInterface = require("MineOSInterface")
local MineOSPaths = require("MineOSPaths")
--------------------------------------------------------------------------------
local user
local configPath = MineOSPaths.applicationData .. "/GitHub/Config.cfg"
local config = {
avatarColors = {
[11760002] = 0x3C3C3C,
}
}
local function saveConfig()
table.toFile(configPath, config)
end
if fs.exists(configPath) then
config = table.fromFile(configPath)
end
local addUserShit
--------------------------------------------------------------------------------
local mainContainer, window = MineOSInterface.addWindow(GUI.tabbedWindow(1, 1, 108, 33))
local titlePanel = window:addChild(GUI.panel(1, 1, window.width, 3, 0x2D2D2D))
window.tabBar:moveToFront()
window.actionButtons:moveToFront()
window.backgroundPanel.colors.background = 0xF0F0F0
window.tabBar:addItem("Repositories")
window.tabBar:addItem("Gists")
window.tabBar:addItem("Followers")
window.tabBar:addItem("Following")
local searchInput = window:addChild(GUI.input(9, 2, 14, 1, 0x3C3C3C, 0xB4B4B4, 0x696969, 0x3C3C3C, 0xE1E1E1, "", "Search…"))
local progressIndicator = window:addChild(GUI.progressIndicator(1, 1, 0x1E1E1E, 0x99FF80, 0x00B640))
local userContainer = window:addChild(GUI.container(3, 5, 20, 1))
local contentContainer = window:addChild(GUI.container(userContainer.localX + userContainer.width + 2, 4, 1, 1))
local function request(api)
local url = "https://api.github.com/" .. api
-- GUI.alert(url)
local data = ""
local success, reason = web.rawRequest(
url,
nil,
{
["User-Agent"]="Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0",
["Authorization"]="Basic " .. config.authorization
},
function(chunk)
data = data .. chunk
mainContainer:drawOnScreen()
progressIndicator:roll()
end,
math.huge
)
if success then
return json:decode(data)
else
return false, "API request failed: " .. tostring(reason)
end
end
local function avatarDraw(self)
local textColor = 0xFFFFFF - self.color
buffer.drawRectangle(self.x, self.y, self.width, self.height, self.color, textColor, " ")
buffer.drawText(math.floor(self.x + self.width / 2 - unicode.len(self.text) / 2), math.floor(self.y + self.height / 2), textColor, self.text)
end
local function newAvatar(x, y, width, height, id, name)
local self = GUI.object(x, y, width, height)
local shortcut = ""
for part in name:gmatch("[^%s]+") do
shortcut = shortcut .. unicode.upper(unicode.sub(part, 1, 1))
end
self.text = #shortcut > 0 and shortcut or unicode.upper(unicode.sub(name, 1, 1))
self.draw = avatarDraw
self.color = config.avatarColors[id]
if not self.color then
config.avatarColors[id] = color.HSBToInteger(math.random(0, 360), math.random(100) / 100, 1)
saveConfig()
end
return self
end
local function repositoryGUI(repositoryName, branches)
progressIndicator.active = true
local reason
if not branches then
branches, reason = request("repos/" .. user.login .. "/" .. repositoryName .. "/branches")
if not branches then
GUI.alert(reason)
progressIndicator.active = false
mainContainer:drawOnScreen()
return
end
end
contentContainer:removeChildren()
local path = ""
local branchesComboBox = contentContainer:addChild(GUI.comboBox(1, 2, 14, 1, 0xE1E1E1, 0x2D2D2D, 0xC3C3C3, 0x787878))
local pathLayout = contentContainer:addChild(GUI.layout(branchesComboBox.localX + branchesComboBox.width + 2, 2, contentContainer.width, 10, 1, 1))
pathLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
pathLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
pathLayout:setSpacing(1, 1, 0)
for i = 1, #branches do
branchesComboBox:addItem(branches[i].name)
end
local previewContainer = contentContainer:addChild(GUI.container(1, 4, contentContainer.width - 2, contentContainer.height - 3))
local fillList, fillPath, fillCode
fillList = function()
progressIndicator.active = true
local result, reason = request("repos/" .. user.login .. "/" .. repositoryName .. "/contents/" .. path .. "?ref=" .. branchesComboBox:getItem(branchesComboBox.selectedItem).text)
if result then
if result.type == "file" then
local lines = {}
for line in base64.decode(result.content:gsub("\r\n", "\n")):gmatch("[^\n]+") do
line = line:gsub("\t", " ")
table.insert(lines, line)
end
local codeView = previewContainer:addChild(GUI.codeView(1, 1, previewContainer.width, previewContainer.height, 1, 1, 1, {}, {}, GUI.LUA_SYNTAX_PATTERNS, GUI.LUA_SYNTAX_COLOR_SCHEME, fs.extension(result.name) == ".lua", lines))
else
-- Sort files alphabetically
local files = {}
local i = 1
while i <= #result do
if result[i].type ~= "dir" then
table.insert(files, result[i])
table.remove(result, i)
else
i = i + 1
end
end
table.sort(files, function(a, b) return unicode.lower(a.name) < unicode.lower(b.name) end)
i = 1
while i <= #files do
table.insert(result, files[i])
table.remove(files, i)
end
-- Fill files container
local function hyperlinkDraw(self)
buffer.drawRectangle(self.x, self.y, self.width, self.height, self.pressed and self.pressedColor or self.backgroundColor, self.textColor, " ")
buffer.drawText(self.x + 2, math.floor(self.y + self.height / 2), self.textColor, (self.node.type == "dir" and "" or "") .. self.node.name)
end
local function hyperlinkEventHandler(mainContainer, self, e1)
if e1 == "touch" then
self.pressed = true
mainContainer:drawOnScreen()
-- if self.node.type == "dir" then
path = path .. web.encode(self.node.name) .. "/"
fillPath()
fillList()
-- else
-- end
end
end
local function newHyperlink(x, y, width, height, backgroundColor, pressedColor, textColor, node)
local object = GUI.object(x, y, width, height)
object.pressed = false
object.backgroundColor = backgroundColor
object.pressedColor = pressedColor
object.textColor = textColor
object.draw = hyperlinkDraw
object.eventHandler = hyperlinkEventHandler
object.node = node
return object
end
previewContainer:removeChildren()
local y, step = 1, false
for i = 1, #result do
local hyperlink = previewContainer:addChild(newHyperlink(1, y, previewContainer.width, 3, step and 0xD2D2D2 or 0xE1E1E1, 0xC3C3C3, result[i].type == "dir" and 0x3C3C3C or 0x787878, result[i]))
y, step = y + hyperlink.height, not step
end
end
else
GUI.alert(reason)
end
progressIndicator.active = false
mainContainer:drawOnScreen()
end
fillPath = function()
pathLayout:removeChildren()
pathLayout:addChild(GUI.button(1, 1, unicode.len(repositoryName), 1, nil, 0x9949BF, nil, 0x332480, repositoryName)).onTouch = function()
path = ""
fillPath()
fillList()
end
pathLayout:addChild(GUI.text(1, 1, 0xC3C3C3, "/"))
local segments = fs.segments(path)
for i = 1, #segments - 1 do
pathLayout:addChild(GUI.button(1, 1, unicode.len(segments[i]), 1, nil, 0x9949BF, nil, 0x332480, segments[i])).onTouch = function()
path = table.concat(segments, "/", 1, i) .. "/"
fillPath()
fillList()
end
pathLayout:addChild(GUI.text(1, 1, 0xC3C3C3, "/"))
end
if #segments > 0 then
pathLayout:addChild(GUI.text(1, 1, 0x787878, segments[#segments]))
end
end
branchesComboBox.onItemSelected = function()
end
fillPath()
fillList()
end
local function repositoryDraw(self)
buffer.drawRectangle(self.x, self.y, self.width, self.height, self.pressed and 0xE1E1E1 or 0xFFFFFF, 0x969696, " ")
buffer.drawText(self.x + 2, self.y + 1, 0x004980, self.name)
buffer.drawText(self.x + 2, self.y + 3, 0x969696, self.lines[1])
buffer.drawText(self.x + 2, self.y + 4, 0x969696, self.lines[2])
buffer.drawText(self.x + 2, self.y + 6, 0x3C3C3C, self.stats)
end
local function repositoryEventHandler(mainContainer, self, e1)
if e1 == "touch" then
self.pressed = true
mainContainer:drawOnScreen()
repositoryGUI(self.name)
end
end
local function newRepository(x, y, width, repository)
local self = GUI.object(x, y, width, 8)
self.pressed = false
self.name = repository.name
self.lines = string.wrap(repository.description or "No description provided", width - 4)
if self.lines[2] then
self.lines[2] = string.limit(table.concat(self.lines, " ", 2), width - 4)
end
self.stats = (repository.language and "" .. repository.language .. " " or "") .. "* " .. repository.stargazers_count .. " # " .. repository.forks_count
self.draw = repositoryDraw
self.eventHandler = repositoryEventHandler
return self
end
local function repositoriesGUI(forks, page)
local startX, startY = 1, 2
local width = 40
local perHeight = math.floor((contentContainer.height - 3) / 9)
local perWidth = math.floor((contentContainer.width - startX) / width)
local perPage = perWidth * perHeight
progressIndicator.active = true
local result, reason = request("users/" .. user.login .. "/repos?page=" .. page .. "&per_page=" .. perPage .. "&sort=updated")
if result then
contentContainer:removeChildren()
local x, y, maxHeightPerLine = startX, startY, 0
for i = 1, math.min(#result, perPage) do
-- if forks and result[i].fork or not forks and not result[i].fork then
local repository = contentContainer:addChild(newRepository(x, y, width, result[i]))
maxHeightPerLine = math.max(maxHeightPerLine, repository.height)
x = x + repository.width + 2
if x > contentContainer.width - 2 then
x, y = startX, y + maxHeightPerLine + 1
maxHeightPerLine = 0
end
-- end
end
else
GUI.alert(reason)
end
progressIndicator.active = false
mainContainer:drawOnScreen()
end
local function loginGUI()
local loginContainer = window:addChild(GUI.container(1, 1, window.width, window.height))
window.actionButtons:moveToFront()
loginContainer:addChild(GUI.panel(1, 1, loginContainer.width, loginContainer.height, 0xF0F0F0))
local layout = loginContainer:addChild(GUI.layout(1, 1, loginContainer.width, loginContainer.height, 1, 1))
local try, again
try = function()
progressIndicator.active = true
layout:removeChildren()
layout:addChild(GUI.text(1, 1, 0x969696, "Logging in..."))
mainContainer:drawOnScreen()
local result, reason = request("users/" .. config.user)
if result and result.id then
loginContainer:remove()
user = result
addUserShit()
repositoriesGUI(false, 1)
else
GUI.alert("Incorrect login or password")
user = nil
config.authorization = nil
saveConfig()
again()
end
progressIndicator.active = false
mainContainer:drawOnScreen()
end
again = function()
layout:removeChildren()
local userInput = layout:addChild(GUI.input(1, 1, 26, 3, 0xE1E1E1, 0x787878, 0xB4B4B4, 0xE1E1E1, 0x2D2D2D, config.user or "", "Username"))
local passwordInput = layout:addChild(GUI.input(1, 1, 26, 3, 0xE1E1E1, 0x787878, 0xB4B4B4, 0xE1E1E1, 0x2D2D2D, "", "Password", false, "*"))
local submitButton = layout:addChild(GUI.button(1, 1, 26, 3, 0xC3C3C3, 0xFFFFFF, 0x969696, 0xFFFFFF, "Login"))
submitButton.onTouch = function()
config.user = userInput.text
config.authorization = base64.encode(userInput.text .. ":" .. passwordInput.text)
saveConfig()
try()
end
mainContainer:drawOnScreen()
end
if config.authorization then
try()
else
again()
end
end
addUserShit = function()
userContainer:removeChildren()
local y = 1
local avatar = userContainer:addChild(newAvatar(1, y, userContainer.width, userContainer.width / 2 - 1, user.id, user.name or user.login))
y = y + avatar.height + 1
userContainer:addChild(GUI.text(1, y, 0x2D2D2D, user.login))
y = y + 2
if user.name then
userContainer:addChild(GUI.text(1, y - 1, 0x969696, user.name))
y = y + 1
end
if user.bio then
local lines = string.wrap(user.bio, userContainer.width)
local textBox = userContainer:addChild(GUI.textBox(1, y, userContainer.width, #lines, nil, 0x969696, lines, 1))
textBox.eventHandler = nil
y = y + #lines + 1
end
userContainer:addChild(GUI.roundedButton(2, y, userContainer.width - 2, 1, 0xB4B4B4, 0xFFFFFF, 0x969696, 0xFFFFFF, "Logout")).onTouch = function()
config.authorization = nil
saveConfig()
loginGUI()
end
end
local function calculateSizes()
userContainer.height = window.height - 4
contentContainer.width, contentContainer.height = window.width - userContainer.width - 4, window.height - 3
progressIndicator.localX = window.width - progressIndicator.width
titlePanel.width = window.width
window.tabBar.width = window.width - progressIndicator.width - window.actionButtons.width - searchInput.width - 8
window.tabBar.localX = searchInput.localX + searchInput.width + 2
end
calculateSizes()
loginGUI()