[add] PixabayApi
This commit is contained in:
@@ -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";
|
||||
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
89
src/haxe/ru/m/pixabay/PixabayApi.hx
Normal file
89
src/haxe/ru/m/pixabay/PixabayApi.hx
Normal 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]);
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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...
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user