2 Commits

Author SHA1 Message Date
981ae3b89d build(docker): add docker builder 2026-04-05 22:55:08 +03:00
5a7d23f7e3 feat(app): add nginx inage source 2026-03-10 23:13:58 +03:00
24 changed files with 379 additions and 168 deletions

6
.env.default Normal file
View File

@@ -0,0 +1,6 @@
KEY_STORE=<keystore.jks>
KEY_PASS=<passphrase>
SDK_PATH=$HOME/sdk
PUBLISH_PATH=$HOME/public/puzzlez
BUILD_PATH=./build
TARGET_PATH=./target

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@ config.json
/log /log
/ansible/*.retry /ansible/*.retry
.npmrc .npmrc
.env

28
Dockerfile Normal file
View File

@@ -0,0 +1,28 @@
FROM ubuntu:noble AS base
RUN apt update && apt install -y \
nodejs \
npm \
openjdk-11-jre \
protobuf-compiler \
jq
RUN node -v
RUN npm -v
FROM base AS builder
ENV GRADLE_USER_HOME=/sdk/gradle
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY gulpfile.js protohx.json ./
COPY tasks ./tasks
COPY config.example.json ./config.example.json
RUN --mount=type=secret,id=KEY_STORE \
cat /run/secrets/KEY_STORE > ./key.jks
RUN --mount=type=secret,id=KEY_PASS,env=KEY_PASS \
jq --arg key_pass "$KEY_PASS" \
'.SdkDir = "/sdk" | .PublishDir = "/app/publish" | .Key.store = "key.jks" | .Key.pass = $key_pass' \
config.example.json > config.json
COPY src ./src
COPY dependencies ./dependencies
VOLUME ["/sdk", "/app/build", "/app/target", "/app/publish", "/app/src-gen"]
CMD ["npx", "gulp", "default"]

22
docker-compose.yaml Normal file
View File

@@ -0,0 +1,22 @@
name: puzzlez
services:
builder:
container_name: puzzlez-builder
image: infernal-games/puzzlez-builder
build:
context: .
secrets:
- KEY_STORE
- KEY_PASS
volumes:
- ${SDK_PATH}:/sdk
- ${BUILD_PATH}:/app/build
- ${TARGET_PATH}:/app/target
- ${PUBLISH_PATH}:/app/publish
- ./src-gen:/app/src-gen
secrets:
KEY_STORE:
file: $KEY_STORE
KEY_PASS:
environment: KEY_PASS

View File

@@ -1,139 +1,139 @@
const gulp = require('gulp'); const gulp = require("gulp");
const gulpClean = require('gulp-clean'); const gulpClean = require("gulp-clean");
const Config = require('./config.json'); const Config = require("./config.json");
const packageInfo = require('./package.json'); const packageInfo = require("./package.json");
const {System, Sdk, Haxe, Project} = require('gulp-haxetool'); const { System, Sdk, Haxe, Project } = require("gulp-haxetool");
const dateformat = require('dateformat'); const dateformat = require("dateformat");
const argv = require('yargs').argv; const publish = require("./tasks/gulp-publish");
const publish = require('./tasks/gulp-publish');
if (packageInfo.haxe) { if (packageInfo.haxe) {
Haxe.VERSION = packageInfo.haxe; Haxe.VERSION = packageInfo.haxe;
} }
if (Config.SdkDir) { if (Config.SdkDir) {
Sdk.dir = Config.SdkDir; Sdk.dir = Config.SdkDir;
} }
if (Config.BuildDir) { if (Config.BuildDir) {
Haxe.buildDir = Config.BuildDir; Haxe.buildDir = Config.BuildDir;
} }
exports.clean = function clean() { exports.clean = function clean() {
return gulp.src('target/*', {read: false}).pipe(gulpClean()); return gulp.src("target/*", { read: false }).pipe(gulpClean());
}; };
exports.generate = function generate() { exports.setup = function setup() {
return new Haxe().haxelib(['run', 'protohx', 'generate', 'protohx.json']).then(({stdout}) => { const builder = Project.Builder.new(
if (stdout.indexOf('FAIL') > -1) { app.config,
throw stdout; Project.Platform.ANDROID,
app.buildSystem,
);
return builder.prepare();
};
exports.generate = function generate() {
return exports.setup().then(() =>
new Haxe()
.haxelib(["run", "protohx", "generate", "protohx.json"])
.then(({ stdout }) => {
if (stdout.indexOf("FAIL") > -1) {
throw stdout;
} }
}); }),
);
}; };
const config = new Project.Config({ const config = new Project.Config({
meta: { meta: {
title: 'Puzzle\'z', title: "Puzzle'z",
filename: 'puzzlez', filename: "puzzlez",
icon: 'src/app/resources/icon.png', icon: "src/app/resources/icon.png",
pack: 'ru.m.puzzlez', pack: "ru.m.puzzlez",
author: 'shmyga <shmyga.z@gmail.com>', author: "shmyga <shmyga.z@gmail.com>",
company: 'MegaLoMania', company: "MegaLoMania",
version: packageInfo.version + (Config.Develop ? '-SNAPSHOT' : ''), version: packageInfo.version + (Config.Develop ? "-SNAPSHOT" : ""),
}, },
key: Config.Key, key: Config.Key,
libs: packageInfo.haxeDependencies, libs: packageInfo.haxeDependencies,
macros: [ macros: [
`CompilationOption.set('build','${dateformat(new Date(), 'yyyy-mm-dd HH:MM:ss')}')`, `CompilationOption.set('build','${dateformat(new Date(), "yyyy-mm-dd HH:MM:ss")}')`,
`CompilationOption.set('UNSPLASH_KEY','${Config.UnsplashKey}')`, `CompilationOption.set('UNSPLASH_KEY','${Config.UnsplashKey}')`,
`CompilationOption.set('PIXABAY_KEY','${Config.PixabayKey}')`, `CompilationOption.set('PIXABAY_KEY','${Config.PixabayKey}')`,
], ],
flags: [ flags: ["proto_debug"],
'proto_debug',
]
}); });
const app = new Project( const app = new Project(
Project.BuildSystem.OPENFL, Project.BuildSystem.OPENFL,
[ [
Project.Platform.FLASH, Project.Platform.FLASH,
Project.Platform.HTML5, Project.Platform.HTML5,
Project.Platform.LINUX, Project.Platform.LINUX,
Project.Platform.WINDOWS, Project.Platform.WINDOWS,
Project.Platform.ANDROID, Project.Platform.ANDROID,
],
config.branch({
name: "app",
sources: ["src-gen/haxe", "src/common/haxe", "src/app/haxe"],
android: [
{
path: "dependencies/android",
extensions: ["ru.m.android.FileUtil"],
},
], ],
config.branch({ assets: ["src/app/resources"],
name: 'app', main: "ru.m.puzzlez.PuzzlezApp",
sources: [ meta: {
'src-gen/haxe', width: 1280,
'src/common/haxe', height: 768,
'src/app/haxe', },
], flags: ["app"],
android: [{ }),
path: 'dependencies/android',
extensions: ['ru.m.android.FileUtil'],
}],
assets: [
'src/app/resources',
],
main: 'ru.m.puzzlez.PuzzlezApp',
meta: {
width: 1280,
height: 768,
},
flags: [
'app',
]
}),
).bind(module, gulp); ).bind(module, gulp);
const server = new Project( const server = new Project(
Project.BuildSystem.HAXE, Project.BuildSystem.HAXE,
[ [Project.Platform.NEKO, Project.Platform.CPP],
Project.Platform.NEKO, config.branch({
Project.Platform.CPP, name: "server",
], sources: ["src-gen/haxe", "src/common/haxe", "src/server/haxe"],
config.branch({ main: "ru.m.puzzlez.PuzzlezServer",
name: 'server', }),
sources: [
'src-gen/haxe',
'src/common/haxe',
'src/server/haxe',
],
main: 'ru.m.puzzlez.PuzzlezServer',
})
).bind(module, gulp); ).bind(module, gulp);
module.exports.publish = publish(packageInfo.name, packageInfo.version, Config.PublishDir, Config.PublishUrl); module.exports.publish = publish(
packageInfo.name,
packageInfo.version,
Config.PublishDir,
Config.PublishUrl,
);
const defaultSeries = [ const defaultSeries = [
exports.clean, exports.clean,
exports.generate, exports.generate,
module.exports['app:flash:build'], module.exports["app:flash:build"],
module.exports['app:flash:html'], module.exports["app:flash:html"],
module.exports['app:html5:build'], module.exports["app:html5:build"],
]; ];
if (System.isLinux) { if (System.isLinux) {
defaultSeries.push( defaultSeries.push(
module.exports['app:linux:build'], module.exports["app:linux:build"],
module.exports['app:linux:archive'], module.exports["app:linux:archive"],
module.exports['app:linux:deb'], module.exports["app:linux:deb"],
module.exports['app:android:build'], module.exports["app:android:build"],
); );
} }
if (System.isWindows) { if (System.isWindows) {
defaultSeries.push( defaultSeries.push(
module.exports['app:windows:build'], module.exports["app:windows:build"],
module.exports['app:windows:archive'], module.exports["app:windows:archive"],
module.exports['app:windows:installer'], module.exports["app:windows:installer"],
); );
} }
defaultSeries.push( defaultSeries.push(exports.publish);
exports.publish,
);
module.exports.default = gulp.series(defaultSeries); module.exports.default = gulp.series(defaultSeries);

View File

@@ -1,5 +1,8 @@
{ {
"indentation": { "indentation": {
"character": " " "character": " "
},
"wrapping": {
"maxLineLength": 120
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "puzzlez", "name": "puzzlez",
"version": "0.5.1", "version": "0.6.0",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"dateformat": "^3.0.3", "dateformat": "^3.0.3",

View File

@@ -1,46 +1,53 @@
{ {
"folders": [ "folders": [
{ {
"path": "." "path": ".",
} },
], ],
"settings": { "settings": {
"haxe.executable": { "haxe.executable": {
"path": "/home/shmyga/sdk/haxe/4.2.5/haxe", "path": "/home/shmyga/sdk/haxe/4.2.5/haxe",
"env": { "env": {
"HAXE_STD_PATH": "/home/shmyga/sdk/haxe/4.2.5/std" "HAXE_STD_PATH": "/home/shmyga/sdk/haxe/4.2.5/std",
} },
}, },
"haxe.configurations": [["build/app/flash/haxe/debug.hxml"]], "haxe.configurations": [["build/app/flash/haxe/debug.hxml"]],
"haxe.displayServer": { "haxe.displayServer": {
"arguments": [ "arguments": [
//"-v" //"-v"
] ],
} },
}, },
"extensions": { "extensions": {
"recommendations": ["nadako.vshaxe"] "recommendations": ["nadako.vshaxe"],
}, },
"launch": { "launch": {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"args": ["app:flash:test"], "args": ["${input:target}"],
"name": "app:flash:test", "name": "${input:target}",
"program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js", "program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js",
"request": "launch", "request": "launch",
"skipFiles": ["<node_internals>/**"], "skipFiles": ["<node_internals>/**"],
"type": "node" "type": "node",
"consoleTitle": "${input:target}",
}, },
{
"args": ["server:cpp:test"],
"name": "server:cpp:test",
"program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js",
"request": "launch",
"skipFiles": ["<node_internals>/**"],
"type": "node"
}
], ],
"compounds": [] "inputs": [
} {
"id": "target",
"description": "Please enter target name",
"options": [
"app:flash:test",
"app:html5:test",
"app:linux:test",
"server:cpp:test",
],
"type": "pickString",
"default": "app:flash:test",
},
],
"compounds": [],
},
} }

7
scripts/build Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -e
cd "$(dirname $(dirname "$0"))" || exit
source .env
mkdir -p "$SDK_PATH" "$PUBLISH_PATH" "$BUILD_PATH" "$TARGET_PATH"
docker compose run --rm --user $(id -u):$(id -g) --build --remove-orphans builder

View File

@@ -8,6 +8,7 @@ import ru.m.puzzlez.render.part.IPartBuilder;
import ru.m.puzzlez.settings.Settings; import ru.m.puzzlez.settings.Settings;
import ru.m.puzzlez.source.AssetImageSource; import ru.m.puzzlez.source.AssetImageSource;
import ru.m.puzzlez.source.FileImageSource; import ru.m.puzzlez.source.FileImageSource;
import ru.m.puzzlez.source.NginxImageSource;
import ru.m.puzzlez.source.PixabayImageSource; import ru.m.puzzlez.source.PixabayImageSource;
import ru.m.puzzlez.source.UnsplashImageSource; import ru.m.puzzlez.source.UnsplashImageSource;
import ru.m.puzzlez.storage.GameStorage; import ru.m.puzzlez.storage.GameStorage;
@@ -25,8 +26,9 @@ class PuzzlezApp {
GameStorage; GameStorage;
sourceBundle.register(new AssetImageSource()); sourceBundle.register(new AssetImageSource());
sourceBundle.register(new FileImageSource()); sourceBundle.register(new FileImageSource());
sourceBundle.register(new PixabayImageSource()); sourceBundle.register(new PixabayImageSource(CompilationOption.get("PIXABAY_KEY")));
sourceBundle.register(new UnsplashImageSource()); sourceBundle.register(new UnsplashImageSource(CompilationOption.get("UNSPLASH_KEY")));
sourceBundle.register(new NginxImageSource("https://home.shmyga.ru/puzzlez/"));
L.push(new TraceLogger()); L.push(new TraceLogger());
updater = new Updater(Const.instance.VERSION, "https://shmyga.ru/repo/puzzlez/packages.json"); updater = new Updater(Const.instance.VERSION, "https://shmyga.ru/repo/puzzlez/packages.json");
var app = new App(); var app = new App();

View File

@@ -24,6 +24,10 @@ class PuzzlezTheme extends Theme {
public function new() { public function new() {
super({embed: true}, {light: "gray"}, {base: "4h"}); super({embed: true}, {light: "gray"}, {base: "4h"});
register(new Style("frame", ["geometry.padding" => Box.fromFloat(8),])); register(new Style("frame", ["geometry.padding" => Box.fromFloat(8),]));
register(new Style("button.source", [
"geometry.width" => SizeValue.fromString("30%"),
"geometry.height" => SizeValue.fromString("40%"),
], "button"));
register(new Style("view", [ register(new Style("view", [
"skin.background.color" => colors.light, "skin.background.color" => colors.light,
"skin.border.color" => colors.border, "skin.border.color" => colors.border,

View File

@@ -7,4 +7,5 @@ import ru.m.puzzlez.proto.game.ImageId;
interface ImageSource extends DataSource<ImageId> { interface ImageSource extends DataSource<ImageId> {
public var id(default, never):String; public var id(default, never):String;
public function load(id:String, thumb:Bool = false):Promise<ImageValue>; public function load(id:String, thumb:Bool = false):Promise<ImageValue>;
public function getCategories():Null<Promise<Array<String>>>;
} }

View File

@@ -101,8 +101,8 @@ class SpritePartView extends PartView {
if (playerId != null) { if (playerId != null) {
var rect = cast(part.rect, RectangleExt).clone(); var rect = cast(part.rect, RectangleExt).clone();
rect.x += (image.width - size.x) / 2; rect.x = (image.width - size.x) / 2;
rect.y += (image.height - size.y) / 2; rect.y = (image.height - size.y) / 2;
var path = builder.build(rect, part.bounds); var path = builder.build(rect, part.bounds);
graphics.lineStyle(4, 0xffff00, 0.3); graphics.lineStyle(4, 0xffff00, 0.3);
path.draw(graphics); path.draw(graphics);

View File

@@ -13,6 +13,7 @@ class AssetImageSource implements ImageSource {
private var _data:Array<ImageId>; private var _data:Array<ImageId>;
private var data(get, null):Array<ImageId>; private var data(get, null):Array<ImageId>;
private var key:String;
private function get_data():Array<ImageId> { private function get_data():Array<ImageId> {
if (_data == null) { if (_data == null) {
@@ -21,11 +22,14 @@ class AssetImageSource implements ImageSource {
return _data; return _data;
} }
public function new() {} public function new(key:String = "resources/image") {
this.key = key;
}
private function resolveData():Array<ImageId> { private function resolveData():Array<ImageId> {
var keyLength = this.key.length;
return [ return [
for (name in Assets.list(AssetType.IMAGE).filter((name:String) -> name.substr(0, 15) == "resources/image")) for (name in Assets.list(AssetType.IMAGE).filter((name:String) -> name.substr(0, keyLength) == this.key))
new ImageId().setSource(id).setId(name) new ImageId().setSource(id).setId(name)
]; ];
} }
@@ -41,4 +45,8 @@ class AssetImageSource implements ImageSource {
public function load(id:String, thumb:Bool = false):Promise<ImageValue> { public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
return Promise.promise(ImageValue.BITMAP(Assets.getBitmapData(id))); return Promise.promise(ImageValue.BITMAP(Assets.getBitmapData(id)));
} }
public function getCategories():Null<Promise<Array<String>>> {
return null;
}
} }

View File

@@ -27,4 +27,8 @@ class FileImageSource implements ImageSource {
public function load(id:String, thumb:Bool = false):Promise<ImageValue> { public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
return storage.get(id).then(bytes -> ImageValue.BYTES(bytes)); return storage.get(id).then(bytes -> ImageValue.BYTES(bytes));
} }
public function getCategories():Null<Promise<Array<String>>> {
return null;
}
} }

View File

@@ -0,0 +1,78 @@
package ru.m.puzzlez.source;
import hw.net.JsonLoader;
import promhx.Promise;
import ru.m.data.DataSource;
import ru.m.puzzlez.core.ImageSource;
import ru.m.puzzlez.core.ImageValue;
import ru.m.puzzlez.proto.game.ImageId;
enum abstract NginxResponseItemType(String) from String to String {
var FILE = "file";
var DIRECTORY = "directory";
}
typedef NginxResponseItem = {
var name:String;
var type:NginxResponseItemType;
var mtime:String;
var size:Int;
}
typedef NginxResponse = Array<NginxResponseItem>;
class NginxImageSource implements ImageSource {
public var id(default, never):String = "nginx";
private var baseUrl:String;
public function new(baseUrl:String) {
this.baseUrl = baseUrl;
}
private function buildUrl(name:String):String {
return baseUrl + name;
}
public function getPage(page:Page):Promise<DataPage<ImageId>> {
var category = page.filter.get("category");
return new JsonLoader<NginxResponse>().GET(category != null ? this.baseUrl + category + "/" : this.baseUrl)
.then((response:NginxResponse) -> {
var data:Array<ImageId> = [];
for (item in response) {
if (item.type == NginxResponseItemType.FILE) {
var itemId = category != null ? category + "@" + item.name : item.name;
data.push(new ImageId().setSource(id).setId(itemId));
}
}
return {
page: page,
data: data,
total: response.length,
}
});
}
public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
var url = this.baseUrl + StringTools.replace(id, "@", "/");
if (thumb) {
var parts = url.split(".");
var index = parts.length - 2;
parts[index] = parts[index] + "-thumbnail";
url = parts.join(".");
}
return Promise.promise(ImageValue.URL(url));
}
public function getCategories():Null<Promise<Array<String>>> {
return new JsonLoader<NginxResponse>().GET(this.baseUrl).then((response:NginxResponse) -> {
var data:Array<String> = [];
for (item in response) {
if (item.type == NginxResponseItemType.DIRECTORY) {
data.push(item.name);
}
}
return data;
});
}
}

View File

@@ -14,24 +14,24 @@ class PixabayImageSource implements ImageSource {
private static var imageUrlsCache:Map<String, String> = new Map(); private static var imageUrlsCache:Map<String, String> = new Map();
public function new() { public function new(key:String) {
var key:String = CompilationOption.get("PIXABAY_KEY");
api = new PixabayApi(key); api = new PixabayApi(key);
} }
public function getPage(page:Page):Promise<DataPage<ImageId>> { public function getPage(page:Page):Promise<DataPage<ImageId>> {
return this.api.getPage(page.index + 1, page.count, page.filter.get("category")).then((response:PixabayResponse) -> { return this.api.getPage(page.index + 1, page.count, page.filter.get("category"))
var data:Array<ImageId> = []; .then((response:PixabayResponse) -> {
for (hit in response.hits) { var data:Array<ImageId> = [];
imageUrlsCache.set('${hit.id}', hit.largeImageURL); for (hit in response.hits) {
data.push(new ImageId().setSource(id).setId(Std.string(hit.id))); imageUrlsCache.set('${hit.id}', hit.largeImageURL);
} data.push(new ImageId().setSource(id).setId(Std.string(hit.id)));
return { }
page: page, return {
data: data, page: page,
total: response.totalHits, data: data,
} total: response.totalHits,
}); }
});
} }
public function load(id:String, thumb:Bool = false):Promise<ImageValue> { public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
@@ -50,4 +50,8 @@ class PixabayImageSource implements ImageSource {
}); });
} }
} }
public function getCategories():Null<Promise<Array<String>>> {
return Promise.promise(AbstractEnumTools.getValues(PixabayCategory));
}
} }

View File

@@ -51,8 +51,7 @@ class UnsplashImageSource implements ImageSource {
private static var resolver:ImageResolver; private static var resolver:ImageResolver;
public function new() { public function new(key:String) {
var key:String = CompilationOption.get("UNSPLASH_KEY");
api = new UnsplashApi(key); api = new UnsplashApi(key);
resolver = new ImageResolver((imageId, thumb) -> { resolver = new ImageResolver((imageId, thumb) -> {
return api.get(imageId).then(data -> thumb ? data.urls.small : data.urls.regular); return api.get(imageId).then(data -> thumb ? data.urls.small : data.urls.regular);
@@ -60,22 +59,28 @@ class UnsplashImageSource implements ImageSource {
} }
public function getPage(page:Page):Promise<DataPage<ImageId>> { public function getPage(page:Page):Promise<DataPage<ImageId>> {
return this.api.getPage(page.index + 1, page.count, page.filter.get("category")).then((response:UnsplashResponse) -> { return this.api.getPage(page.index + 1, page.count, page.filter.get("category"))
var data:Array<ImageId> = []; .then((response:UnsplashResponse) -> {
for (image in response.results) { var data:Array<ImageId> = [];
resolver.setValue(image.id, image.urls.small, true); for (image in response.results) {
resolver.setValue(image.id, image.urls.regular); resolver.setValue(image.id, image.urls.small, true);
data.push(new ImageId().setSource(id).setId(image.id)); resolver.setValue(image.id, image.urls.regular);
} data.push(new ImageId().setSource(id).setId(image.id));
return { }
page: page, return {
data: data, page: page,
total: response.total, data: data,
} total: response.total,
}); }
});
} }
public function load(id:String, thumb:Bool = false):Promise<ImageValue> { public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
return resolver.resolve(id, thumb); return resolver.resolve(id, thumb);
} }
public function getCategories():Null<Promise<Array<String>>> {
// TODO: unsplash categories list
return Promise.promise([]);
}
} }

View File

@@ -34,6 +34,7 @@ import ru.m.puzzlez.view.popup.PreviewPopup;
} }
override public function onShow(state:GameState):Void { override public function onShow(state:GameState):Void {
L.d("Frame", '$ID: ${state.preset.image.source}:${state.preset.image.id}');
onHide(); onHide();
if (state.online) { if (state.online) {
// game = new NetworkGame(state); // game = new NetworkGame(state);

View File

@@ -27,6 +27,7 @@ typedef GameListConfig = {
} }
override public function onShow(data:GameListConfig):Void { override public function onShow(data:GameListConfig):Void {
L.d("Frame", '$ID: $data');
games.reset(); games.reset();
if (data != null) { if (data != null) {
header.text = data.title; header.text = data.title;

View File

@@ -23,11 +23,18 @@ typedef ImageListConfig = {
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
@:provide var sourceBundle:ImageSourceBundle; @:provide var sourceBundle:ImageSourceBundle;
private var config:ImageListConfig;
public function new() { public function new() {
super(ID); super(ID);
} }
override public function onShow(data:ImageListConfig):Void { override public function onShow(data:ImageListConfig):Void {
if (data == null) {
data = config;
}
L.d("Frame", '$ID: $data');
config = data;
images.reset(); images.reset();
if (data != null) { if (data != null) {
header.text = data.title; header.text = data.title;

View File

@@ -48,6 +48,7 @@ import ru.m.puzzlez.view.common.PresetView;
override public function onShow(data:ImageId):Void { override public function onShow(data:ImageId):Void {
super.onShow(data); super.onShow(data);
L.d("Frame", '$ID: ${data.source}:${data.id}');
imageId = data; imageId = data;
selectSize(sizesView.data[sizesView.data.length - 1]); selectSize(sizesView.data[sizesView.data.length - 1]);
} }

View File

@@ -4,8 +4,8 @@ import hw.view.data.DataView;
import hw.view.form.ButtonView; import hw.view.form.ButtonView;
import hw.view.frame.FrameSwitcher; import hw.view.frame.FrameSwitcher;
import hw.view.frame.FrameView; import hw.view.frame.FrameView;
import ru.m.api.PixabayApi;
import ru.m.puzzlez.FileUtil; import ru.m.puzzlez.FileUtil;
import ru.m.puzzlez.image.ImageSourceBundle;
import ru.m.puzzlez.proto.game.GameStatus; import ru.m.puzzlez.proto.game.GameStatus;
import ru.m.puzzlez.storage.FileStorage; import ru.m.puzzlez.storage.FileStorage;
import ru.m.puzzlez.storage.GameStorage; import ru.m.puzzlez.storage.GameStorage;
@@ -27,24 +27,41 @@ import ru.m.update.Updater;
@:provide var fileStorage:FileStorage; @:provide var fileStorage:FileStorage;
@:provide var gameStorage:GameStorage; @:provide var gameStorage:GameStorage;
@:provide var sourceBundle:ImageSourceBundle;
private var fileSource:ImageListConfig = {title: "Files", sourceId: "file"}; private var fileSource:ImageListConfig = {title: "Files", sourceId: "file"};
private var startedGames:GameListConfig = {title: "Started", source: gameStorage, filter: ["status" => GameStatus.STARTED]}; private var startedGames:GameListConfig = {
private var completedGames:GameListConfig = {title: "Completed", source: gameStorage, filter: ["status" => GameStatus.COMPLETE]}; title: "Started",
source: gameStorage,
filter: ["status" => GameStatus.STARTED]
};
private var completedGames:GameListConfig = {
title: "Completed",
source: gameStorage,
filter: ["status" => GameStatus.COMPLETE]
};
public function new() { public function new() {
super(ID); super(ID);
var data:Array<ImageListConfig> = []; var data:Array<ImageListConfig> = [];
// data.push({title: "Assets", sourceId: "asset"}); sourceBundle.get(fileSource.sourceId).getPage({index: 0, count: 0}).then((page) -> {
data.push(fileSource); if (page.total > 0) {
for (type in AbstractEnumTools.getValues(PixabayCategory)) { data.unshift(fileSource);
data.push({title: type, sourceId: "unsplash", filter: ["category" => type]}); sources.data = data;
} }
});
sources.data = data; sources.data = data;
sourceBundle.get("nginx").getCategories().then((categories) -> {
for (category in categories) {
data.push({title: category, sourceId: "nginx", filter: ["category" => category]});
}
sources.data = data;
});
} }
private function sourceViewFactory(index:Int, source:ImageListConfig):ButtonView { private function sourceViewFactory(index:Int, source:ImageListConfig):ButtonView {
var result = new ButtonView(); var result = new ButtonView();
result.style = "button.source";
result.text = source.title; result.text = source.title;
return result; return result;
} }
@@ -62,6 +79,7 @@ import ru.m.update.Updater;
} }
override public function onShow(data:Dynamic):Void { override public function onShow(data:Dynamic):Void {
L.d("Frame", '$ID');
appUpdater.check().then((info:Null<PackageInfo>) -> { appUpdater.check().then((info:Null<PackageInfo>) -> {
if (info != null) { if (info != null) {
updateButton.visible = true; updateButton.visible = true;
@@ -74,9 +92,11 @@ import ru.m.update.Updater;
private function refresh():Void { private function refresh():Void {
gameStorage.getIndexPage({index: 0, count: 0, filter: startedGames.filter}).then(response -> { gameStorage.getIndexPage({index: 0, count: 0, filter: startedGames.filter}).then(response -> {
startedButton.text = response.total > 0 ? 'Started (${response.total})' : 'Started'; startedButton.text = response.total > 0 ? 'Started (${response.total})' : 'Started';
startedButton.disabled = response.total == 0;
}); });
gameStorage.getIndexPage({index: 0, count: 0, filter: completedGames.filter}).then(response -> { gameStorage.getIndexPage({index: 0, count: 0, filter: completedGames.filter}).then(response -> {
completedButton.text = response.total > 0 ? 'Completed (${response.total})' : 'Completed'; completedButton.text = response.total > 0 ? 'Completed (${response.total})' : 'Completed';
completedButton.disabled = response.total == 0;
}); });
} }
} }

View File

@@ -1,10 +1,10 @@
package ru.m.storage; package ru.m.storage;
import haxe.Unserializer;
import flash.net.SharedObject; import flash.net.SharedObject;
import haxe.Serializer;
import haxe.Unserializer;
import haxe.crypto.Md5; import haxe.crypto.Md5;
import haxe.io.Bytes; import haxe.io.Bytes;
import haxe.Serializer;
import promhx.Promise; import promhx.Promise;
import ru.m.data.DataSource; import ru.m.data.DataSource;
@@ -124,7 +124,8 @@ class SharedObjectDataStorage<D, I> implements DataStorage<D, I> {
} }
} }
applyOrder(page.order, values); applyOrder(page.order, values);
var result:Array<I> = values.slice(page.index * page.count, page.index * page.count + page.count).map(value -> value.id); var result:Array<I> = values.slice(page.index * page.count, page.index * page.count + page.count)
.map(value -> value.id);
return Promise.promise({ return Promise.promise({
page: page, page: page,
total: values.length, total: values.length,
@@ -138,7 +139,7 @@ class SharedObjectDataStorage<D, I> implements DataStorage<D, I> {
return { return {
page: indexPage.page, page: indexPage.page,
total: indexPage.total, total: indexPage.total,
data: data, data: data.filter((item:D) -> item != null),
} }
}); });
}); });