· 6 years ago · Mar 01, 2020, 10:36 AM
1-- 137 - config
2-- 599 - show id
3-- 956 - settings
4
5local internet = require("Internet")
6local GUI = require("GUI")
7local screen = require("Screen")
8local system = require("System")
9local paths = require("Paths")
10local image = require("Image")
11local filesystem = require("Filesystem")
12local color = require("Color")
13local number = require("Number")
14local text = require("Text")
15local event = require("Event")
16
17--------------------------------------------------------------------------------
18
19local host = "http://mineos.modder.pw/MineOSAPI/2.04/"
20local iconCheckReponseTime = 2
21
22local overviewIconsCount = 14
23local overviewAnimationDelay = 0.05
24local overviewForceDecay = 0.15
25local overviewForceLimit = 0.5
26local overviewMaximumTouchAcceleration = 5
27
28local appMarketPath = paths.user.applicationData .. "App Market/"
29local configPath = appMarketPath .. "Config.cfg"
30local userPath = appMarketPath .. "User.cfg"
31local iconCachePath = appMarketPath .. "Cache/"
32
33local currentScriptDirectory = filesystem.path(system.getCurrentScript())
34local localization = system.getLocalization(currentScriptDirectory .. "Localizations/")
35
36local categories = {
37 localization.categoryApplications,
38 localization.categoryLibraries,
39 localization.categoryScripts,
40}
41
42local orderDirections = {
43 "desc",
44 "asc",
45}
46
47local downloadPaths = {
48 paths.system.applications,
49 paths.system.libraries,
50 "/",
51}
52
53local licenses = {
54 "MIT",
55 "GNU GPLv3",
56 "GNU AGPLv3",
57 "GNU LGPLv3",
58 "Apache Licence 2.0",
59 "Mozilla Public License 2.0",
60 "The Unlicense",
61}
62
63local orderBys = {
64 "popularity",
65 "rating",
66 "name",
67 "date",
68}
69
70local languages = {
71 [18] = "English",
72 [71] = "Russian",
73}
74
75--------------------------------------------------------------------------------
76
77filesystem.makeDirectory(iconCachePath)
78
79local luaIcon = image.load(currentScriptDirectory .. "Icons/Lua.pic")
80local fileNotExistsIcon = image.load(currentScriptDirectory .. "Icons/FileNotExists.pic")
81local scriptIcon = image.load(currentScriptDirectory .. "Icons/Script.pic")
82
83local search = ""
84local appWidth, appHeight, appHSpacing, appVSpacing, currentPage, appsPerPage, appsPerWidth, appsPerHeight = 32, 6, 2, 1, 0
85local config, userVersions, systemVersions, user
86local updateFileList, editPublication, messagesItem
87
88--------------------------------------------------------------------------------
89
90local workspace, window = system.addWindow(GUI.filledWindow(1, 1, 127, 33, 0xF0F0F0))
91
92local leftListPanel = window:addChild(GUI.panel(1, 1, 23, 3, 0x2D2D2D))
93local leftList = window:addChild(GUI.list(1, 4, leftListPanel.width, 1, 3, 0, 0x2D2D2D, 0x787878, 0x2D2D2D, 0x787878, 0xF0F0F0, 0x2D2D2D, false))
94
95local contentContainer = window:addChild(GUI.container(1, 1, 1, 1))
96
97local sponsoredLabel = window:addChild(GUI.text(2, 1, 0x3C3C3C, "Sponsored by McModder"))
98
99local progressIndicator = window:addChild(GUI.progressIndicator(math.floor(leftListPanel.width / 2 - 1), 1, 0x3C3C3C, 0x00B640, 0x99FF80))
100
101window.actionButtons.localX = 3
102window.actionButtons:moveToFront()
103
104--------------------------------------------------------------------------------
105
106local function saveConfig()
107 filesystem.writeTable(configPath, config)
108end
109
110local function saveVersions()
111 filesystem.writeTable(paths.system.versions, systemVersions, true)
112 filesystem.writeTable(paths.user.versions, userVersions, true)
113end
114
115local function saveUser()
116 filesystem.writeTable(userPath, user)
117end
118
119local function loadConfig()
120 local function loadVersions(path)
121 if filesystem.exists(path) then
122 return filesystem.readTable(path)
123 else
124 return {}
125 end
126 end
127
128 userVersions = loadVersions(paths.user.versions)
129 systemVersions = loadVersions(paths.system.versions)
130
131 if filesystem.exists(userPath) then
132 user = filesystem.readTable(userPath)
133 else
134 user = {}
135 end
136
137 if filesystem.exists(configPath) then
138 config = filesystem.readTable(configPath)
139 else
140 config = {
141 language_id = 18,
142 orderBy = 4,
143 orderDirection = 1,
144 singleSession = false,
145 K4K = {
146 autoClear = false,
147 showId = false
148 }
149 }
150 end
151
152 messagesItem.hidden = not user.token
153end
154
155--------------------------------------------------------------------------------
156
157local function RawAPIRequest(script, postData, notUnserialize)
158 progressIndicator.active = true
159 workspace:draw()
160
161 local data = ""
162 local success, reason = internet.rawRequest(
163 host .. script .. ".php",
164 postData and internet.serialize(postData) or nil,
165 {["User-Agent"] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36"},
166 function(chunk)
167 data = data .. chunk
168
169 progressIndicator:roll()
170 workspace:draw()
171 end,
172 math.huge
173 )
174
175 progressIndicator.active = false
176 workspace:draw()
177
178
179 if success then
180 if not notUnserialize then
181 local unserializeResult, unserializeReason = text.deserialize(data)
182 if unserializeResult then
183 if unserializeResult.success then
184 return unserializeResult
185 else
186 return false, "API request not succeded: " .. tostring(unserializeResult.reason)
187 end
188 else
189 return false, "Failed to unserialize response data: " .. tostring(unserializeReason)
190 end
191 else
192 return result
193 end
194 else
195 return false, "Web request failed: " .. tostring(reason)
196 end
197end
198
199local function fieldAPIRequest(fieldToReturn, script, data)
200 local success, reason = RawAPIRequest(script, data)
201 if success then
202 if success[fieldToReturn] then
203 return success[fieldToReturn]
204 else
205 GUI.alert("Request was successful, but field " .. tostring(fieldToReturn) .. " doesn't exists")
206 end
207 else
208 GUI.alert(reason)
209 end
210end
211
212local function checkContentLength(url)
213 local handle = component.get("internet").request(url)
214 if handle then
215 local deadline, _, _, responseData = computer.uptime() + iconCheckReponseTime
216 repeat
217 _, _, responseData = handle:response()
218 until responseData or computer.uptime() >= deadline
219
220 if responseData and responseData["Content-Length"] then
221 if tonumber(responseData["Content-Length"][1]) <= 10240 then
222 return handle
223 else
224 handle:close()
225 return false, "Specified image size is too big"
226 end
227 else
228 return false, "Too long without response"
229 end
230 else
231 return false, "Invalid URL"
232 end
233end
234
235local function checkImage(url, mneTolkoSprosit)
236 local handle, reason = checkContentLength(url)
237 if handle then
238 local needCheck, data, chunk, reason = true, ""
239 while true do
240 chunk, reason = handle.read(math.huge)
241 if chunk then
242 data = data .. chunk
243
244 progressIndicator:roll()
245 workspace:draw()
246
247 if needCheck and #data > 8 then
248 if data:sub(1, 4) == "OCIF" then
249 if string.byte(data:sub(5, 5)) == 6 then
250 if string.byte(data:sub(6, 6)) == 8 and string.byte(data:sub(7, 7)) == 4 then
251 if mneTolkoSprosit then
252 handle:close()
253 return true
254 end
255 needCheck = false
256 else
257 handle:close()
258 return false, "Image size is larger than 8x4"
259 end
260 else
261 handle:close()
262 return false, "Image encoding method is not supported"
263 end
264 else
265 handle:close()
266 return false, "Wrong image file signature"
267 end
268 end
269 else
270 handle:close()
271
272 if reason then
273 return false, reason
274 else
275 return data
276 end
277 end
278 end
279 else
280 return false, reason
281 end
282end
283
284local function tryToDownload(...)
285 local success, reason = internet.download(...)
286 if not success then
287 GUI.alert(reason)
288 end
289
290 return success
291end
292
293--------------------------------------------------------------------------------
294
295local lastMethod, lastArguments
296local function callLastMethod()
297 lastMethod(table.unpack(lastArguments))
298end
299
300local function showLabelAsContent(container, text)
301 container:removeChildren()
302 container:addChild(GUI.label(1, 1, container.width, container.height, 0x2D2D2D, text)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_CENTER)
303end
304
305local function newButtonsLayout(x, y, width, spacing)
306 local buttonsLayout = GUI.layout(x, y, width, 1, 1, 1)
307 buttonsLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
308 buttonsLayout:setSpacing(1, 1, spacing)
309
310 return buttonsLayout
311end
312
313local function getVersionsTable(file_id)
314 if systemVersions[file_id] then
315 return systemVersions
316 else
317 return userVersions
318 end
319end
320
321local function getUpdateState(file_id, version)
322 local versionsTable = getVersionsTable(file_id)
323 if versionsTable[file_id] then
324 if filesystem.exists(versionsTable[file_id].path) then
325 if versionsTable[file_id].version >= version then
326 return 4
327 else
328 return 3
329 end
330 else
331 return 2
332 end
333 else
334 return 1
335 end
336end
337
338local function ratingWidgetDraw(object)
339 local x = 0
340 for i = 1, 5 do
341 screen.drawText(object.x + x, object.y, object.rating >= i and object.colors.first or object.colors.second, "*")
342 x = x + object.spacing
343 end
344
345 return object
346end
347
348local function newRatingWidget(x, y, rating, firstColor, secondColor)
349 local object = GUI.object(x, y, 9, 1)
350
351 object.colors = {
352 first = firstColor or 0xFFB600,
353 second = secondColor or 0xC3C3C3
354 }
355 object.spacing = 2
356 object.draw = ratingWidgetDraw
357 object.rating = rating
358
359 return object
360end
361
362local function deletePublication(publication)
363 local container = GUI.addBackgroundContainer(workspace, true, true, localization.areYouSure)
364 local buttonsLayout = container.layout:addChild(newButtonsLayout(1, 1, container.layout.width, 3))
365
366 buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0xE1E1E1, 0x2D2D2D, 0x0, 0xE1E1E1, localization.yes)).onTouch = function()
367 local success, reason = RawAPIRequest("delete", {
368 token = user.token,
369 file_id = publication.file_id,
370 })
371
372 if success then
373 container:remove()
374 updateFileList(publication.category_id)
375 else
376 GUI.alert(reason)
377 end
378 end
379
380 buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0xA5A5A5, 0x2D2D2D, 0x0, 0xE1E1E1, localization.no)).onTouch = function()
381 container:remove()
382 workspace:draw()
383 end
384end
385
386--------------------------------------------------------------------------------
387
388local function getApplicationPathFromVersions(versionsPath)
389 return versionsPath:gsub("%.app/Main%.lua", ".app")
390end
391
392local function getDependencyPath(mainFilePath, dependency)
393 local path
394 -- Если зависимость - эт публикация
395 if dependency.publication_name then
396 path = downloadPaths[dependency.category_id] .. dependency.path
397 -- Если зависимость - эт ресурс
398 else
399 -- Ресурсы по абсолютному пути
400 if dependency.path:sub(1, 1) == "/" then
401 path = dependency.path
402 -- Ресурсы по релятивному пути
403 else
404 path = getApplicationPathFromVersions(mainFilePath) .. "/" .. dependency.path
405 end
406 end
407
408 return filesystem.removeSlashes(path)
409end
410
411local function download(publication)
412 if not publication.translated_description then
413 publication = fieldAPIRequest("result", "publication", {
414 file_id = publication.file_id,
415 language_id = config.language_id,
416 })
417 end
418
419 if publication then
420 local container = GUI.addBackgroundContainer(workspace, true, true, localization.choosePath)
421
422 local versionsTable = getVersionsTable(publication.file_id)
423 local filesystemChooserPath = versionsTable[publication.file_id] and getApplicationPathFromVersions(versionsTable[publication.file_id].path)
424 if not filesystemChooserPath then
425 if publication.category_id == 1 then
426 filesystemChooserPath = downloadPaths[publication.category_id] .. publication.publication_name .. ".app"
427 else
428 filesystemChooserPath = downloadPaths[publication.category_id] .. publication.path
429 end
430 end
431
432 local filesystemChooser = container.layout:addChild(GUI.filesystemChooser(1, 1, 44, 3, 0xE1E1E1, 0x2D2D2D, 0x4B4B4B, 0x969696, filesystemChooserPath, localization.save, localization.cancel, localization.fileName, "/"))
433 filesystemChooser:setMode(GUI.IO_MODE_SAVE, GUI.IO_MODE_FILE)
434
435 container.layout:addChild(GUI.text(1, 1, 0xE1E1E1, localization.tree))
436 local tree = container.layout:addChild(GUI.tree(1, 1, 44, 10, 0xE1E1E1, 0xA5A5A5, 0x3C3C3C, 0xA5A5A5, 0x3C3C3C, 0xE1E1E1, 0xB4B4B4, 0xA5A5A5, 0xC3C3C3, 0x444444))
437
438 local mainFilePath
439 local function updateTree()
440 tree.items = {}
441 tree.fromItem = 1
442
443 mainFilePath = filesystemChooser.path .. (publication.category_id == 1 and "/Main.lua" or "")
444
445 -- Вот тута будет йоба-древо
446 local dependencyTree = {}
447 local treeData = {
448 {
449 path = mainFilePath,
450 source_url = publication.source_url,
451 }
452 }
453
454 if publication.dependencies then
455 for i = 1, #publication.all_dependencies do
456 table.insert(treeData, publication.dependencies_data[publication.all_dependencies[i]])
457 end
458 end
459
460 for i = 1, #treeData do
461 local idiNahooy = dependencyTree
462 local dependencyPath = getDependencyPath(mainFilePath, treeData[i])
463
464 for blyad in filesystem.path(dependencyPath):gmatch("[^/]+") do
465 if not idiNahooy[blyad] then
466 idiNahooy[blyad] = {}
467 end
468 idiNahooy = idiNahooy[blyad]
469 end
470
471 table.insert(idiNahooy, {
472 path = dependencyPath,
473 source_url = treeData[i].source_url,
474 })
475 end
476
477 -- Пизда для формирования той ебалы, как ее там
478 local function pizda(t, offset, initPath)
479 for chlen, devka in pairs(t) do
480 if devka.path then
481 tree:addItem(filesystem.name(devka.path), devka.path, offset, false)
482 else
483 tree:addItem(chlen, chlen, offset, true)
484 tree.expandedItems[chlen] = true
485
486 pizda(devka, offset + 2, initPath .. chlen .. "/")
487 end
488 end
489 end
490
491 pizda(dependencyTree, 1, "")
492 end
493
494 local shortcutSwitchAndLabel = container.layout:addChild(GUI.switchAndLabel(1, 1, 44, 6, 0x66DB80, 0x0, 0xE1E1E1, 0x878787, localization.createShortcut .. ":", true))
495 shortcutSwitchAndLabel.hidden = publication.category_id == 2
496
497 container.layout:addChild(GUI.button(1, 1, 44, 3, 0x696969, 0xFFFFFF, 0x0, 0xFFFFFF, localization.download)).onTouch = function()
498 container.layout:removeChildren(2)
499 local progressBar = container.layout:addChild(GUI.progressBar(1, 1, 40, 0x66DB80, 0x0, 0xE1E1E1, 0, true, true, "", "%"))
500
501 local countOfShit = 1 + (publication.all_dependencies and #publication.all_dependencies or 0)
502
503 local function govnoed(pizda, i)
504 container.label.text = localization.downloading .. " " .. filesystem.name(pizda.path)
505 progressBar.value = math.floor(i / countOfShit * 100)
506 workspace:draw()
507 end
508
509 -- SAVED
510 versionsTable[publication.file_id] = {
511 path = mainFilePath,
512 version = publication.version,
513 }
514
515 govnoed(publication, 1)
516 tryToDownload(publication.source_url, mainFilePath)
517
518 if publication.dependencies then
519 for i = 1, #publication.all_dependencies do
520 local dependency = publication.dependencies_data[publication.all_dependencies[i]]
521 local dependencyPath = getDependencyPath(mainFilePath, dependency)
522
523 govnoed(dependency, i + 1)
524
525 if getUpdateState(publication.all_dependencies[i], dependency.version) < 4 then
526 versionsTable[publication.all_dependencies[i]] = {
527 path = dependencyPath,
528 version = dependency.version,
529 }
530 tryToDownload(dependency.source_url, dependencyPath)
531 else
532 event.sleep(0.05)
533 end
534 end
535 end
536
537 container:remove()
538 callLastMethod()
539
540 if not shortcutSwitchAndLabel.hidden and shortcutSwitchAndLabel.switch.state then
541 system.createShortcut(paths.user.desktop .. filesystem.hideExtension(filesystem.name(filesystemChooser.path)), filesystemChooser.path .. "/")
542 end
543
544 computer.pushSignal("system", "updateFileList")
545 saveVersions()
546 end
547
548 filesystemChooser.onSubmit = updateTree
549 updateTree()
550 end
551end
552
553local function addPanel(container, color)
554 container.panel = container:addChild(GUI.panel(1, 1, container.width, container.height, color or 0xFFFFFF))
555end
556
557local function loadImage(path)
558 local picture, reason = image.load(path)
559 if picture then
560 return picture
561 else
562 return fileNotExistsIcon
563 end
564end
565
566local function getPublicationIcon(publication)
567 if publication.icon_url then
568 local path = iconCachePath .. publication.file_id .. "@" .. publication.version .. ".pic"
569
570 if filesystem.exists(path) then
571 return loadImage(path)
572 else
573 progressIndicator.active = true
574 workspace:draw()
575
576 local data, reason = checkImage(publication.icon_url)
577
578 progressIndicator.active = false
579 workspace:draw()
580
581 if data then
582 filesystem.write(path, data)
583
584 return loadImage(path)
585 else
586 return fileNotExistsIcon
587 end
588 end
589 elseif publication.category_id == 2 then
590 return luaIcon
591 else
592 return scriptIcon
593 end
594end
595
596local function addApplicationInfo(container, publication, limit)
597 addPanel(container)
598 container.image = container:addChild(GUI.image(3, 2, getPublicationIcon(publication)))
599
600 if config.K4K.showId == false then
601 container.nameLabel = container:addChild(GUI.text(13, 2, 0x0, text.limit(publication.publication_name, limit, "right")))
602 else
603 container.nameLabel = container:addChild(GUI.text(13, 2, 0x0, text.limit(publication.publication_name .. " - id:" .. tostring(publication.file_id), limit, "right")))
604 end
605
606 container.developerLabel = container:addChild(GUI.text(13, 3, 0x878787, text.limit("©" .. publication.user_name, limit, "right")))
607 container.rating = container:addChild(newRatingWidget(13, 4, publication.average_rating and number.round(publication.average_rating) or 0))
608
609 local updateState = getUpdateState(publication.file_id, publication.version)
610 container.downloadButton = container:addChild(GUI.adaptiveRoundedButton(13, 5, 1, 0, 0xC3C3C3, 0xFFFFFF, 0x969696, 0xFFFFFF, updateState == 4 and localization.installed or updateState == 3 and localization.update or localization.install))
611 container.downloadButton.onTouch = function()
612 download(publication)
613 end
614 container.downloadButton.colors.disabled.background = 0xE1E1E1
615 container.downloadButton.colors.disabled.text = 0xFFFFFF
616 container.downloadButton.disabled = updateState == 4
617
618 if updateState > 2 then
619 container.downloadButton.width = container.downloadButton.width + 1
620 container:addChild(GUI.adaptiveRoundedButton(container.downloadButton.localX + container.downloadButton.width, container.downloadButton.localY, 1, 0, 0xF0F0F0, 0x969696, 0x969696, 0xFFFFFF, "x")).onTouch = function()
621 local versionsTable = getVersionsTable(publication.file_id)
622 filesystem.remove(getApplicationPathFromVersions(versionsTable[publication.file_id].path))
623 filesystem.remove(paths.user.desktop .. publication.publication_name .. ".lnk")
624 versionsTable[publication.file_id] = nil
625
626 callLastMethod()
627 computer.pushSignal("system", "updateFileList")
628 saveVersions()
629 end
630 end
631end
632
633local function containerScrollEventHandler(workspace, object, e1, e2, e3, e4, e5)
634 if e1 == "scroll" then
635 local first, last = object.children[1], object.children[#object.children]
636
637 if e5 == 1 then
638 if first.localY < 2 then
639 for i = 1, #object.children do
640 object.children[i].localY = object.children[i].localY + 1
641 end
642 workspace:draw()
643 end
644 else
645 if last.localY + last.height - 1 >= object.height then
646 for i = 1, #object.children do
647 object.children[i].localY = object.children[i].localY - 1
648 end
649 workspace:draw()
650 end
651 end
652 end
653end
654
655local newApplicationPreview, newPublicationInfo
656
657local function applicationWidgetEventHandler(workspace, object, e1)
658 if e1 == "touch" then
659 object.parent.panel.colors.background = 0xE1E1E1
660 workspace:draw()
661 newPublicationInfo(object.parent.file_id)
662 end
663end
664
665newApplicationPreview = function(x, y, publication)
666 local container = GUI.container(x, y, appWidth, appHeight)
667
668 container.file_id = publication.file_id
669 addApplicationInfo(container, publication, appWidth - 14)
670
671 container.panel.eventHandler,
672 container.image.eventHandler,
673 container.nameLabel.eventHandler,
674 container.developerLabel.eventHandler,
675 container.rating.eventHandler =
676 applicationWidgetEventHandler,
677 applicationWidgetEventHandler,
678 applicationWidgetEventHandler,
679 applicationWidgetEventHandler,
680 applicationWidgetEventHandler
681
682 return container
683end
684
685local function overview()
686 leftList.selectedItem = 1
687 lastMethod, lastArguments = overview, {}
688
689 local statistics = fieldAPIRequest("result", "statistics")
690 if statistics then
691 local publications = fieldAPIRequest("result", "publications", {
692 order_by = "popularity",
693 order_direction = "desc",
694 offset = 0,
695 count = overviewIconsCount + 1,
696 category_id = 1,
697 })
698
699 if publications then
700 contentContainer:removeChildren()
701
702 local iconsContainer = contentContainer:addChild(GUI.container(1, 1, contentContainer.width, contentContainer.height))
703
704 local width = appWidth + 4
705 local container = contentContainer:addChild(GUI.container(math.floor(contentContainer.width / 2 - width / 2), 1, width, contentContainer.height))
706 container:addChild(GUI.panel(1, 1, container.width, container.height, 0xFFFFFF))
707
708 local statisticsLayout = container:addChild(GUI.layout(1, 1, container.width, container.height, 1, 1))
709
710 statisticsLayout:addChild(GUI.image(1, 1, image.load(currentScriptDirectory .. "Icon.pic"))).height = 5
711
712 local textLayout = statisticsLayout:addChild(GUI.layout(1, 1, container.width - 4, 1, 1, 1))
713 textLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
714
715 local function add(key, value)
716 textLayout:addChild(GUI.keyAndValue(1, 1, 0x4B4B4B, 0xA5A5A5, key, ": " .. value))
717 end
718
719 add(localization.statisticsUsersCount, statistics.users_count)
720 add(localization.statisticsNewUser, statistics.last_registered_user)
721 add(localization.statisticsMostPopularUser, statistics.most_popular_user)
722 add(localization.statisticsPublicationsCount, statistics.publications_count)
723 add(localization.statisticsReviewsCount, statistics.reviews_count)
724 add(localization.statisticsMessagesCount, statistics.messages_count)
725
726 textLayout.height = #textLayout.children * 2
727
728 local applicationPreview = statisticsLayout:addChild(newApplicationPreview(1, 1, publications[1]))
729 applicationPreview.panel.colors.background = 0xF0F0F0
730 statisticsLayout:addChild(GUI.label(1, 1, statisticsLayout.width, 1, 0xA5A5A5, localization.statisticsPopularPublication)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_CENTER)
731
732 workspace:draw()
733
734 local uptime, newUptime = computer.uptime()
735 local function tick()
736 newUptime = computer.uptime()
737 if newUptime - uptime > overviewAnimationDelay then
738 uptime = newUptime
739
740 local child
741 for i = 1, #iconsContainer.children do
742 child = iconsContainer.children[i]
743
744 child.moveX, child.moveY = child.moveX + child.forceX, child.moveY + child.forceY
745
746 if child.forceX > 0 then
747 if child.forceX > overviewForceLimit then
748 child.forceX = child.forceX - overviewForceDecay
749 else
750 child.forceX = overviewForceLimit
751 end
752 else
753 if child.forceX < -overviewForceLimit then
754 child.forceX = child.forceX + overviewForceDecay
755 else
756 child.forceX = -overviewForceLimit
757 end
758 end
759
760 if child.forceY > 0 then
761 if child.forceY > overviewForceLimit then
762 child.forceY = child.forceY - overviewForceDecay
763 else
764 child.forceY = overviewForceLimit
765 end
766 else
767 if child.forceY < -overviewForceLimit then
768 child.forceY = child.forceY + overviewForceDecay
769 else
770 child.forceY = -overviewForceLimit
771 end
772 end
773
774 if child.moveX <= 1 then
775 child.forceX, child.moveX = -child.forceX, 1
776 elseif child.moveX + child.width - 1 >= iconsContainer.width then
777 child.forceX, child.moveX = -child.forceX, iconsContainer.width - child.width + 1
778 end
779
780 if child.moveY <= 1 then
781 child.forceY, child.moveY = -child.forceY, 1
782 elseif child.moveY + child.height - 1 >= iconsContainer.height then
783 child.forceY, child.moveY = -child.forceY, iconsContainer.height - child.height + 1
784 end
785
786 child.localX, child.localY = math.floor(child.moveX), math.floor(child.moveY)
787 end
788
789 workspace:draw()
790
791 return true
792 end
793 end
794
795 iconsContainer.eventHandler = function(workspace, object, e1, e2, e3, e4)
796 if e1 == "touch" or e1 == "drag" then
797 local child, deltaX, deltaY, vectorLength
798 for i = 1, #iconsContainer.children do
799 child = iconsContainer.children[i]
800
801 deltaX, deltaY = e3 - child.x, e4 - child.y
802 vectorLength = math.sqrt(deltaX ^ 2 + deltaY ^ 2)
803 if vectorLength > 0 then
804 child.forceX = deltaX / vectorLength * math.random(overviewMaximumTouchAcceleration)
805 child.forceY = deltaY / vectorLength * math.random(overviewMaximumTouchAcceleration)
806 end
807 end
808 end
809
810 tick()
811 end
812
813 local function makeBlyad(object)
814 object.localX = math.random(1, iconsContainer.width - object.width + 1)
815 object.localY = math.random(1, iconsContainer.height - object.width + 1)
816 object.moveX = object.localX
817 object.moveY = object.localY
818 object.forceX = math.random(-100, 100) / 100 * overviewForceLimit
819 object.forceY = math.random(-100, 100) / 100 * overviewForceLimit
820
821 if not tick() then
822 workspace:draw()
823 end
824 end
825
826 for i = 2, #publications do
827 makeBlyad(iconsContainer:addChild(GUI.image(1, 1, getPublicationIcon(publications[i])), 1))
828 end
829 end
830 end
831end
832
833local function settings()
834 local function logout()
835 user = {}
836 saveUser()
837 settings()
838 end
839
840 local function addAccountShit(login, register, recover)
841 contentContainer:removeChildren()
842
843 local layout = contentContainer:addChild(GUI.layout(1, 1, contentContainer.width, contentContainer.height, 1, 1))
844
845 layout:addChild(GUI.label(1, 1, 36, 1, 0x0, login and localization.login or register and localization.createAccount or recover and localization.changePassword)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
846 local nameInput = layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", localization.nickname))
847 local emailInput = layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", login and localization.nicknameOrEmail or "E-mail"))
848 local currentPasswordInput = layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", localization.currentPassword, false, "*"))
849 local passwordInput = layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", recover and localization.newPassword or localization.password, false, "*"))
850
851 local singleSessionSwitchAndLabel = layout:addChild(GUI.switchAndLabel(1, 1, 36, 6, 0x66DB80, 0xC3C3C3, 0xFFFFFF, 0xA5A5A5, localization.singleSession .. ":", config.singleSession))
852
853 layout:addChild(GUI.button(1, 1, 36, 3, 0xA5A5A5, 0xFFFFFF, 0x696969, 0xFFFFFF, "OK")).onTouch = function()
854 if login then
855 local result = fieldAPIRequest("result", "login", {
856 [(string.find(emailInput.text, "@") and "email" or "name")] = emailInput.text,
857 password = passwordInput.text
858 })
859
860 if result then
861 user = {
862 token = result.token,
863 name = result.name,
864 id = result.id,
865 email = result.email,
866 timestamp = result.timestamp,
867 }
868
869 settings()
870
871 config.singleSession = singleSessionSwitchAndLabel.switch.state
872 if not config.singleSession then
873 saveUser()
874 end
875 saveConfig()
876 end
877 elseif register then
878 local result = fieldAPIRequest("result", "register", {
879 name = nameInput.text,
880 email = emailInput.text,
881 password = passwordInput.text,
882 })
883
884 if result then
885 showLabelAsContent(contentContainer, localization.registrationSuccessfull)
886 end
887 else
888 local success, reason = RawAPIRequest("change_password", {
889 email = user.email,
890 current_password = currentPasswordInput.text,
891 new_password = passwordInput.text,
892 })
893
894 if success then
895 logout()
896 else
897 GUI.alert(reason)
898 end
899 end
900 end
901
902 if login then
903 local registerLayout = layout:addChild(GUI.layout(1, 1, layout.width, 1, 1, 1))
904 registerLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
905 registerLayout:addChild(GUI.text(1, 1, 0xA5A5A5, localization.notRegistered))
906 registerLayout:addChild(GUI.adaptiveButton(1, 1, 0, 0, nil, 0x696969, nil, 0x0, localization.createAccount)).onTouch = function()
907 addAccountShit(false, true, false)
908 end
909 end
910
911 currentPasswordInput.hidden = not recover
912 emailInput.hidden = recover
913 nameInput.hidden = login or recover
914 singleSessionSwitchAndLabel.hidden = not login
915 end
916
917 messagesItem.hidden = not user.token
918
919 if user.token then
920 local result = fieldAPIRequest("result", "publications", {
921 user_name = user.name,
922 order_by = "name",
923 order_direction = "asc",
924 })
925
926 if result then
927 contentContainer:removeChildren()
928 local layout = contentContainer:addChild(GUI.layout(1, 1, contentContainer.width, contentContainer.height, 1, 1))
929
930 layout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.profile))
931
932 local textLayout = layout:addChild(GUI.layout(1, 1, 36, 5, 1, 1))
933 textLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
934
935 textLayout:addChild(GUI.keyAndValue(1, 1, 0x696969, 0x969696, localization.nickname, ": " .. user.name))
936 textLayout:addChild(GUI.keyAndValue(1, 1, 0x696969, 0x969696, "E-Mail", ": " .. user.email))
937 textLayout:addChild(GUI.keyAndValue(1, 1, 0x696969, 0x969696, localization.registrationDate, ": " .. os.date("%d.%m.%Y", user.timestamp)))
938 textLayout.height = #textLayout.children * 2 - 1
939
940 local buttonsLayout = layout:addChild(newButtonsLayout(1, 1, layout.width, 2))
941
942 buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.changePassword)).onTouch = function()
943 addAccountShit(false, false, true)
944 end
945
946 buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.exit)).onTouch = function()
947 logout()
948 end
949
950 buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.clearCache)).onTouch = function()
951 local list = filesystem.list(iconCachePath)
952 for i = 1, #list do
953 filesystem.remove(iconCachePath .. list[i])
954 end
955 end
956
957 local autoClearSwitch = buttonsLayout:addChild(GUI.switchAndLabel(2, 2, 25, 8, 0x66DB80, 0x1D1D1D, 0xEEEEEE, 0x999999, "Автоочистка кэша", config.K4K.autoClear)).switch
958 autoClearSwitch.onStateChanged = function()
959 config.K4K.autoClear = autoClearSwitch.state
960 saveConfig()
961 end
962
963 local showIdSwitch = buttonsLayout:addChild(GUI.switchAndLabel(2, 2, 25, 8, 0x66DB80, 0x1D1D1D, 0xEEEEEE, 0x999999, "Показывать ID", config.K4K.showId)).switch
964 showIdSwitch.onStateChanged = function()
965 config.K4K.showId = showIdSwitch.state
966 saveConfig()
967 end
968
969 layout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.language))
970
971 local languageComboBox = layout:addChild(GUI.comboBox(1, 1, 36, 1, 0xFFFFFF, 0x696969, 0x969696, 0xE1E1E1))
972 for key, value in pairs(languages) do
973 languageComboBox:addItem(value).onTouch = function()
974 config.language_id = key
975 saveConfig()
976 end
977
978 if key == config.language_id then
979 languageComboBox.selectedItem = languageComboBox:count()
980 end
981 end
982
983 if #result > 0 then
984 layout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.publications))
985
986 local comboBox = layout:addChild(GUI.comboBox(1, 1, 36, 1, 0xFFFFFF, 0x696969, 0x969696, 0xE1E1E1))
987 for i = 1, #result do
988 comboBox:addItem(result[i].publication_name)
989 end
990
991 local buttonsLayout = layout:addChild(newButtonsLayout(1, 1, layout.width, 2))
992 buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.open)).onTouch = function()
993 newPublicationInfo(result[comboBox.selectedItem].file_id)
994 end
995
996 buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.edit)).onTouch = function()
997 local result = fieldAPIRequest("result", "publication", {
998 file_id = result[comboBox.selectedItem].file_id,
999 language_id = config.language_id,
1000 })
1001
1002 if result then
1003 editPublication(result)
1004 end
1005 end
1006 end
1007 end
1008 else
1009 addAccountShit(true, false, false)
1010 workspace:draw()
1011 end
1012end
1013
1014local function dialogGUI(to_user_name)
1015 local messages
1016 if to_user_name then
1017 local result = fieldAPIRequest("result", "messages", {
1018 token = user.token,
1019 user_name = to_user_name
1020 })
1021
1022 if result then
1023 messages = result
1024 end
1025 end
1026 messages = messages or {}
1027
1028 contentContainer:removeChildren()
1029
1030 local button = contentContainer:addChild(GUI.adaptiveButton(1, contentContainer.height - 2, 2, 1, 0x4B4B4B, 0xE1E1E1, 0x2D2D2D, 0xE1E1E1, localization.send))
1031 button.localX = contentContainer.width - button.width + 1
1032 button.colors.disabled.background = 0xB4B4B4
1033 button.colors.disabled.text = 0xFFFFFF
1034 button.disabled = true
1035
1036 local input = contentContainer:addChild(GUI.input(1, button.localY, contentContainer.width - button.width, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", localization.typeMessageText))
1037
1038 local function check()
1039 button.disabled = not (to_user_name and #input.text > 0)
1040 end
1041
1042 input.onInputFinished = check
1043
1044 button.onTouch = function()
1045 local success, reason = RawAPIRequest("message", {
1046 token = user.token,
1047 user_name = to_user_name,
1048 text = input.text
1049 })
1050
1051 if success then
1052 dialogGUI(to_user_name)
1053 else
1054 GUI.alert(reason)
1055 end
1056 end
1057
1058 local messagesContainer = contentContainer:addChild(GUI.container(1, 4, contentContainer.width, contentContainer.height - 6))
1059 messagesContainer.eventHandler = containerScrollEventHandler
1060
1061 local panel = contentContainer:addChild(GUI.panel(1, 1, contentContainer.width, 3, 0xFFFFFF))
1062 if not to_user_name then
1063 panel.colors.transparency = nil
1064 local text = contentContainer:addChild(GUI.text(3, 2, 0x0, localization.toWho))
1065 local input = contentContainer:addChild(GUI.input(text.localX + text.width, 1, contentContainer.width - text.width - 4, 3, 0xFFFFFF, 0x878787, 0xC3C3C3, 0xFFFFFF, 0x878787, to_user_name or "", localization.typeUserName))
1066 input.onInputFinished = function()
1067 to_user_name = input.text
1068 check()
1069 end
1070 else
1071 local keyAndValue = contentContainer:addChild(GUI.keyAndValue(1, 2, 0x878787, 0x0, localization.dialogWith, to_user_name))
1072 keyAndValue.localX = math.floor(contentContainer.width / 2 - (keyAndValue.keyLength + keyAndValue.valueLength) / 2)
1073 -- contentContainer:addChild(GUI.label(1, 2, contentContainer.width, 1, 0x2D2D2D, to_user_name)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_CENTER)
1074 end
1075
1076 local function cloudDraw(object)
1077 local backgroundColor, textColor = object.me and 0x6692FF or 0xFFFFFF, object.me and 0xFFFFFF or 0x4B4B4B
1078
1079 screen.drawRectangle(object.x, object.y, object.width, object.height, backgroundColor, textColor, " ")
1080 screen.drawText(object.x, object.y - 1, backgroundColor, "⢀" .. string.rep("⣀", object.width - 2) .. "⡀")
1081 screen.drawText(object.x, object.y + object.height, backgroundColor, "⠈" .. string.rep("⠉", object.width - 2) .. "⠁")
1082
1083 local date = os.date("%d.%m.%Y, %H:%M", object.timestamp)
1084 if object.me then
1085 screen.drawText(object.x - #date - 1, object.y, 0xC3C3C3, date)
1086 screen.drawText(object.x + object.width, object.y + object.height - 1, backgroundColor, "◤")
1087 else
1088 screen.drawText(object.x + object.width + 1, object.y, 0xC3C3C3, date)
1089 screen.drawText(object.x - 1, object.y + object.height - 1, backgroundColor, "◥")
1090 end
1091
1092 for i = 1, #object.lines do
1093 screen.drawText(object.x + 1, object.y + i - 1, textColor, object.lines[i])
1094 end
1095 end
1096
1097 local function newCloud(y, width, cloudText, me, timestamp)
1098 local lines = text.wrap(cloudText, width - 2)
1099 local object = GUI.object(me and messagesContainer.width - width - 1 or 3, y - #lines + 1, width, 1)
1100
1101 object.lines = lines
1102 object.height = #lines
1103 object.me = me
1104 object.text = cloudText
1105 object.draw = cloudDraw
1106 object.timestamp = timestamp
1107
1108 return object
1109 end
1110
1111 local y = messagesContainer.height - 1
1112 for j = 1, #messages do
1113 local cloud = messagesContainer:addChild(newCloud(y, math.floor(messagesContainer.width * 0.6), tostring(messages[j].text), messages[j].user_name == user.name, messages[j].timestamp), 1)
1114 y = y - cloud.height - 2
1115 end
1116
1117 -- Пустой объект для прокрутки ниже этой прозрачной пизды
1118 messagesContainer:addChild(GUI.object(1, y, 1, 2), 1)
1119end
1120
1121local function dialogs()
1122 local dialogs = fieldAPIRequest("result", "dialogs", {
1123 token = user.token,
1124 })
1125
1126 if dialogs then
1127 contentContainer:removeChildren()
1128
1129 local dialogsContainer = contentContainer:addChild(GUI.container(1, 1, contentContainer.width, contentContainer.height))
1130 dialogsContainer.eventHandler = containerScrollEventHandler
1131
1132 local sendMessageButton = dialogsContainer:addChild(GUI.adaptiveRoundedButton(1, #dialogs > 0 and 2 or math.floor(dialogsContainer.height / 2 + 1), 2, 0, 0x696969, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.newMessage))
1133 sendMessageButton.localX = math.floor(dialogsContainer.width / 2 - sendMessageButton.width / 2)
1134 sendMessageButton.onTouch = function()
1135 dialogGUI()
1136 end
1137
1138 if #dialogs > 0 then
1139 local y = sendMessageButton.localY + 2
1140
1141 for i = 1, #dialogs do
1142 local backgroundColor, nicknameColor, timestampColor, textColor = 0xFFFFFF, 0x0, 0xD2D2D2, 0x969696
1143 if dialogs[i].last_message_is_read == 0 and dialogs[i].last_message_user_name ~= user.name then
1144 backgroundColor, nicknameColor, timestampColor, textColor = 0xCCDBFF, 0x0, 0xB4B4B4, 0x878787
1145 end
1146
1147 local dialogContainer = dialogsContainer:addChild(GUI.container(3, y, dialogsContainer.width - 4, 4))
1148 addPanel(dialogContainer,backgroundColor)
1149
1150 dialogContainer:addChild(GUI.keyAndValue(3, 2, nicknameColor, timestampColor, dialogs[i].dialog_user_name, os.date(" (%d.%m.%Y, %H:%M)", dialogs[i].timestamp)))
1151 dialogContainer:addChild(GUI.text(3, 3, textColor, text.limit((dialogs[i].last_message_user_name == user.name and localization.yourText .. " " or "") .. dialogs[i].text, dialogContainer.width - 4, "right")))
1152
1153 dialogContainer.eventHandler = function(workspace, object, e1)
1154 if e1 == "touch" then
1155 dialogContainer.panel.colors.background = 0xE1E1E1
1156 dialogGUI(dialogs[i].dialog_user_name)
1157 end
1158 end
1159
1160 y = y + dialogContainer.height + 1
1161 end
1162 else
1163 dialogsContainer:addChild(GUI.label(1, sendMessageButton.localY - 2, dialogsContainer.width, 1, 0xA5A5A5, localization.hereBeYourDialogs)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_CENTER)
1164 end
1165 end
1166end
1167
1168newPublicationInfo = function(file_id)
1169 lastMethod, lastArguments = newPublicationInfo, {file_id}
1170
1171 local publication = fieldAPIRequest("result", "publication", {
1172 file_id = file_id,
1173 language_id = config.language_id,
1174 })
1175
1176 if publication then
1177 workspace:draw()
1178
1179 local reviews = fieldAPIRequest("result", "reviews", {
1180 file_id = file_id,
1181 offset = 0,
1182 count = 10,
1183 })
1184
1185 if reviews then
1186 contentContainer:removeChildren()
1187
1188 local infoContainer = contentContainer:addChild(GUI.container(1, 1, contentContainer.width, contentContainer.height))
1189 infoContainer.eventHandler = containerScrollEventHandler
1190
1191 -- Жирный йоба-лейаут для отображения ВАЩЕ всего - и инфы, и отзыввов
1192 local layout = infoContainer:addChild(GUI.layout(3, 2, infoContainer.width - 4, infoContainer.height, 1, 1))
1193 layout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
1194
1195 -- А вот эт уже контейнер чисто инфы крч
1196 local detailsContainer = layout:addChild(GUI.container(3, 2, layout.width, 6))
1197
1198 -- Тут будут находиться ваще пизда подробности о публикации
1199 local ratingsContainer = detailsContainer:addChild(GUI.container(1, 1, 28, 6))
1200 ratingsContainer.localX = detailsContainer.width - ratingsContainer.width + 1
1201 addPanel(ratingsContainer, 0xE1E1E1)
1202
1203 local y = 2
1204
1205 ratingsContainer:addChild(GUI.keyAndValue(2, y, 0x2D2D2D, 0x878787, localization.developer, ": " .. publication.user_name)); y = y + 1
1206 ratingsContainer:addChild(GUI.keyAndValue(2, y, 0x2D2D2D, 0x878787, localization.license, ": " .. licenses[publication.license_id])); y = y + 1
1207 ratingsContainer:addChild(GUI.keyAndValue(2, y, 0x2D2D2D, 0x878787, localization.category, ": " .. categories[publication.category_id])); y = y + 1
1208 ratingsContainer:addChild(GUI.keyAndValue(2, y, 0x2D2D2D, 0x878787, localization.version, ": " .. publication.version)); y = y + 1
1209 ratingsContainer:addChild(GUI.keyAndValue(2, y, 0x2D2D2D, 0x878787, localization.updated, ": " .. os.date("%d.%m.%Y", publication.timestamp))); y = y + 1
1210
1211 -- Добавляем инфу с общими рейтингами
1212 if #reviews > 0 then
1213 local ratings = {0, 0, 0, 0, 0}
1214 for i = 1, #reviews do
1215 ratings[reviews[i].rating] = ratings[reviews[i].rating] + 1
1216 end
1217
1218 y = y + 1
1219 ratingsContainer:addChild(GUI.keyAndValue(2, y, 0x2D2D2D, 0x878787, localization.reviews, ": " .. #reviews)); y = y + 1
1220 ratingsContainer:addChild(GUI.keyAndValue(2, y, 0x2D2D2D, 0x878787, localization.averageRating, ": " .. string.format("%.1f", publication.average_rating or 0))); y = y + 1
1221
1222 for i = #ratings, 1, -1 do
1223 local text = tostring(ratings[i])
1224 local textLength = #text
1225 ratingsContainer:addChild(newRatingWidget(2, y, i, nil, 0xC3C3C3))
1226 ratingsContainer:addChild(GUI.progressBar(12, y, ratingsContainer.width - textLength - 13, 0x2D2D2D, 0xC3C3C3, 0xC3C3C3, ratings[i] / #reviews * 100, true))
1227 ratingsContainer:addChild(GUI.text(ratingsContainer.width - textLength, y, 0x2D2D2D, text))
1228 y = y + 1
1229 end
1230 end
1231
1232 -- Фигачим кнопочки на изменение хуйни
1233 if user.token then
1234 y = y + 1
1235
1236 local buttonsLayout = ratingsContainer:addChild(GUI.layout(1, y, ratingsContainer.width, 3, 1, 1))
1237
1238 if publication.user_name == user.name then
1239 buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xA5A5A5, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.edit)).onTouch = function()
1240 editPublication(publication)
1241 end
1242
1243 buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xA5A5A5, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.remove)).onTouch = function()
1244 deletePublication(publication)
1245 end
1246 else
1247 buttonsLayout:addChild(GUI.adaptiveRoundedButton(2, 1, 1, 0, 0xA5A5A5, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.newMessageToDeveloper)).onTouch = function()
1248 dialogGUI(publication.user_name)
1249 end
1250
1251 local existingReviewText
1252 if #reviews > 0 then
1253 for i = 1, #reviews do
1254 if reviews[i].user_name == user.name then
1255 existingReviewText = reviews[i].comment
1256 break
1257 end
1258 end
1259 end
1260
1261 buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xA5A5A5, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, existingReviewText and localization.changeReview or localization.writeReview)).onTouch = function()
1262 local container = GUI.addBackgroundContainer(workspace, true, true, existingReviewText and localization.changeReview or localization.writeReview)
1263
1264 local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, existingReviewText or "", localization.writeReviewHere))
1265
1266 local pizda = container.layout:addChild(GUI.container(1, 1, 1, 1))
1267 local eblo = pizda:addChild(GUI.text(1, 1, 0xE1E1E1, localization.yourRating .. ": "))
1268 pizda.width = eblo.width + 9
1269
1270 local cyka = pizda:addChild(newRatingWidget(eblo.width + 1, 1, 4))
1271 cyka.eventHandler = function(workspace, object, e1, e2, e3)
1272 if e1 == "touch" then
1273 cyka.rating = number.round((e3 - object.x + 1) / object.width * 5)
1274 workspace:draw()
1275 end
1276 end
1277
1278 local govno = container.layout:addChild(GUI.button(1, 1, 36, 3, 0x696969, 0xFFFFFF, 0x3C3C3C, 0xFFFFFF, "OK"))
1279 govno.disabled = true
1280 govno.colors.disabled.background = 0xA5A5A5
1281 govno.colors.disabled.text = 0xC3C3C3
1282 govno.onTouch = function()
1283 local success, reason = RawAPIRequest("review", {
1284 token = user.token,
1285 file_id = publication.file_id,
1286 rating = cyka.rating,
1287 comment = input.text,
1288 })
1289
1290 container:remove()
1291 workspace:draw()
1292
1293 if success then
1294 newPublicationInfo(publication.file_id)
1295 else
1296 GUI.alert(reason)
1297 end
1298 end
1299
1300 input.onInputFinished = function()
1301 local textLength, from, to = unicode.len(input.text), 2, 1000
1302 if textLength >= from and textLength <= to then
1303 govno.disabled = false
1304 else
1305 govno.disabled = true
1306 if textLength > to then
1307 GUI.alert("Too big review length (" .. textLength .. "). Maximum is " .. to)
1308 end
1309 end
1310
1311 workspace:draw()
1312 end
1313
1314 input.onInputFinished()
1315 end
1316 end
1317 end
1318
1319 -- Добавляем контейнер под описание и прочую пизду
1320 local textDetailsContainer = detailsContainer:addChild(GUI.container(1, 1, detailsContainer.width - ratingsContainer.width, detailsContainer.height))
1321 -- Ебурим саму пизду
1322 addApplicationInfo(textDetailsContainer, publication, math.huge)
1323 -- Ебурим описание
1324 local x, y = 3, 7
1325
1326 local function addLabel(text)
1327 textDetailsContainer:addChild(GUI.label(1, y, textDetailsContainer.width, 1, 0x696969, text)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
1328 y = y + 2
1329 end
1330
1331 local function addTextBox(t)
1332 local lines = text.wrap(t, textDetailsContainer.width - 4)
1333 local textBox = textDetailsContainer:addChild(GUI.textBox(3, y, textDetailsContainer.width - 4, #lines, nil, 0x969696, lines, 1, 0, 0))
1334 textBox.eventHandler = nil
1335 y = y + textBox.height + 1
1336 end
1337
1338 addTextBox(publication.translated_description)
1339
1340 -- Инфа о чем-то новом
1341 if publication.whats_new then
1342 addLabel(localization.whatsNewInVersion .. " " .. publication.whats_new_version .. ":")
1343 addTextBox(publication.whats_new)
1344 end
1345
1346 -- Зависимости
1347 if publication.dependencies then
1348 local publicationDependencyExists = false
1349 for i = 1, #publication.all_dependencies do
1350 if publication.dependencies_data[publication.all_dependencies[i]].publication_name then
1351 publicationDependencyExists = true
1352 break
1353 end
1354 end
1355
1356 if publicationDependencyExists then
1357 addLabel(localization.dependencies .. ":")
1358
1359 for i = 1, #publication.all_dependencies do
1360 local dependency = publication.dependencies_data[publication.all_dependencies[i]]
1361 if dependency.publication_name then
1362 local textLength = unicode.len(dependency.publication_name)
1363 if x + textLength + 4 > textDetailsContainer.width - 4 then
1364 x, y = 3, y + 2
1365 end
1366
1367 local button = textDetailsContainer:addChild(GUI.tagButton(x, y, textLength + 2, 1, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, dependency.publication_name))
1368 button.onTouch = function()
1369 newPublicationInfo(publication.all_dependencies[i])
1370 end
1371
1372 x = x + button.width + 2
1373 end
1374 end
1375
1376 y = y + 2
1377 end
1378 end
1379
1380 -- Подсчитываем результирующие размеры
1381 textDetailsContainer.height = math.max(
1382 textDetailsContainer.children[#textDetailsContainer.children].localY + textDetailsContainer.children[#textDetailsContainer.children].height,
1383 ratingsContainer.children[#ratingsContainer.children].localY + ratingsContainer.children[#ratingsContainer.children].height
1384 )
1385 textDetailsContainer.panel.height = textDetailsContainer.height
1386 ratingsContainer.height = textDetailsContainer.height
1387 ratingsContainer.panel.height = textDetailsContainer.height
1388 detailsContainer.height = textDetailsContainer.height
1389
1390 if #reviews > 0 then
1391 -- Отображаем все оценки
1392 layout:addChild(GUI.text(1, 1, 0x696969, localization.reviewsOfUsers))
1393
1394 -- Перечисляем все отзывы
1395 for i = 1, #reviews do
1396 local reviewContainer = layout:addChild(GUI.container(1, 1, layout.width, 4))
1397 addPanel(reviewContainer)
1398
1399 local y = 2
1400 local nameLabel = reviewContainer:addChild(GUI.text(3, y, 0x2D2D2D, reviews[i].user_name))
1401 reviewContainer:addChild(GUI.text(nameLabel.localX + nameLabel.width + 1, y, 0xC3C3C3, "(" .. os.date("%d.%m.%Y", reviews[i].timestamp) .. ")"))
1402 y = y + 1
1403
1404 reviewContainer:addChild(newRatingWidget(3, y, reviews[i].rating))
1405 y = y + 1
1406
1407 local lines = text.wrap(tostring(reviews[i].comment), reviewContainer.width - 4)
1408 local textBox = reviewContainer:addChild(GUI.textBox(3, y, reviewContainer.width - 4, #lines, nil, 0x878787, lines, 1, 0, 0))
1409 textBox.eventHandler = nil
1410 y = y + #lines
1411
1412 if reviews[i].votes or user.token and user.name ~= reviews[i].user_name then
1413 y = y + 1
1414 end
1415
1416 if reviews[i].votes then
1417 reviewContainer:addChild(GUI.text(3, y, 0xC3C3C3, reviews[i].votes.positive .. " " .. localization.of .. " " .. reviews[i].votes.total .. " " .. localization.usersLoveReview))
1418 y = y + 1
1419 end
1420
1421 if user.token and user.name ~= reviews[i].user_name then
1422 local wasHelpText = reviewContainer:addChild(GUI.text(3, y, 0xC3C3C3, localization.wasReviewHelpful))
1423
1424 local layout = reviewContainer:addChild(GUI.layout(wasHelpText.localX + wasHelpText.width + 1, y, reviewContainer.width - wasHelpText.localX - wasHelpText.width - 1, 1, 1, 1))
1425 layout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
1426 layout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
1427
1428 local function go(rating)
1429 local success = fieldAPIRequest("result", "review_vote", {
1430 token = user.token,
1431 review_id = reviews[i].id,
1432 rating = rating
1433 })
1434
1435 if success then
1436 wasHelpText.text = localization.thanksForVote
1437 wasHelpText.color = 0x696969
1438 layout:remove()
1439 end
1440 end
1441
1442 layout:addChild(GUI.adaptiveButton(1, 1, 0, 0, nil, 0x696969, nil, 0x2D2D2D, localization.yes)).onTouch = function()
1443 go(1)
1444 end
1445 layout:addChild(GUI.text(1, 1, 0xC3C3C3, "|"))
1446 layout:addChild(GUI.adaptiveButton(1, 1, 0, 0, nil, 0x696969, nil, 0x2D2D2D, localization.no)).onTouch = function()
1447 go(0)
1448 end
1449 layout:addChild(GUI.text(1, 1, 0xC3C3C3, "|"))
1450 layout:addChild(GUI.adaptiveButton(1, 1, 0, 0, nil, 0x696969, nil, 0x2D2D2D, localization.newMessagePersonal)).onTouch = function()
1451 dialogGUI(reviews[i].user_name.user_name)
1452 end
1453
1454 y = y + 1
1455 end
1456
1457 reviewContainer.height = y
1458 reviewContainer.panel.height = reviewContainer.height
1459 end
1460 end
1461
1462 layout:update()
1463 layout.height = layout.children[#layout.children].localY + layout.children[#layout.children].height - 1
1464 end
1465 end
1466end
1467
1468--------------------------------------------------------------------------------
1469
1470local function newPlusMinusCyka(width, disableLimit)
1471 local layout = GUI.layout(1, 1, width, 1, 2, 1)
1472 layout:setColumnWidth(1, GUI.SIZE_POLICY_RELATIVE, 1.0)
1473 layout:setColumnWidth(2, GUI.SIZE_POLICY_ABSOLUTE, 8)
1474 layout:setFitting(1, 1, true, false)
1475 layout:setMargin(2, 1, 1, 0)
1476 layout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
1477 layout:setAlignment(2, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
1478 layout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
1479 layout:setDirection(2, 1, GUI.DIRECTION_HORIZONTAL)
1480
1481 layout.comboBox = layout:addChild(GUI.comboBox(1, 1, width - 7, 1, 0xFFFFFF, 0x878787, 0x969696, 0xE1E1E1))
1482 layout.defaultColumn = 2
1483 layout.addButton = layout:addChild(GUI.button(1, 1, 3, 1, 0x696969, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, "+"))
1484 layout.removeButton = layout:addChild(GUI.button(1, 1, 3, 1, 0x696969, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, "-"))
1485
1486 local overrideRemoveButtonDraw = layout.removeButton.draw
1487 layout.removeButton.draw = function(...)
1488 layout.removeButton.disabled = layout.comboBox:count() <= disableLimit
1489 overrideRemoveButtonDraw(...)
1490 end
1491
1492 layout.removeButton.onTouch = function()
1493 layout.comboBox:removeItem(layout.comboBox.selectedItem)
1494 workspace:draw()
1495 end
1496
1497 return layout
1498end
1499
1500editPublication = function(initialPublication, initialCategoryID)
1501 lastMethod, lastArguments = editPublication, {initialPublication}
1502 contentContainer:removeChildren()
1503
1504 local layout = contentContainer:addChild(GUI.layout(1, 1, contentContainer.width, contentContainer.height, 3, 1))
1505 layout:setColumnWidth(1, GUI.SIZE_POLICY_RELATIVE, 0.5)
1506 layout:setColumnWidth(2, GUI.SIZE_POLICY_ABSOLUTE, 36)
1507 layout:setColumnWidth(3, GUI.SIZE_POLICY_RELATIVE, 0.5)
1508 layout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_RIGHT, GUI.ALIGNMENT_VERTICAL_CENTER)
1509 layout:setAlignment(2, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_CENTER)
1510 layout:setFitting(2, 1, true, false)
1511 layout:setMargin(1, 1, 1, 0)
1512
1513 local function addText(text)
1514 return layout:addChild(GUI.text(1, 1, 0x4B4B4B, text))
1515 end
1516
1517 local function addInput(...)
1518 return layout:addChild(GUI.input(1, 1, 36, 1, 0xFFFFFF, 0x878787, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, ...))
1519 end
1520
1521 local function addComboBox()
1522 return layout:addChild(GUI.comboBox(1, 1, 36, 1, 0xFFFFFF, 0x878787, 0x969696, 0xE1E1E1))
1523 end
1524
1525 addText(localization.category .. ":")
1526 addText(localization.license .. ":")
1527 addText(localization.publicationName .. ":")
1528 addText(localization.mainFileURL .. ":")
1529 local iconHint = addText(localization.iconURL .. ":")
1530 local pathHint = addText(localization.mainFileName .. ":")
1531 addText(localization.description .. ":")
1532 local whatsNewHint = addText(localization.whatsNew .. ":")
1533 addText(localization.dependenciesAndResources .. ":")
1534
1535 layout.defaultColumn = 2
1536
1537 layout:addChild(GUI.label(1, 1, 36, 1, 0x0, initialPublication and localization.edit or localization.publish)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
1538
1539 local categoryComboBox = addComboBox()
1540 for i = 1, #categories do
1541 categoryComboBox:addItem(categories[i])
1542 end
1543
1544 local licenseComboBox = addComboBox()
1545 for i = 1, #licenses do
1546 licenseComboBox:addItem(licenses[i])
1547 end
1548
1549 local nameInput = addInput(initialPublication and initialPublication.publication_name or "", "My publication")
1550 local mainUrlInput = addInput(initialPublication and initialPublication.source_url or "", "http://example.com/Main.lua")
1551 local iconUrlInput = addInput(initialPublication and initialPublication.icon_url or "", "http://example.com/Icon.pic")
1552 local mainPathInput = addInput(initialPublication and initialPublication.path or "", "MyScript.lua")
1553 local descriptionInput = addInput(initialPublication and initialPublication.initial_description or "", "This's my favourite script", true)
1554 local whatsNewInput = addInput("", "Added some cool features...")
1555 local dependenciesLayout = layout:addChild(newPlusMinusCyka(36, 0))
1556
1557 local function addDependency(dependency)
1558 local text
1559 if dependency.publication_name then
1560 text = "#" .. dependency.publication_name
1561 else
1562 if dependency.path:sub(1, 1) == "/" then
1563 text = dependency.path
1564 else
1565 text = "../" .. dependency.path
1566 end
1567 end
1568 dependenciesLayout.comboBox:addItem(text).dependency = dependency
1569 dependenciesLayout.comboBox.selectedItem = dependenciesLayout.comboBox:count()
1570 end
1571
1572 if initialPublication and initialPublication.dependencies then
1573 for i = 1, #initialPublication.dependencies do
1574 local dependency = initialPublication.dependencies_data[initialPublication.dependencies[i]]
1575 if dependency.publication_name then
1576 addDependency({publication_name = dependency.publication_name})
1577 elseif dependency.path ~= "Icon.pic" then
1578 addDependency({source_url = dependency.source_url, path = dependency.path})
1579 end
1580 end
1581 end
1582
1583 if initialPublication then
1584 categoryComboBox.selectedItem = initialPublication.category_id
1585 licenseComboBox.selectedItem = initialPublication.license_id
1586 elseif initialCategoryID then
1587 categoryComboBox.selectedItem = initialCategoryID
1588 end
1589 whatsNewHint.hidden, whatsNewInput.hidden = not initialPublication, not initialPublication
1590
1591 local lastDependencyType = 1
1592 dependenciesLayout.addButton.onTouch = function()
1593 local container = GUI.addBackgroundContainer(workspace, true, true, localization.addDependency)
1594
1595 local dependencyTypeComboBox = container.layout:addChild(GUI.comboBox(1, 1, 36, 3, 0xFFFFFF, 0x4B4B4B, 0x969696, 0xE1E1E1))
1596 dependencyTypeComboBox:addItem(localization.fileByURL)
1597 dependencyTypeComboBox:addItem(localization.localizationDependency)
1598 dependencyTypeComboBox:addItem(localization.existingPublication)
1599 dependencyTypeComboBox.selectedItem = lastDependencyType
1600
1601 local publicationNameInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", "MineOS"))
1602 local urlInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", ""))
1603 local pathInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", ""))
1604 local pathType = container.layout:addChild(GUI.switchAndLabel(1, 1, 36, 6, 0x66DB80, 0x0, 0xE1E1E1, 0x878787, localization.relativePath .. ":", true))
1605
1606 local button = container.layout:addChild(GUI.button(1, 1, 36, 3, 0x696969, 0xFFFFFF, 0x0, 0xFFFFFF, localization.add))
1607 button.onTouch = function()
1608 addDependency({
1609 publication_name = lastDependencyType == 3 and publicationNameInput.text or nil,
1610 path =
1611 lastDependencyType == 1 and
1612 (
1613 not pathType.hidden and pathType.switch.state and pathInput.text:gsub("^/+", "") or filesystem.removeSlashes("/" .. pathInput.text)
1614 ) or
1615 lastDependencyType == 2 and
1616 (
1617 "Localizations/" .. filesystem.name(urlInput.text)
1618 ) or
1619 nil,
1620 source_url = lastDependencyType ~= 3 and urlInput.text or nil,
1621 })
1622
1623 container:remove()
1624 workspace:draw()
1625 end
1626
1627 publicationNameInput.onInputFinished = function()
1628 if lastDependencyType == 1 then
1629 button.disabled = #pathInput.text == 0 or #urlInput.text == 0
1630 elseif lastDependencyType == 2 then
1631 button.disabled = #urlInput.text == 0 or filesystem.extension(urlInput.text) ~= ".lang"
1632 else
1633 button.disabled = #publicationNameInput.text == 0
1634 end
1635 end
1636 pathInput.onInputFinished, urlInput.onInputFinished = publicationNameInput.onInputFinished, publicationNameInput.onInputFinished
1637
1638 local function updatePlaceholder()
1639 urlInput.placeholderText = lastDependencyType == 1 and "http://example.com/Main.lua" or "http://example.com/English.lang"
1640 pathInput.placeholderText = pathType.switch.state and "Resources/Main.lua" or "Users/Scripts/Main.lua"
1641 end
1642
1643 local function onDependencyTypeComboBoxItemSelected()
1644 lastDependencyType = dependencyTypeComboBox.selectedItem
1645 publicationNameInput.hidden = lastDependencyType ~= 3
1646 pathInput.hidden = lastDependencyType ~= 1
1647 urlInput.hidden = lastDependencyType == 3
1648 pathType.hidden = categoryComboBox.selectedItem == 3 or pathInput.hidden
1649
1650 updatePlaceholder()
1651 end
1652
1653 dependencyTypeComboBox.onItemSelected = function()
1654 onDependencyTypeComboBoxItemSelected()
1655 workspace:draw()
1656 end
1657
1658 pathType.switch.onStateChanged = updatePlaceholder
1659
1660 publicationNameInput.onInputFinished()
1661 onDependencyTypeComboBoxItemSelected()
1662 pathType.switch.onStateChanged()
1663 workspace:draw()
1664 end
1665
1666 local publishButton = layout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0x696969, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.save))
1667
1668 local function checkFields()
1669 publishButton.disabled = not (#nameInput.text > 0 and #mainUrlInput.text > 0 and #descriptionInput.text > 0 and (iconUrlInput.hidden and true or #iconUrlInput.text > 0) and (mainPathInput.hidden and true or #mainPathInput.text > 0))
1670 end
1671
1672 iconUrlInput.onInputFinished, nameInput.onInputFinished, mainUrlInput.onInputFinished, mainPathInput.onInputFinished, descriptionInput.onInputFinished = checkFields, checkFields, checkFields, checkFields, checkFields
1673
1674 categoryComboBox.onItemSelected = function()
1675 iconHint.hidden = categoryComboBox.selectedItem > 1
1676 iconUrlInput.hidden = iconHint.hidden
1677
1678 pathHint.hidden = not iconHint.hidden
1679 mainPathInput.hidden = pathHint.hidden
1680
1681 nameInput.onInputFinished()
1682 workspace:draw()
1683 end
1684
1685 categoryComboBox.onItemSelected()
1686
1687 publishButton.onTouch = function()
1688 local dependencies = {}
1689 for i = 1, dependenciesLayout.comboBox:count() do
1690 table.insert(dependencies, dependenciesLayout.comboBox:getItem(i).dependency)
1691 end
1692
1693 if categoryComboBox.selectedItem == 1 then
1694 table.insert(dependencies, {
1695 source_url = iconUrlInput.text,
1696 path = "Icon.pic"
1697 })
1698 end
1699
1700 local success, reason = RawAPIRequest(initialPublication and "update" or "upload", {
1701 -- Вот эта хня чисто для апдейта
1702 file_id = initialPublication and initialPublication.file_id or nil,
1703 -- А вот эта хня универсальная
1704 token = user.token,
1705 name = nameInput.text,
1706 source_url = mainUrlInput.text,
1707 path = categoryComboBox.selectedItem == 1 and "Main.lua" or mainPathInput.text,
1708 description = descriptionInput.text,
1709 license_id = licenseComboBox.selectedItem,
1710 dependencies = dependencies,
1711 category_id = categoryComboBox.selectedItem,
1712 whats_new = #whatsNewInput.text > 0 and whatsNewInput.text or nil
1713 })
1714
1715 if success then
1716 leftList.selectedItem = categoryComboBox.selectedItem + 1
1717
1718 if initialPublication then
1719 newPublicationInfo(initialPublication.file_id)
1720 else
1721 config.orderBy = 4
1722 saveConfig()
1723 updateFileList(categoryComboBox.selectedItem)
1724 end
1725 else
1726 GUI.alert(reason)
1727 end
1728 end
1729end
1730
1731--------------------------------------------------------------------------------
1732
1733updateFileList = function(category_id, updates)
1734 lastMethod, lastArguments = updateFileList, {category_id, updates}
1735
1736 local file_ids
1737 if updates then
1738 file_ids = {}
1739
1740 local function iterate(t)
1741 for id in pairs(t) do
1742 table.insert(file_ids, id)
1743 end
1744 end
1745
1746 iterate(userVersions)
1747 iterate(systemVersions)
1748 end
1749
1750 local result = fieldAPIRequest("result", "publications", {
1751 category_id = category_id,
1752 order_by = updates and "date" or orderBys[config.orderBy],
1753 order_direction = updates and "desc" or orderDirections[config.orderDirection],
1754 offset = currentPage * appsPerPage,
1755 count = updates and 100 or appsPerPage + 1,
1756 search = search,
1757 file_ids = file_ids,
1758 })
1759
1760 if result then
1761 contentContainer:removeChildren()
1762
1763 if updates then
1764 local i = 1
1765 while i <= #result do
1766 if getUpdateState(result[i].file_id, result[i].version) ~= 3 then
1767 table.remove(result, i)
1768 else
1769 i = i + 1
1770 end
1771 end
1772 end
1773
1774 local y = 2
1775 local layout = contentContainer:addChild(GUI.layout(1, y, contentContainer.width, 1, 1, 1))
1776 layout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
1777 layout:setSpacing(1, 1, 2)
1778
1779 if not updates or updates and #result > 0 then
1780 if updates then
1781 if #result > 0 then
1782 layout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0x696969, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.updateAll)).onTouch = function()
1783 local container = GUI.addBackgroundContainer(workspace, true, true, "")
1784
1785 local progressBar = container.layout:addChild(GUI.progressBar(1, 1, 40, 0x66DB80, 0x0, 0xE1E1E1, 0, true, true, "", "%"))
1786
1787 for i = 1, #result do
1788 container.label.text = localization.downloading .. " " .. result[i].publication_name
1789 progressBar.value = number.round(i / #result * 100)
1790 workspace:draw()
1791
1792 local publication = fieldAPIRequest("result", "publication", {
1793 file_id = result[i].file_id,
1794 language_id = config.language_id,
1795 })
1796
1797 local versionsTable = getVersionsTable(publication.file_id)
1798 versionsTable[publication.file_id].version = publication.version
1799 tryToDownload(publication.source_url, versionsTable[publication.file_id].path)
1800
1801 if publication then
1802 if publication.dependencies then
1803 for j = 1, #publication.all_dependencies do
1804 local dependency = publication.dependencies_data[publication.all_dependencies[j]]
1805 if not dependency.publication_name then
1806 container.label.text = localization.downloading .. " " .. dependency.path
1807 workspace:draw()
1808
1809 if getUpdateState(publication.all_dependencies[j], dependency.version) < 4 then
1810 local dependencyPath = getDependencyPath(versionsTable[publication.file_id].path, dependency)
1811
1812 versionsTable[publication.all_dependencies[j]] = {
1813 path = dependencyPath,
1814 version = dependency.version,
1815 }
1816
1817 tryToDownload(dependency.source_url, dependencyPath)
1818 else
1819 event.sleep(0.05)
1820 end
1821 end
1822 end
1823 end
1824 end
1825 end
1826
1827 container:remove()
1828 saveVersions()
1829 computer.shutdown(true)
1830 end
1831 end
1832 else
1833 local input = layout:addChild(GUI.input(1, 1, 20, layout.height, 0xFFFFFF, 0x2D2D2D, 0xA5A5A5, 0xFFFFFF, 0x2D2D2D, search or "", localization.search, true))
1834 input.onInputFinished = function()
1835 if #input.text == 0 then
1836 search = nil
1837 else
1838 search = input.text
1839 end
1840
1841 currentPage = 0
1842 updateFileList(category_id, updates)
1843 end
1844
1845 local orderByComboBox = layout:addChild(GUI.comboBox(1, 1, 20, layout.height, 0xFFFFFF, 0x696969, 0x969696, 0xE1E1E1))
1846 orderByComboBox:addItem(localization.byPopularity)
1847 orderByComboBox:addItem(localization.byRating)
1848 orderByComboBox:addItem(localization.byName)
1849 orderByComboBox:addItem(localization.byDate)
1850
1851 orderByComboBox.selectedItem = config.orderBy
1852
1853 local orderDirectionComboBox = layout:addChild(GUI.comboBox(1, 1, 18, layout.height, 0xFFFFFF, 0x696969, 0x969696, 0xE1E1E1))
1854 orderDirectionComboBox:addItem(localization.desc)
1855 orderDirectionComboBox:addItem(localization.asc)
1856 orderDirectionComboBox.selectedItem = config.orderDirection
1857
1858 orderByComboBox.onItemSelected = function()
1859 config.orderBy = orderByComboBox.selectedItem
1860 config.orderDirection = orderDirectionComboBox.selectedItem
1861 updateFileList(category_id, updates)
1862 saveConfig()
1863 end
1864 orderDirectionComboBox.onItemSelected = orderByComboBox.onItemSelected
1865
1866 if user.token then
1867 layout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0x696969, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.publish)).onTouch = function()
1868 editPublication(nil, category_id)
1869 end
1870 end
1871 end
1872
1873 y = y + layout.height + 1
1874
1875 local navigationLayout = contentContainer:addChild(GUI.layout(1, contentContainer.height - 1, contentContainer.width, 1, 1, 1))
1876 navigationLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
1877 navigationLayout:setSpacing(1, 1, 2)
1878
1879 local function switchPage(forward)
1880 currentPage = currentPage + (forward and 1 or -1)
1881 updateFileList(category_id, updates)
1882 end
1883
1884 local backButton = navigationLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xFFFFFF, 0x696969, 0xA5A5A5, 0xFFFFFF, "<"))
1885 backButton.colors.disabled.background = 0xE1E1E1
1886 backButton.colors.disabled.text = 0xC3C3C3
1887 backButton.disabled = currentPage == 0
1888 backButton.onTouch = function()
1889 switchPage(false)
1890 end
1891
1892 navigationLayout:addChild(GUI.text(1, 1, 0x696969, localization.page .. " " .. (currentPage + 1)))
1893 local nextButton = navigationLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xFFFFFF, 0x696969, 0xA5A5A5, 0xFFFFFF, ">"))
1894 nextButton.colors.disabled = backButton.colors.disabled
1895 nextButton.disabled = #result <= appsPerPage
1896 nextButton.onTouch = function()
1897 switchPage(true)
1898 end
1899
1900 local xStart = math.floor(1 + contentContainer.width / 2 - (appsPerWidth * (appWidth + appHSpacing) - appHSpacing) / 2)
1901 local x, counter = xStart, 1
1902 for i = 1, #result do
1903 contentContainer:addChild(newApplicationPreview(x, y, result[i]))
1904
1905 if counter >= appsPerPage then
1906 break
1907 elseif counter % appsPerWidth == 0 then
1908 x, y = xStart, y + appHeight + appVSpacing
1909 else
1910 x = x + appWidth + appHSpacing
1911 end
1912 counter = counter + 1
1913
1914 workspace:draw()
1915 end
1916 else
1917 showLabelAsContent(contentContainer, localization.noUpdates)
1918 end
1919 end
1920end
1921
1922local function loadCategory(...)
1923 currentPage, search = 0, nil
1924 updateFileList(...)
1925end
1926
1927--------------------------------------------------------------------------------
1928
1929leftList:addItem(localization.categoryOverview).onTouch = overview
1930
1931for i = 1, #categories do
1932 leftList:addItem(categories[i]).onTouch = function()
1933 loadCategory(i)
1934 end
1935end
1936
1937leftList:addItem(localization.categoryUpdates).onTouch = function()
1938 loadCategory(nil, true)
1939end
1940
1941messagesItem = leftList:addItem(localization.messages)
1942messagesItem.onTouch = dialogs
1943
1944leftList:addItem(localization.settings).onTouch = settings
1945
1946window.onResize = function(width, height)
1947 leftList.height = height - leftListPanel.height
1948
1949 window.backgroundPanel.localX = leftList.width + 1
1950 window.backgroundPanel.width = width - leftList.width
1951 window.backgroundPanel.height = height
1952
1953 contentContainer.localX = window.backgroundPanel.localX
1954 contentContainer.width = window.backgroundPanel.width
1955 contentContainer.height = window.backgroundPanel.height
1956
1957 progressIndicator.localY = height - progressIndicator.height
1958 sponsoredLabel.localY = height
1959end
1960
1961window.onResizeFinished = function()
1962 appsPerWidth = math.floor((contentContainer.width + appHSpacing) / (appWidth + appHSpacing))
1963 appsPerHeight = math.floor((contentContainer.height - 6 + appVSpacing) / (appHeight + appVSpacing))
1964 appsPerPage = appsPerWidth * appsPerHeight
1965 currentPage = 0
1966
1967 contentContainer:removeChildren()
1968 callLastMethod()
1969end
1970
1971--------------------------------------------------------------------------------
1972
1973loadConfig()
1974
1975lastMethod = loadCategory
1976
1977if select(1, ...) == "updates" then
1978 lastArguments = {nil, true}
1979 leftList.selectedItem = #categories + 2
1980else
1981 lastArguments = {1}
1982 leftList.selectedItem = 2
1983end
1984
1985window:resize(window.width, window.height)