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