From ed3549d27a060eb826b7bf472f287f58b7af1132 Mon Sep 17 00:00:00 2001 From: shmyga Date: Thu, 12 Mar 2020 16:14:02 +0300 Subject: [PATCH] [add] PixabayApi --- src/haxe/ru/m/data/DataStorage.hx | 2 +- src/haxe/ru/m/data/IDataSource.hx | 8 +- src/haxe/ru/m/pixabay/PixabayApi.hx | 89 +++++++++++++++++++ src/haxe/ru/m/puzzlez/source/AssetSource.hx | 10 ++- src/haxe/ru/m/puzzlez/source/FileSource.hx | 14 ++- src/haxe/ru/m/puzzlez/source/IImageSource.hx | 4 +- src/haxe/ru/m/puzzlez/source/PixabaySource.hx | 80 ++--------------- src/haxe/ru/m/puzzlez/storage/ImageStorage.hx | 4 +- src/haxe/ru/m/puzzlez/view/ImageListFrame.hx | 48 +++++++--- .../ru/m/puzzlez/view/ImageListFrame.yaml | 12 +++ src/haxe/ru/m/puzzlez/view/ImageListSource.hx | 6 +- src/haxe/ru/m/puzzlez/view/StartFrame.hx | 9 +- 12 files changed, 179 insertions(+), 107 deletions(-) create mode 100644 src/haxe/ru/m/pixabay/PixabayApi.hx diff --git a/src/haxe/ru/m/data/DataStorage.hx b/src/haxe/ru/m/data/DataStorage.hx index 82573e7..e26bf67 100644 --- a/src/haxe/ru/m/data/DataStorage.hx +++ b/src/haxe/ru/m/data/DataStorage.hx @@ -51,7 +51,7 @@ typedef DataMeta = Filter; typedef IdValue = {id:I, meta:DataMeta}; -class DataStorage implements IDataSource { +class DataStorage implements IDataManager implements IDataIndex { inline private static var DATA_KEY = "item"; diff --git a/src/haxe/ru/m/data/IDataSource.hx b/src/haxe/ru/m/data/IDataSource.hx index 14fd55b..86d4f46 100644 --- a/src/haxe/ru/m/data/IDataSource.hx +++ b/src/haxe/ru/m/data/IDataSource.hx @@ -19,10 +19,16 @@ typedef DataPage = { var data:Array; } -interface IDataSource { +interface IDataIndex { public function getIndexPage(page:Page):Promise>; +} + +interface IDataSource { public function getPage(page:Page):Promise>; public function get(id:I):Promise; +} + +interface IDataManager extends IDataSource { public function save(item:D):Promise; public function delete(id:I):Promise; } diff --git a/src/haxe/ru/m/pixabay/PixabayApi.hx b/src/haxe/ru/m/pixabay/PixabayApi.hx new file mode 100644 index 0000000..c88a8f6 --- /dev/null +++ b/src/haxe/ru/m/pixabay/PixabayApi.hx @@ -0,0 +1,89 @@ +package ru.m.pixabay; + +import haxework.net.JsonLoader; +import promhx.Promise; +import ru.m.data.IDataSource; + +typedef PixabayImage = { + var id:Int; + var largeImageURL:String; + var webformatURL:String; + var previewURL:String; +} + +typedef PixabayResponse = { + var total:Int; + var totalHits:Int; + var hits:Array; +} + +enum abstract PixabayCategory(String) from String to String { + var FASHION = "fashion"; + var NATURE = "nature"; + var BACKGROUNDS = "backgrounds"; + var SCIENCE = "science"; + var EDUCATION = "education"; + var PEOPLE = "people"; + var FEELINGS = "feelings"; + var RELIGION = "religion"; + var HEALTH = "health"; + var PLACES = "places"; + var ANIMALS = "animals"; + var INDUSTRY = "industry"; + var FOOD = "food"; + var COMPUTER = "computer"; + var SPORTS = "sports"; + var TRANSPORTATION = "transportation"; + var TRAVEL = "travel"; + var BUILDINGS = "buildings"; + var BUSINESS = "business"; + var MUSIC = "music"; +} + +enum abstract PixabayImageType(String) from String to String { + var ALL = "all"; + var PHOTO = "photo"; + var ILLUSTRATION = "illustration"; + var VECTOR = "vector"; +} + +class PixabayApi implements IDataSource { + private var baseUrl:String = "https://pixabay.com/api/"; + private var key:String; + + public function new(key:String) { + this.key = key; + } + + private function buildRequest(queryMap:Map):String { + queryMap.set("key", key); + var query = [for (k in queryMap.keys()) '${k}=${queryMap.get(k)}'].join("&"); + return '${baseUrl}?${query}'; + } + + public function getPage(page:Page):Promise> { + var category = PixabayCategory.NATURE; + if (page.filter != null && page.filter.exists("type")) { + category = page.filter.get("type"); + } + return new JsonLoader() + .GET(buildRequest([ + "category" => category, + "image_type" => PixabayImageType.PHOTO, + "editors_choice" => true, + "per_page" => page.count, + "page" => page.index + 1, + ])) + .then((response:PixabayResponse) -> ({ + page: page, + total: response.totalHits, + data: response.hits, + })); + } + + public function get(id:Int):Promise { + return new JsonLoader() + .GET(buildRequest(["id" => id])) + .then((response:PixabayResponse) -> response.hits[0]); + } +} diff --git a/src/haxe/ru/m/puzzlez/source/AssetSource.hx b/src/haxe/ru/m/puzzlez/source/AssetSource.hx index 004f07c..d9baedf 100644 --- a/src/haxe/ru/m/puzzlez/source/AssetSource.hx +++ b/src/haxe/ru/m/puzzlez/source/AssetSource.hx @@ -1,12 +1,13 @@ package ru.m.puzzlez.source; +import ru.m.data.IDataSource; import flash.display.BitmapData; import openfl.Assets; import openfl.utils.AssetType; import promhx.Promise; import ru.m.puzzlez.core.Id; -class AssetSource implements IImageSource { +class AssetSource implements IImageSource { public static var ID:SourceId = "asset"; public var id(default, never):SourceId = ID; @@ -20,11 +21,12 @@ class AssetSource implements IImageSource { return [for (name in Assets.list(AssetType.IMAGE).filter((name:String) -> name.substr(0, 15) == "resources/image")) new ImageId(id, name)]; } - public function getList(?type:Dynamic):Promise> { - if (data == null) { + public function getIndexPage(page:Page):Promise> { + throw "Implement Me"; + /*if (data == null) { data = Promise.promise(resolveData()); } - return data; + return data;*/ } public function loadImage(id:ImageId, preview:Bool = false):Promise { diff --git a/src/haxe/ru/m/puzzlez/source/FileSource.hx b/src/haxe/ru/m/puzzlez/source/FileSource.hx index b4ec0f5..c1193a6 100644 --- a/src/haxe/ru/m/puzzlez/source/FileSource.hx +++ b/src/haxe/ru/m/puzzlez/source/FileSource.hx @@ -6,9 +6,10 @@ import haxe.crypto.Md5; import haxe.DynamicAccess; import haxe.io.Bytes; import promhx.Promise; +import ru.m.data.IDataSource; import ru.m.puzzlez.core.Id; -class FileSource implements IImageSource { +class FileSource implements IImageSource { public static var ID:SourceId = "file"; public var id(default, never):SourceId = ID; @@ -19,8 +20,9 @@ class FileSource implements IImageSource { listData = SharedObject.getLocal('${id}_list'); } - public function getList(?type:Dynamic):Promise> { - return Promise.promise([for (key in Reflect.fields(listData.data)) ImageId.fromString(key)]); + public function getIndexPage(page:Page):Promise> { + throw "Implement Me"; + //return Promise.promise([for (key in Reflect.fields(listData.data)) ImageId.fromString(key)]); } public function loadImage(id:ImageId, preview:Bool = false):Promise { @@ -57,10 +59,6 @@ class FileSource implements IImageSource { } public function clean():Void { - getList().then(function(ids) { - for (id in ids) { - remove(id); - } - }); + } } diff --git a/src/haxe/ru/m/puzzlez/source/IImageSource.hx b/src/haxe/ru/m/puzzlez/source/IImageSource.hx index a87fe9e..13d3527 100644 --- a/src/haxe/ru/m/puzzlez/source/IImageSource.hx +++ b/src/haxe/ru/m/puzzlez/source/IImageSource.hx @@ -2,10 +2,10 @@ package ru.m.puzzlez.source; import flash.display.BitmapData; import promhx.Promise; +import ru.m.data.IDataSource; import ru.m.puzzlez.core.Id; -interface IImageSource { +interface IImageSource extends IDataIndex { public var id(default, never):SourceId; - public function getList(?type:T):Promise>; public function loadImage(id:ImageId, preview:Bool = false):Promise; } diff --git a/src/haxe/ru/m/puzzlez/source/PixabaySource.hx b/src/haxe/ru/m/puzzlez/source/PixabaySource.hx index 55a9cc2..7ffa94d 100644 --- a/src/haxe/ru/m/puzzlez/source/PixabaySource.hx +++ b/src/haxe/ru/m/puzzlez/source/PixabaySource.hx @@ -1,92 +1,30 @@ package ru.m.puzzlez.source; -import haxe.io.Bytes; import flash.display.BitmapData; import flash.utils.ByteArray; +import haxe.io.Bytes; import haxework.net.BytesLoader; -import haxework.net.JsonLoader; import promhx.Promise; +import ru.m.data.IDataSource; +import ru.m.pixabay.PixabayApi; import ru.m.puzzlez.core.Id; import ru.m.puzzlez.storage.CacheStorage; -typedef PixabayImage = { - var id:Int; - var largeImageURL:String; - var webformatURL:String; - var previewURL:String; -} - -typedef PixabayResponse = { - var hits:Array; -} - -enum abstract PixabayCategory(String) from String to String { - var FASHION = "fashion"; - var NATURE = "nature"; - var BACKGROUNDS = "backgrounds"; - var SCIENCE = "science"; - var EDUCATION = "education"; - var PEOPLE = "people"; - var FEELINGS = "feelings"; - var RELIGION = "religion"; - var HEALTH = "health"; - var PLACES = "places"; - var ANIMALS = "animals"; - var INDUSTRY = "industry"; - var FOOD = "food"; - var COMPUTER = "computer"; - var SPORTS = "sports"; - var TRANSPORTATION = "transportation"; - var TRAVEL = "travel"; - var BUILDINGS = "buildings"; - var BUSINESS = "business"; - var MUSIC = "music"; -} - -enum abstract PixabayImageType(String) from String to String { - var ALL = "all"; - var PHOTO = "photo"; - var ILLUSTRATION = "illustration"; - var VECTOR = "vector"; -} - -class PixabaySource implements IImageSource { +class PixabaySource implements IImageSource { public static var ID:SourceId = "pixabay"; public var id(default, never):SourceId = ID; - private var baseUrl:String = "https://pixabay.com/api/"; - private var key:String = "14915210-5eae157281211e0ad28bc8def"; - - private var cache:Map>; + private var api:PixabayApi; @:provide static var imageCache:CacheStorage; public function new() { - cache = new Map(); + api = new PixabayApi("14915210-5eae157281211e0ad28bc8def"); } - public function getList(?type:PixabayCategory):Promise> { - return new JsonLoader() - .GET('${baseUrl}?key=${key}&category=${type}&image_type=${PixabayImageType.PHOTO}&editors_choice=true') - .then((response:PixabayResponse) -> { - var result = []; - for (item in response.hits) { - var imageId = new ImageId(id, Std.string(item.id)); - cache.set(imageId, Promise.promise(item)); - result.push(imageId); - } - return [for (item in response.hits) new ImageId(id, Std.string(item.id))]; - }); - } - - private function getImage(id:ImageId):Promise { - if (!cache.exists(id)) { - cache.set(id, new JsonLoader() - .GET('${baseUrl}?key=${key}&id=${id.id}') - .then((response:PixabayResponse) -> response.hits[0])); - } - return cache.get(id); + public function getIndexPage(page:Page):Promise> { + return api.getPage(page).then(data -> ({page: data.page, total: data.total, data: data.data.map(item -> new ImageId(id, Std.string(item.id)))})); } public function loadImage(id:ImageId, preview:Bool = false):Promise { @@ -94,7 +32,7 @@ class PixabaySource implements IImageSource { if (imageCache.exists(key)) { return ImageUtil.bytesToImage(imageCache.read(key)); } else { - return getImage(id) + return api.get(Std.parseInt(id.id)) .pipe((data:PixabayImage) -> new BytesLoader().GET(preview ? data.webformatURL : data.largeImageURL)) .pipe((data:ByteArray) -> { var bytes = Bytes.ofData(data); diff --git a/src/haxe/ru/m/puzzlez/storage/ImageStorage.hx b/src/haxe/ru/m/puzzlez/storage/ImageStorage.hx index 17afc46..f018320 100644 --- a/src/haxe/ru/m/puzzlez/storage/ImageStorage.hx +++ b/src/haxe/ru/m/puzzlez/storage/ImageStorage.hx @@ -12,7 +12,7 @@ import ru.m.puzzlez.source.PixabaySource; @:provide class ImageStorage { - public var sources(default, null):Map>; + public var sources(default, null):Map; private var cache:Map>; @@ -24,7 +24,7 @@ import ru.m.puzzlez.source.PixabaySource; register(new PixabaySource()); } - public function register(source:IImageSource):Void { + public function register(source:IImageSource):Void { sources.set(source.id, source); } diff --git a/src/haxe/ru/m/puzzlez/view/ImageListFrame.hx b/src/haxe/ru/m/puzzlez/view/ImageListFrame.hx index 747257d..d8a4fbd 100644 --- a/src/haxe/ru/m/puzzlez/view/ImageListFrame.hx +++ b/src/haxe/ru/m/puzzlez/view/ImageListFrame.hx @@ -1,10 +1,12 @@ package ru.m.puzzlez.view; +import haxework.view.form.ToggleButtonView; import haxework.view.data.DataView; import haxework.view.form.ButtonView; import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameView; import haxework.view.popup.ConfirmView; +import ru.m.data.IDataSource; import ru.m.puzzlez.core.Id; import ru.m.puzzlez.FileUtil; import ru.m.puzzlez.source.FileSource; @@ -12,42 +14,61 @@ import ru.m.puzzlez.storage.GameStorage; import ru.m.puzzlez.storage.ImageStorage; import ru.m.puzzlez.view.PuzzleImageView; -@:template class ImageListFrame extends FrameView> { +@:template class ImageListFrame extends FrameView { public static var ID = "image_list"; - @:view var images:ActionDataView; + @:view("images") var imagesView:ActionDataView; + @:view("pages") var pagesView:DataView; @:view var select:ButtonView; + @:provide var imageStorage:ImageStorage; @:provide var gameStorage:GameStorage; @:provide var switcher:FrameSwitcher; - private var source:ImageListSource; + private var source:ImageListSource; private var loading:LoadingWrapper; + private var page:Page; + private var data(default, set):DataPage; + + private function set_data(value:DataPage):DataPage { + data = value; + imagesView.data = data.data; + pagesView.data = [for (i in 0...Std.int((data.total - 1) / data.page.count) + 1) i]; + return data; + } + public function new() { super(ID); - loading = new LoadingWrapper(images); + page = {index: 0, count: 6}; + loading = new LoadingWrapper(imagesView); } - override public function onShow(data:ImageListSource):Void { + private function pageFactory(index:Int, value:Int):ToggleButtonView { + var result = new ToggleButtonView(); + result.text = '${value}'; + result.on = data.page.index == value; + return result; + } + + override public function onShow(data:ImageListSource):Void { if (data != null) { source = data; select.visible = source.source.id == FileSource.ID; - images.data = []; - loading.promise = data.source.getList(data.type).then(result -> images.data = result); + page.filter = ["type" => data.type]; + refresh(); } } override public function onHide():Void { - //images.data = []; + //imagesView.data = []; } private function selectFile():Void { FileUtil.browse().then((data:FileContent) -> { var fileSource:FileSource = cast source.source; var imageId = fileSource.append(data.content); - images.data.push(imageId); - images.data = images.data; + refresh(); }); } @@ -74,7 +95,12 @@ import ru.m.puzzlez.view.PuzzleImageView; } private function refresh():Void { - loading.promise = source.source.getList().then(result -> images.data = result); + data = { + page: page, + total: 0, + data: [], + }; + loading.promise = source.source.getIndexPage(page).then(data -> this.data = data); } private function start(imageId:ImageId):Void { diff --git a/src/haxe/ru/m/puzzlez/view/ImageListFrame.yaml b/src/haxe/ru/m/puzzlez/view/ImageListFrame.yaml index 3c86891..c46c81f 100644 --- a/src/haxe/ru/m/puzzlez/view/ImageListFrame.yaml +++ b/src/haxe/ru/m/puzzlez/view/ImageListFrame.yaml @@ -13,6 +13,18 @@ views: +onDataAction: ~onAction geometry.margin: 5 overflow.y: scroll + - id: pages + $type: haxework.view.data.DataView + geometry.width: 100% + layout: + $type: haxework.view.layout.TailLayout + margin: 5 + factory: ~pageFactory + +onDataSelect: | + ~(index) -> { + page.index = index; + refresh(); + } - id: select $type: haxework.view.form.ButtonView text: Select... diff --git a/src/haxe/ru/m/puzzlez/view/ImageListSource.hx b/src/haxe/ru/m/puzzlez/view/ImageListSource.hx index 5f8cee2..59f8042 100644 --- a/src/haxe/ru/m/puzzlez/view/ImageListSource.hx +++ b/src/haxe/ru/m/puzzlez/view/ImageListSource.hx @@ -2,7 +2,7 @@ package ru.m.puzzlez.view; import ru.m.puzzlez.source.IImageSource; -typedef ImageListSource = { - var source:IImageSource; - @:optional var type:T; +typedef ImageListSource = { + var source:IImageSource; + @:optional var type:Dynamic; } diff --git a/src/haxe/ru/m/puzzlez/view/StartFrame.hx b/src/haxe/ru/m/puzzlez/view/StartFrame.hx index b17ec4a..d0675ff 100644 --- a/src/haxe/ru/m/puzzlez/view/StartFrame.hx +++ b/src/haxe/ru/m/puzzlez/view/StartFrame.hx @@ -6,6 +6,7 @@ import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameView; import haxework.view.popup.ConfirmView; import ru.m.data.IDataSource; +import ru.m.pixabay.PixabayApi; import ru.m.puzzlez.core.GameState.GameStatus; import ru.m.puzzlez.source.AssetSource; import ru.m.puzzlez.source.FileSource; @@ -17,7 +18,7 @@ import ru.m.update.Updater; @:template class StartFrame extends FrameView { public static var ID = "start"; - @:view var sources:DataView, ButtonView>; + @:view var sources:DataView; @:view("load") var loadButton:ButtonView; @:view("complete") var completeButton:ButtonView; @:view("update") var updateButton:ButtonView; @@ -29,7 +30,7 @@ import ru.m.update.Updater; public function new() { super(ID); - var data:Array> = []; + var data:Array = []; data.push({source: storage.sources.get(AssetSource.ID), type: "asset"}); data.push({source: storage.sources.get(FileSource.ID), type: "file"}); for (type in AbstractEnumTools.getValues(PixabayCategory)) { @@ -63,13 +64,13 @@ import ru.m.update.Updater; }); } - private function sourceViewFactory(index:Int, source:ImageListSource):ButtonView { + private function sourceViewFactory(index:Int, source:ImageListSource):ButtonView { var result = new ButtonView(); result.text = Std.string(source.type != null ? source.type : "custom"); return result; } - private function load(source:ImageListSource):Void { + private function load(source:ImageListSource):Void { switcher.change(ImageListFrame.ID, source); }