diff --git a/hxformat.json b/hxformat.json index 7bf0a43..d1dca53 100644 --- a/hxformat.json +++ b/hxformat.json @@ -1,5 +1,8 @@ { "indentation": { "character": " " + }, + "wrapping": { + "maxLineLength": 120 } } diff --git a/package.json b/package.json index 99b4416..e77daf8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "puzzlez", - "version": "0.5.1", + "version": "0.6.0", "private": true, "devDependencies": { "dateformat": "^3.0.3", diff --git a/puzzlez.code-workspace b/puzzlez.code-workspace index f5ff926..d62e6aa 100644 --- a/puzzlez.code-workspace +++ b/puzzlez.code-workspace @@ -1,46 +1,53 @@ { "folders": [ { - "path": "." - } + "path": ".", + }, ], "settings": { "haxe.executable": { "path": "/home/shmyga/sdk/haxe/4.2.5/haxe", "env": { - "HAXE_STD_PATH": "/home/shmyga/sdk/haxe/4.2.5/std" - } + "HAXE_STD_PATH": "/home/shmyga/sdk/haxe/4.2.5/std", + }, }, "haxe.configurations": [["build/app/flash/haxe/debug.hxml"]], "haxe.displayServer": { "arguments": [ //"-v" - ] - } + ], + }, }, "extensions": { - "recommendations": ["nadako.vshaxe"] + "recommendations": ["nadako.vshaxe"], }, "launch": { "version": "0.2.0", "configurations": [ { - "args": ["app:flash:test"], - "name": "app:flash:test", + "args": ["${input:target}"], + "name": "${input:target}", "program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js", "request": "launch", "skipFiles": ["/**"], - "type": "node" + "type": "node", + "consoleTitle": "${input:target}", }, - { - "args": ["server:cpp:test"], - "name": "server:cpp:test", - "program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js", - "request": "launch", - "skipFiles": ["/**"], - "type": "node" - } ], - "compounds": [] - } + "inputs": [ + { + "id": "target", + "description": "Please enter target name", + "options": [ + "app:flash:test", + "app:html5:test", + "app:linux:test", + "server:cpp:test", + ], + "type": "pickString", + "default": "app:flash:test", + }, + ], + "compounds": [], + }, } diff --git a/src/app/haxe/ru/m/puzzlez/PuzzlezApp.hx b/src/app/haxe/ru/m/puzzlez/PuzzlezApp.hx index 6abfba3..6fc3670 100644 --- a/src/app/haxe/ru/m/puzzlez/PuzzlezApp.hx +++ b/src/app/haxe/ru/m/puzzlez/PuzzlezApp.hx @@ -8,6 +8,7 @@ import ru.m.puzzlez.render.part.IPartBuilder; import ru.m.puzzlez.settings.Settings; import ru.m.puzzlez.source.AssetImageSource; import ru.m.puzzlez.source.FileImageSource; +import ru.m.puzzlez.source.NginxImageSource; import ru.m.puzzlez.source.PixabayImageSource; import ru.m.puzzlez.source.UnsplashImageSource; import ru.m.puzzlez.storage.GameStorage; @@ -25,8 +26,9 @@ class PuzzlezApp { GameStorage; sourceBundle.register(new AssetImageSource()); sourceBundle.register(new FileImageSource()); - sourceBundle.register(new PixabayImageSource()); - sourceBundle.register(new UnsplashImageSource()); + sourceBundle.register(new PixabayImageSource(CompilationOption.get("PIXABAY_KEY"))); + sourceBundle.register(new UnsplashImageSource(CompilationOption.get("UNSPLASH_KEY"))); + sourceBundle.register(new NginxImageSource("https://home.shmyga.ru/puzzlez/")); L.push(new TraceLogger()); updater = new Updater(Const.instance.VERSION, "https://shmyga.ru/repo/puzzlez/packages.json"); var app = new App(); diff --git a/src/app/haxe/ru/m/puzzlez/PuzzlezTheme.hx b/src/app/haxe/ru/m/puzzlez/PuzzlezTheme.hx index eeea168..6c5c53a 100644 --- a/src/app/haxe/ru/m/puzzlez/PuzzlezTheme.hx +++ b/src/app/haxe/ru/m/puzzlez/PuzzlezTheme.hx @@ -24,6 +24,10 @@ class PuzzlezTheme extends Theme { public function new() { super({embed: true}, {light: "gray"}, {base: "4h"}); register(new Style("frame", ["geometry.padding" => Box.fromFloat(8),])); + register(new Style("button.source", [ + "geometry.width" => SizeValue.fromString("30%"), + "geometry.height" => SizeValue.fromString("40%"), + ], "button")); register(new Style("view", [ "skin.background.color" => colors.light, "skin.border.color" => colors.border, diff --git a/src/app/haxe/ru/m/puzzlez/core/ImageSource.hx b/src/app/haxe/ru/m/puzzlez/core/ImageSource.hx index 32356f4..7c4b70e 100644 --- a/src/app/haxe/ru/m/puzzlez/core/ImageSource.hx +++ b/src/app/haxe/ru/m/puzzlez/core/ImageSource.hx @@ -7,4 +7,5 @@ import ru.m.puzzlez.proto.game.ImageId; interface ImageSource extends DataSource { public var id(default, never):String; public function load(id:String, thumb:Bool = false):Promise; + public function getCategories():Null>>; } diff --git a/src/app/haxe/ru/m/puzzlez/render/PartView.hx b/src/app/haxe/ru/m/puzzlez/render/PartView.hx index 800f2f7..858806c 100644 --- a/src/app/haxe/ru/m/puzzlez/render/PartView.hx +++ b/src/app/haxe/ru/m/puzzlez/render/PartView.hx @@ -101,8 +101,8 @@ class SpritePartView extends PartView { if (playerId != null) { var rect = cast(part.rect, RectangleExt).clone(); - rect.x += (image.width - size.x) / 2; - rect.y += (image.height - size.y) / 2; + rect.x = (image.width - size.x) / 2; + rect.y = (image.height - size.y) / 2; var path = builder.build(rect, part.bounds); graphics.lineStyle(4, 0xffff00, 0.3); path.draw(graphics); diff --git a/src/app/haxe/ru/m/puzzlez/source/AssetImageSource.hx b/src/app/haxe/ru/m/puzzlez/source/AssetImageSource.hx index 599a4ce..4b741f6 100644 --- a/src/app/haxe/ru/m/puzzlez/source/AssetImageSource.hx +++ b/src/app/haxe/ru/m/puzzlez/source/AssetImageSource.hx @@ -13,6 +13,7 @@ class AssetImageSource implements ImageSource { private var _data:Array; private var data(get, null):Array; + private var key:String; private function get_data():Array { if (_data == null) { @@ -21,11 +22,14 @@ class AssetImageSource implements ImageSource { return _data; } - public function new() {} + public function new(key:String = "resources/image") { + this.key = key; + } private function resolveData():Array { + var keyLength = this.key.length; return [ - for (name in Assets.list(AssetType.IMAGE).filter((name:String) -> name.substr(0, 15) == "resources/image")) + for (name in Assets.list(AssetType.IMAGE).filter((name:String) -> name.substr(0, keyLength) == this.key)) new ImageId().setSource(id).setId(name) ]; } @@ -41,4 +45,8 @@ class AssetImageSource implements ImageSource { public function load(id:String, thumb:Bool = false):Promise { return Promise.promise(ImageValue.BITMAP(Assets.getBitmapData(id))); } + + public function getCategories():Null>> { + return null; + } } diff --git a/src/app/haxe/ru/m/puzzlez/source/FileImageSource.hx b/src/app/haxe/ru/m/puzzlez/source/FileImageSource.hx index 7b640a4..9224d11 100644 --- a/src/app/haxe/ru/m/puzzlez/source/FileImageSource.hx +++ b/src/app/haxe/ru/m/puzzlez/source/FileImageSource.hx @@ -27,4 +27,8 @@ class FileImageSource implements ImageSource { public function load(id:String, thumb:Bool = false):Promise { return storage.get(id).then(bytes -> ImageValue.BYTES(bytes)); } + + public function getCategories():Null>> { + return null; + } } diff --git a/src/app/haxe/ru/m/puzzlez/source/NginxImageSource.hx b/src/app/haxe/ru/m/puzzlez/source/NginxImageSource.hx new file mode 100644 index 0000000..6ce53fc --- /dev/null +++ b/src/app/haxe/ru/m/puzzlez/source/NginxImageSource.hx @@ -0,0 +1,78 @@ +package ru.m.puzzlez.source; + +import hw.net.JsonLoader; +import promhx.Promise; +import ru.m.data.DataSource; +import ru.m.puzzlez.core.ImageSource; +import ru.m.puzzlez.core.ImageValue; +import ru.m.puzzlez.proto.game.ImageId; + +enum abstract NginxResponseItemType(String) from String to String { + var FILE = "file"; + var DIRECTORY = "directory"; +} + +typedef NginxResponseItem = { + var name:String; + var type:NginxResponseItemType; + var mtime:String; + var size:Int; +} + +typedef NginxResponse = Array; + +class NginxImageSource implements ImageSource { + public var id(default, never):String = "nginx"; + + private var baseUrl:String; + + public function new(baseUrl:String) { + this.baseUrl = baseUrl; + } + + private function buildUrl(name:String):String { + return baseUrl + name; + } + + public function getPage(page:Page):Promise> { + var category = page.filter.get("category"); + return new JsonLoader().GET(category != null ? this.baseUrl + category + "/" : this.baseUrl) + .then((response:NginxResponse) -> { + var data:Array = []; + for (item in response) { + if (item.type == NginxResponseItemType.FILE) { + var itemId = category != null ? category + "@" + item.name : item.name; + data.push(new ImageId().setSource(id).setId(itemId)); + } + } + return { + page: page, + data: data, + total: response.length, + } + }); + } + + public function load(id:String, thumb:Bool = false):Promise { + var url = this.baseUrl + StringTools.replace(id, "@", "/"); + if (thumb) { + var parts = url.split("."); + var index = parts.length - 2; + parts[index] = parts[index] + "-thumbnail"; + url = parts.join("."); + } + return Promise.promise(ImageValue.URL(url)); + } + + public function getCategories():Null>> { + return new JsonLoader().GET(this.baseUrl).then((response:NginxResponse) -> { + var data:Array = []; + for (item in response) { + if (item.type == NginxResponseItemType.DIRECTORY) { + data.push(item.name); + } + } + return data; + }); + } +} diff --git a/src/app/haxe/ru/m/puzzlez/source/PixabayImageSource.hx b/src/app/haxe/ru/m/puzzlez/source/PixabayImageSource.hx index d200fe4..12b693c 100644 --- a/src/app/haxe/ru/m/puzzlez/source/PixabayImageSource.hx +++ b/src/app/haxe/ru/m/puzzlez/source/PixabayImageSource.hx @@ -14,24 +14,24 @@ class PixabayImageSource implements ImageSource { private static var imageUrlsCache:Map = new Map(); - public function new() { - var key:String = CompilationOption.get("PIXABAY_KEY"); + public function new(key:String) { api = new PixabayApi(key); } public function getPage(page:Page):Promise> { - return this.api.getPage(page.index + 1, page.count, page.filter.get("category")).then((response:PixabayResponse) -> { - var data:Array = []; - for (hit in response.hits) { - imageUrlsCache.set('${hit.id}', hit.largeImageURL); - data.push(new ImageId().setSource(id).setId(Std.string(hit.id))); - } - return { - page: page, - data: data, - total: response.totalHits, - } - }); + return this.api.getPage(page.index + 1, page.count, page.filter.get("category")) + .then((response:PixabayResponse) -> { + var data:Array = []; + for (hit in response.hits) { + imageUrlsCache.set('${hit.id}', hit.largeImageURL); + data.push(new ImageId().setSource(id).setId(Std.string(hit.id))); + } + return { + page: page, + data: data, + total: response.totalHits, + } + }); } public function load(id:String, thumb:Bool = false):Promise { @@ -50,4 +50,8 @@ class PixabayImageSource implements ImageSource { }); } } + + public function getCategories():Null>> { + return Promise.promise(AbstractEnumTools.getValues(PixabayCategory)); + } } diff --git a/src/app/haxe/ru/m/puzzlez/source/UnsplashImageSource.hx b/src/app/haxe/ru/m/puzzlez/source/UnsplashImageSource.hx index 3b48203..98f46ca 100644 --- a/src/app/haxe/ru/m/puzzlez/source/UnsplashImageSource.hx +++ b/src/app/haxe/ru/m/puzzlez/source/UnsplashImageSource.hx @@ -51,8 +51,7 @@ class UnsplashImageSource implements ImageSource { private static var resolver:ImageResolver; - public function new() { - var key:String = CompilationOption.get("UNSPLASH_KEY"); + public function new(key:String) { api = new UnsplashApi(key); resolver = new ImageResolver((imageId, thumb) -> { return api.get(imageId).then(data -> thumb ? data.urls.small : data.urls.regular); @@ -60,22 +59,28 @@ class UnsplashImageSource implements ImageSource { } public function getPage(page:Page):Promise> { - return this.api.getPage(page.index + 1, page.count, page.filter.get("category")).then((response:UnsplashResponse) -> { - var data:Array = []; - for (image in response.results) { - resolver.setValue(image.id, image.urls.small, true); - resolver.setValue(image.id, image.urls.regular); - data.push(new ImageId().setSource(id).setId(image.id)); - } - return { - page: page, - data: data, - total: response.total, - } - }); + return this.api.getPage(page.index + 1, page.count, page.filter.get("category")) + .then((response:UnsplashResponse) -> { + var data:Array = []; + for (image in response.results) { + resolver.setValue(image.id, image.urls.small, true); + resolver.setValue(image.id, image.urls.regular); + data.push(new ImageId().setSource(id).setId(image.id)); + } + return { + page: page, + data: data, + total: response.total, + } + }); } public function load(id:String, thumb:Bool = false):Promise { return resolver.resolve(id, thumb); } + + public function getCategories():Null>> { + // TODO: unsplash categories list + return Promise.promise([]); + } } diff --git a/src/app/haxe/ru/m/puzzlez/view/GameFrame.hx b/src/app/haxe/ru/m/puzzlez/view/GameFrame.hx index cc5df69..8bebf6f 100644 --- a/src/app/haxe/ru/m/puzzlez/view/GameFrame.hx +++ b/src/app/haxe/ru/m/puzzlez/view/GameFrame.hx @@ -34,6 +34,7 @@ import ru.m.puzzlez.view.popup.PreviewPopup; } override public function onShow(state:GameState):Void { + L.d("Frame", '$ID: ${state.preset.image.source}:${state.preset.image.id}'); onHide(); if (state.online) { // game = new NetworkGame(state); diff --git a/src/app/haxe/ru/m/puzzlez/view/GameListFrame.hx b/src/app/haxe/ru/m/puzzlez/view/GameListFrame.hx index c149abe..1f31591 100644 --- a/src/app/haxe/ru/m/puzzlez/view/GameListFrame.hx +++ b/src/app/haxe/ru/m/puzzlez/view/GameListFrame.hx @@ -27,6 +27,7 @@ typedef GameListConfig = { } override public function onShow(data:GameListConfig):Void { + L.d("Frame", '$ID: $data'); games.reset(); if (data != null) { header.text = data.title; diff --git a/src/app/haxe/ru/m/puzzlez/view/ImageListFrame.hx b/src/app/haxe/ru/m/puzzlez/view/ImageListFrame.hx index a2f0593..ff18146 100644 --- a/src/app/haxe/ru/m/puzzlez/view/ImageListFrame.hx +++ b/src/app/haxe/ru/m/puzzlez/view/ImageListFrame.hx @@ -23,11 +23,18 @@ typedef ImageListConfig = { @:provide var switcher:FrameSwitcher; @:provide var sourceBundle:ImageSourceBundle; + private var config:ImageListConfig; + public function new() { super(ID); } override public function onShow(data:ImageListConfig):Void { + if (data == null) { + data = config; + } + L.d("Frame", '$ID: $data'); + config = data; images.reset(); if (data != null) { header.text = data.title; diff --git a/src/app/haxe/ru/m/puzzlez/view/PresetFrame.hx b/src/app/haxe/ru/m/puzzlez/view/PresetFrame.hx index 1715993..941fa9d 100644 --- a/src/app/haxe/ru/m/puzzlez/view/PresetFrame.hx +++ b/src/app/haxe/ru/m/puzzlez/view/PresetFrame.hx @@ -48,6 +48,7 @@ import ru.m.puzzlez.view.common.PresetView; override public function onShow(data:ImageId):Void { super.onShow(data); + L.d("Frame", '$ID: ${data.source}:${data.id}'); imageId = data; selectSize(sizesView.data[sizesView.data.length - 1]); } diff --git a/src/app/haxe/ru/m/puzzlez/view/StartFrame.hx b/src/app/haxe/ru/m/puzzlez/view/StartFrame.hx index 5346ca9..855c0b3 100644 --- a/src/app/haxe/ru/m/puzzlez/view/StartFrame.hx +++ b/src/app/haxe/ru/m/puzzlez/view/StartFrame.hx @@ -4,8 +4,8 @@ import hw.view.data.DataView; import hw.view.form.ButtonView; import hw.view.frame.FrameSwitcher; import hw.view.frame.FrameView; -import ru.m.api.PixabayApi; import ru.m.puzzlez.FileUtil; +import ru.m.puzzlez.image.ImageSourceBundle; import ru.m.puzzlez.proto.game.GameStatus; import ru.m.puzzlez.storage.FileStorage; import ru.m.puzzlez.storage.GameStorage; @@ -27,24 +27,41 @@ import ru.m.update.Updater; @:provide var fileStorage:FileStorage; @:provide var gameStorage:GameStorage; + @:provide var sourceBundle:ImageSourceBundle; private var fileSource:ImageListConfig = {title: "Files", sourceId: "file"}; - private var startedGames:GameListConfig = {title: "Started", source: gameStorage, filter: ["status" => GameStatus.STARTED]}; - private var completedGames:GameListConfig = {title: "Completed", source: gameStorage, filter: ["status" => GameStatus.COMPLETE]}; + private var startedGames:GameListConfig = { + title: "Started", + source: gameStorage, + filter: ["status" => GameStatus.STARTED] + }; + private var completedGames:GameListConfig = { + title: "Completed", + source: gameStorage, + filter: ["status" => GameStatus.COMPLETE] + }; public function new() { super(ID); var data:Array = []; - // data.push({title: "Assets", sourceId: "asset"}); - data.push(fileSource); - for (type in AbstractEnumTools.getValues(PixabayCategory)) { - data.push({title: type, sourceId: "unsplash", filter: ["category" => type]}); - } + sourceBundle.get(fileSource.sourceId).getPage({index: 0, count: 0}).then((page) -> { + if (page.total > 0) { + data.unshift(fileSource); + sources.data = data; + } + }); sources.data = data; + sourceBundle.get("nginx").getCategories().then((categories) -> { + for (category in categories) { + data.push({title: category, sourceId: "nginx", filter: ["category" => category]}); + } + sources.data = data; + }); } private function sourceViewFactory(index:Int, source:ImageListConfig):ButtonView { var result = new ButtonView(); + result.style = "button.source"; result.text = source.title; return result; } @@ -62,6 +79,7 @@ import ru.m.update.Updater; } override public function onShow(data:Dynamic):Void { + L.d("Frame", '$ID'); appUpdater.check().then((info:Null) -> { if (info != null) { updateButton.visible = true; @@ -74,9 +92,11 @@ import ru.m.update.Updater; private function refresh():Void { gameStorage.getIndexPage({index: 0, count: 0, filter: startedGames.filter}).then(response -> { startedButton.text = response.total > 0 ? 'Started (${response.total})' : 'Started'; + startedButton.disabled = response.total == 0; }); gameStorage.getIndexPage({index: 0, count: 0, filter: completedGames.filter}).then(response -> { completedButton.text = response.total > 0 ? 'Completed (${response.total})' : 'Completed'; + completedButton.disabled = response.total == 0; }); } } diff --git a/src/app/haxe/ru/m/storage/SharedObjectDataStorage.hx b/src/app/haxe/ru/m/storage/SharedObjectDataStorage.hx index af28af6..dd9b784 100644 --- a/src/app/haxe/ru/m/storage/SharedObjectDataStorage.hx +++ b/src/app/haxe/ru/m/storage/SharedObjectDataStorage.hx @@ -1,10 +1,10 @@ package ru.m.storage; -import haxe.Unserializer; import flash.net.SharedObject; +import haxe.Serializer; +import haxe.Unserializer; import haxe.crypto.Md5; import haxe.io.Bytes; -import haxe.Serializer; import promhx.Promise; import ru.m.data.DataSource; @@ -124,7 +124,8 @@ class SharedObjectDataStorage implements DataStorage { } } applyOrder(page.order, values); - var result:Array = values.slice(page.index * page.count, page.index * page.count + page.count).map(value -> value.id); + var result:Array = values.slice(page.index * page.count, page.index * page.count + page.count) + .map(value -> value.id); return Promise.promise({ page: page, total: values.length, @@ -138,7 +139,7 @@ class SharedObjectDataStorage implements DataStorage { return { page: indexPage.page, total: indexPage.total, - data: data, + data: data.filter((item:D) -> item != null), } }); });