[add] FileStorage

This commit is contained in:
2020-03-12 17:25:41 +03:00
parent ed3549d27a
commit b60e49c0b2
9 changed files with 55 additions and 56 deletions

View File

@@ -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);
} }

View File

@@ -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>;
} }

View File

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

View File

@@ -129,7 +129,6 @@ class GameUtil {
} }
} }
return { return {
id: preset.imageId,
status: READY, status: READY,
preset: preset, preset: preset,
parts: parts, parts: parts,

View File

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

View File

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

View 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))
);
}
}

View File

@@ -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))
); );
} }

View File

@@ -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();
} }
}); });
} }