[add] PixabayApi

This commit is contained in:
2020-03-12 16:14:02 +03:00
parent b3a4adce68
commit ed3549d27a
12 changed files with 179 additions and 107 deletions

View File

@@ -51,7 +51,7 @@ typedef DataMeta = Filter;
typedef IdValue<I> = {id:I, meta:DataMeta};
class DataStorage<I, D:{id:I}> implements IDataSource<I, D> {
class DataStorage<I, D:{id:I}> implements IDataManager<I, D> implements IDataIndex<I> {
inline private static var DATA_KEY = "item";

View File

@@ -19,10 +19,16 @@ typedef DataPage<T> = {
var data:Array<T>;
}
interface IDataSource<I, D:{id:I}> {
interface IDataIndex<I> {
public function getIndexPage(page:Page):Promise<DataPage<I>>;
}
interface IDataSource<I, D:{id:I}> {
public function getPage(page:Page):Promise<DataPage<D>>;
public function get(id:I):Promise<D>;
}
interface IDataManager<I, D:{id:I}> extends IDataSource<I, D> {
public function save(item:D):Promise<D>;
public function delete(id:I):Promise<Bool>;
}

View File

@@ -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<PixabayImage>;
}
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<Int, PixabayImage> {
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, Dynamic>):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<DataPage<PixabayImage>> {
var category = PixabayCategory.NATURE;
if (page.filter != null && page.filter.exists("type")) {
category = page.filter.get("type");
}
return new JsonLoader<PixabayResponse>()
.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<PixabayImage> {
return new JsonLoader<PixabayResponse>()
.GET(buildRequest(["id" => id]))
.then((response:PixabayResponse) -> response.hits[0]);
}
}

View File

@@ -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<Dynamic> {
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<Dynamic> {
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<Array<ImageId>> {
if (data == null) {
public function getIndexPage(page:Page):Promise<DataPage<ImageId>> {
throw "Implement Me";
/*if (data == null) {
data = Promise.promise(resolveData());
}
return data;
return data;*/
}
public function loadImage(id:ImageId, preview:Bool = false):Promise<BitmapData> {

View File

@@ -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<Dynamic> {
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<Dynamic> {
listData = SharedObject.getLocal('${id}_list');
}
public function getList(?type:Dynamic):Promise<Array<ImageId>> {
return Promise.promise([for (key in Reflect.fields(listData.data)) ImageId.fromString(key)]);
public function getIndexPage(page:Page):Promise<DataPage<ImageId>> {
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<BitmapData> {
@@ -57,10 +59,6 @@ class FileSource implements IImageSource<Dynamic> {
}
public function clean():Void {
getList().then(function(ids) {
for (id in ids) {
remove(id);
}
});
}
}

View File

@@ -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<T> {
interface IImageSource extends IDataIndex<ImageId> {
public var id(default, never):SourceId;
public function getList(?type:T):Promise<Array<ImageId>>;
public function loadImage(id:ImageId, preview:Bool = false):Promise<BitmapData>;
}

View File

@@ -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<PixabayImage>;
}
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<PixabayCategory> {
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<String, Promise<PixabayImage>>;
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<Array<ImageId>> {
return new JsonLoader<PixabayResponse>()
.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<PixabayImage> {
if (!cache.exists(id)) {
cache.set(id, new JsonLoader<PixabayResponse>()
.GET('${baseUrl}?key=${key}&id=${id.id}')
.then((response:PixabayResponse) -> response.hits[0]));
}
return cache.get(id);
public function getIndexPage(page:Page):Promise<DataPage<ImageId>> {
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<BitmapData> {
@@ -94,7 +32,7 @@ class PixabaySource implements IImageSource<PixabayCategory> {
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);

View File

@@ -12,7 +12,7 @@ import ru.m.puzzlez.source.PixabaySource;
@:provide class ImageStorage {
public var sources(default, null):Map<SourceId, IImageSource<Dynamic>>;
public var sources(default, null):Map<SourceId, IImageSource>;
private var cache:Map<String, Promise<BitmapData>>;
@@ -24,7 +24,7 @@ import ru.m.puzzlez.source.PixabaySource;
register(new PixabaySource());
}
public function register(source:IImageSource<Dynamic>):Void {
public function register(source:IImageSource):Void {
sources.set(source.id, source);
}

View File

@@ -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<ImageListSource<Dynamic>> {
@:template class ImageListFrame extends FrameView<ImageListSource> {
public static var ID = "image_list";
@:view var images:ActionDataView<ImageId, PuzzleImageView, Action>;
@:view("images") var imagesView:ActionDataView<ImageId, PuzzleImageView, Action>;
@:view("pages") var pagesView:DataView<Int, ToggleButtonView>;
@:view var select:ButtonView;
@:provide var imageStorage:ImageStorage;
@:provide var gameStorage:GameStorage;
@:provide var switcher:FrameSwitcher;
private var source:ImageListSource<Dynamic>;
private var source:ImageListSource;
private var loading:LoadingWrapper;
private var page:Page;
private var data(default, set):DataPage<ImageId>;
private function set_data(value:DataPage<ImageId>):DataPage<ImageId> {
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<Dynamic>):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 {

View File

@@ -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...

View File

@@ -2,7 +2,7 @@ package ru.m.puzzlez.view;
import ru.m.puzzlez.source.IImageSource;
typedef ImageListSource<T> = {
var source:IImageSource<T>;
@:optional var type:T;
typedef ImageListSource = {
var source:IImageSource;
@:optional var type:Dynamic;
}

View File

@@ -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<Dynamic> {
public static var ID = "start";
@:view var sources:DataView<ImageListSource<Dynamic>, ButtonView>;
@:view var sources:DataView<ImageListSource, ButtonView>;
@: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<ImageListSource<Dynamic>> = [];
var data:Array<ImageListSource> = [];
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<Dynamic>):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<Dynamic>):Void {
private function load(source:ImageListSource):Void {
switcher.change(ImageListFrame.ID, source);
}