[add] FileStorage
This commit is contained in:
@@ -34,6 +34,12 @@ class DefaultConverter<D> extends Converter<D, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EmptyConverter<T> extends Converter<T, T> {
|
||||||
|
public function new() {
|
||||||
|
super(item -> item, data -> data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MetaBuilder<D> extends DefaultConverter<DataMeta> {
|
class MetaBuilder<D> extends DefaultConverter<DataMeta> {
|
||||||
private var builder:D -> DataMeta;
|
private var builder:D -> DataMeta;
|
||||||
|
|
||||||
@@ -51,7 +57,7 @@ typedef DataMeta = Filter;
|
|||||||
|
|
||||||
typedef IdValue<I> = {id:I, meta:DataMeta};
|
typedef IdValue<I> = {id:I, meta:DataMeta};
|
||||||
|
|
||||||
class DataStorage<I, D:{id:I}> implements IDataManager<I, D> implements IDataIndex<I> {
|
class DataStorage<I, D> implements IDataManager<I, D> implements IDataIndex<I> {
|
||||||
|
|
||||||
inline private static var DATA_KEY = "item";
|
inline private static var DATA_KEY = "item";
|
||||||
|
|
||||||
@@ -89,7 +95,7 @@ class DataStorage<I, D:{id:I}> implements IDataManager<I, D> implements IDataInd
|
|||||||
private function checkFilter(filter:Null<Filter>, meta:DataMeta):Bool {
|
private function checkFilter(filter:Null<Filter>, meta:DataMeta):Bool {
|
||||||
if (filter != null) {
|
if (filter != null) {
|
||||||
for (k => v in filter) {
|
for (k => v in filter) {
|
||||||
if (meta.get(k) != v) {
|
if (meta.exists(k) && meta.get(k) != v) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,11 +169,12 @@ class DataStorage<I, D:{id:I}> implements IDataManager<I, D> implements IDataInd
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function save(item:D):Promise<D> {
|
public function save(item:D):Promise<D> {
|
||||||
var stringId = serializeId(item.id);
|
var meta = metaBuilder.build(item);
|
||||||
|
var stringId = meta.get("id");
|
||||||
var itemData = SharedObject.getLocal('${name}/${stringId}');
|
var itemData = SharedObject.getLocal('${name}/${stringId}');
|
||||||
itemData.setProperty(DATA_KEY, serialize(item));
|
itemData.setProperty(DATA_KEY, serialize(item));
|
||||||
itemData.flush();
|
itemData.flush();
|
||||||
indexData.setProperty(stringId, metaBuilder.serialize(metaBuilder.build(item)));
|
indexData.setProperty(stringId, metaBuilder.serialize(meta));
|
||||||
indexData.flush();
|
indexData.flush();
|
||||||
return Promise.promise(item);
|
return Promise.promise(item);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ interface IDataIndex<I> {
|
|||||||
public function getIndexPage(page:Page):Promise<DataPage<I>>;
|
public function getIndexPage(page:Page):Promise<DataPage<I>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IDataSource<I, D:{id:I}> {
|
interface IDataSource<I, D> {
|
||||||
public function getPage(page:Page):Promise<DataPage<D>>;
|
public function getPage(page:Page):Promise<DataPage<D>>;
|
||||||
public function get(id:I):Promise<D>;
|
public function get(id:I):Promise<D>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IDataManager<I, D:{id:I}> extends IDataSource<I, D> {
|
interface IDataManager<I, D> extends IDataSource<I, D> {
|
||||||
public function save(item:D):Promise<D>;
|
public function save(item:D):Promise<D>;
|
||||||
public function delete(id:I):Promise<Bool>;
|
public function delete(id:I):Promise<Bool>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package ru.m.puzzlez.core;
|
package ru.m.puzzlez.core;
|
||||||
|
|
||||||
import ru.m.puzzlez.core.Id;
|
|
||||||
|
|
||||||
enum abstract GameStatus(String) from String to String {
|
enum abstract GameStatus(String) from String to String {
|
||||||
var READY = "ready";
|
var READY = "ready";
|
||||||
var STARTED = "started";
|
var STARTED = "started";
|
||||||
@@ -9,7 +7,6 @@ enum abstract GameStatus(String) from String to String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef GameState = {
|
typedef GameState = {
|
||||||
var id:ImageId;
|
|
||||||
var status:GameStatus;
|
var status:GameStatus;
|
||||||
var preset:GamePreset;
|
var preset:GamePreset;
|
||||||
var parts:Array<Part>;
|
var parts:Array<Part>;
|
||||||
|
|||||||
@@ -129,7 +129,6 @@ class GameUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
id: preset.imageId,
|
|
||||||
status: READY,
|
status: READY,
|
||||||
preset: preset,
|
preset: preset,
|
||||||
parts: parts,
|
parts: parts,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class AssetSource implements IImageSource {
|
|||||||
|
|
||||||
public var id(default, never):SourceId = ID;
|
public var id(default, never):SourceId = ID;
|
||||||
|
|
||||||
private var data:Promise<Array<ImageId>>;
|
private var data:Array<ImageId>;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
}
|
}
|
||||||
@@ -22,11 +22,15 @@ class AssetSource implements IImageSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getIndexPage(page:Page):Promise<DataPage<ImageId>> {
|
public function getIndexPage(page:Page):Promise<DataPage<ImageId>> {
|
||||||
throw "Implement Me";
|
if (data == null) {
|
||||||
/*if (data == null) {
|
data = resolveData();
|
||||||
data = Promise.promise(resolveData());
|
|
||||||
}
|
}
|
||||||
return data;*/
|
// ToDo: pagination
|
||||||
|
return Promise.promise({
|
||||||
|
page: page,
|
||||||
|
total: data.length,
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadImage(id:ImageId, preview:Bool = false):Promise<BitmapData> {
|
public function loadImage(id:ImageId, preview:Bool = false):Promise<BitmapData> {
|
||||||
|
|||||||
@@ -1,61 +1,37 @@
|
|||||||
package ru.m.puzzlez.source;
|
package ru.m.puzzlez.source;
|
||||||
|
|
||||||
import flash.display.BitmapData;
|
import flash.display.BitmapData;
|
||||||
import flash.net.SharedObject;
|
|
||||||
import haxe.crypto.Md5;
|
|
||||||
import haxe.DynamicAccess;
|
|
||||||
import haxe.io.Bytes;
|
import haxe.io.Bytes;
|
||||||
import promhx.Promise;
|
import promhx.Promise;
|
||||||
import ru.m.data.IDataSource;
|
import ru.m.data.IDataSource;
|
||||||
import ru.m.puzzlez.core.Id;
|
import ru.m.puzzlez.core.Id;
|
||||||
|
import ru.m.puzzlez.storage.FileStorage;
|
||||||
|
|
||||||
class FileSource implements IImageSource {
|
class FileSource implements IImageSource {
|
||||||
public static var ID:SourceId = "file";
|
public static var ID:SourceId = "file";
|
||||||
|
|
||||||
public var id(default, never):SourceId = ID;
|
public var id(default, never):SourceId = ID;
|
||||||
|
|
||||||
private var listData:SharedObject;
|
private var storage:FileStorage;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
listData = SharedObject.getLocal('${id}_list');
|
storage = new FileStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getIndexPage(page:Page):Promise<DataPage<ImageId>> {
|
public function getIndexPage(page:Page):Promise<DataPage<ImageId>> {
|
||||||
throw "Implement Me";
|
return storage.getIndexPage(page).then(data -> ({page: data.page, total: data.total, data: data.data.map(item -> new ImageId(id, item))}));
|
||||||
//return Promise.promise([for (key in Reflect.fields(listData.data)) ImageId.fromString(key)]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadImage(id:ImageId, preview:Bool = false):Promise<BitmapData> {
|
public function loadImage(id:ImageId, preview:Bool = false):Promise<BitmapData> {
|
||||||
var fileData = SharedObject.getLocal(id);
|
return storage.get(id.id).pipe(ImageUtil.bytesToImage);
|
||||||
var bytes = null;
|
|
||||||
try {
|
|
||||||
bytes = Bytes.ofHex(Reflect.field(fileData.data, "image"));
|
|
||||||
} catch (error:Dynamic) {
|
|
||||||
var result = new Promise<BitmapData>();
|
|
||||||
result.reject(error);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return ImageUtil.bytesToImage(bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function append(data:Bytes):ImageId {
|
public function append(data:Bytes):Promise<Dynamic> {
|
||||||
var name = Md5.make(data).toHex();
|
return storage.save(data);
|
||||||
var imageId = new ImageId(id, name);
|
|
||||||
var fileData = SharedObject.getLocal(imageId);
|
|
||||||
fileData.setProperty("image", data.toHex());
|
|
||||||
fileData.flush();
|
|
||||||
listData.setProperty(imageId, true);
|
|
||||||
listData.flush();
|
|
||||||
return imageId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function remove(id:ImageId):Void {
|
public function remove(id:ImageId):Promise<Dynamic> {
|
||||||
var fileData = SharedObject.getLocal(id);
|
return storage.delete(id.id);
|
||||||
fileData.clear();
|
|
||||||
fileData.flush();
|
|
||||||
var access:DynamicAccess<String> = listData.data;
|
|
||||||
access.remove(id);
|
|
||||||
listData.flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clean():Void {
|
public function clean():Void {
|
||||||
|
|||||||
19
src/haxe/ru/m/puzzlez/storage/FileStorage.hx
Normal file
19
src/haxe/ru/m/puzzlez/storage/FileStorage.hx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package ru.m.puzzlez.storage;
|
||||||
|
|
||||||
|
import haxe.crypto.Md5;
|
||||||
|
import haxe.io.Bytes;
|
||||||
|
import ru.m.data.DataStorage;
|
||||||
|
|
||||||
|
class FileStorage extends DataStorage<String, Bytes> {
|
||||||
|
inline private static var NAME = "file";
|
||||||
|
inline private static var VERSION = 1;
|
||||||
|
|
||||||
|
public function new() {
|
||||||
|
super(
|
||||||
|
'${NAME}/${VERSION}',
|
||||||
|
new MetaBuilder<Bytes>(item -> ["id" => Md5.make(item).toHex(), "date" => Date.now().toString()]),
|
||||||
|
new EmptyConverter<String>(),
|
||||||
|
new Converter<Bytes, String>(item -> item.toHex(), data -> Bytes.ofHex(data))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import ru.m.data.DataStorage;
|
|||||||
public function new() {
|
public function new() {
|
||||||
super(
|
super(
|
||||||
'${NAME}/${VERSION}',
|
'${NAME}/${VERSION}',
|
||||||
new MetaBuilder<GameState>(item -> ["status" => item.status, "date" => Date.now().toString()]),
|
new MetaBuilder<GameState>(item -> ["id" => item.preset.imageId, "status" => item.status, "date" => Date.now().toString()]),
|
||||||
new Converter<ImageId, String>(id -> id.toString(), data -> ImageId.fromString(data))
|
new Converter<ImageId, String>(id -> id.toString(), data -> ImageId.fromString(data))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import ru.m.puzzlez.view.PuzzleImageView;
|
|||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
super(ID);
|
super(ID);
|
||||||
page = {index: 0, count: 6};
|
page = {index: 0, count: 6, order: [{key: "date", reverse: true}]};
|
||||||
loading = new LoadingWrapper(imagesView);
|
loading = new LoadingWrapper(imagesView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,8 +67,7 @@ import ru.m.puzzlez.view.PuzzleImageView;
|
|||||||
private function selectFile():Void {
|
private function selectFile():Void {
|
||||||
FileUtil.browse().then((data:FileContent) -> {
|
FileUtil.browse().then((data:FileContent) -> {
|
||||||
var fileSource:FileSource = cast source.source;
|
var fileSource:FileSource = cast source.source;
|
||||||
var imageId = fileSource.append(data.content);
|
fileSource.append(data.content).then(_ -> refresh());
|
||||||
refresh();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,16 +78,14 @@ import ru.m.puzzlez.view.PuzzleImageView;
|
|||||||
if (fileSource != null) {
|
if (fileSource != null) {
|
||||||
ConfirmView.confirm("Delete image?").then(result -> {
|
ConfirmView.confirm("Delete image?").then(result -> {
|
||||||
if (result) {
|
if (result) {
|
||||||
fileSource.remove(imageId);
|
fileSource.remove(imageId).then(_ -> refresh());
|
||||||
refresh();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
case CLEAN:
|
case CLEAN:
|
||||||
ConfirmView.confirm("Delete state?").then(result -> {
|
ConfirmView.confirm("Delete state?").then(result -> {
|
||||||
if (result) {
|
if (result) {
|
||||||
gameStorage.delete(imageId);
|
gameStorage.delete(imageId).then(_ -> refresh());
|
||||||
refresh();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user