///|
pub enum AsyncImageStatus {
Pending
Loading
Succeeded
Failed
} derive(Debug)
///|
pub impl Show for AsyncImageStatus with fn output(self, logger) {
logger.write_object(Repr(self))
}
///|
pub fn AsyncImageStatus::to_int(self : AsyncImageStatus) -> Int {
match self {
AsyncImageStatus::Pending => 0
AsyncImageStatus::Loading => 1
AsyncImageStatus::Succeeded => 2
AsyncImageStatus::Failed => 3
}
}
///|
pub struct AsyncImageRequest {
id : Int
key : @atlas.AssetKey
url : String
mut status : AsyncImageStatus
mut image_spec : @atlas.ImageSpec?
} derive(Debug)
///|
pub impl Show for AsyncImageRequest with fn output(self, logger) {
logger.write_object(Repr(self))
}
///|
pub struct AsyncImageProgress {
total : Int
pending : Int
loading : Int
succeeded : Int
failed : Int
} derive(Debug)
///|
pub impl Show for AsyncImageProgress with fn output(self, logger) {
logger.write_object(Repr(self))
}
///|
pub struct AsyncImageLoader {
mut next_id : Int
requests : Map[Int, AsyncImageRequest]
mut pending_ids : Array[Int]
mut completed_ids : Array[Int]
fetch_image_fn : (String, (@atlas.ImageSpec?) -> Unit) -> Unit
}
///|
pub fn AsyncImageLoader::new(
fetch_image_fn? : (String, (@atlas.ImageSpec?) -> Unit) -> Unit = @atlas.fetch_image,
) -> AsyncImageLoader {
{
next_id: 1,
requests: Map([]),
pending_ids: [],
completed_ids: [],
fetch_image_fn,
}
}
///|
pub fn AsyncImageLoader::enqueue(
self : AsyncImageLoader,
key : @atlas.AssetKey,
url : String,
) -> Int {
let id = self.next_id
self.next_id = self.next_id + 1
self.requests.set(id, {
id,
key,
url,
status: AsyncImageStatus::Pending,
image_spec: None,
})
self.pending_ids.push(id)
id
}
///|
pub fn AsyncImageLoader::start_pending(self : AsyncImageLoader) -> Int {
let pending = self.pending_ids
self.pending_ids = []
let mut started = 0
for id in pending {
match self.requests.get(id) {
Some(req) =>
match req.status {
AsyncImageStatus::Pending => {
req.status = AsyncImageStatus::Loading
started = started + 1
let url = req.url
(self.fetch_image_fn)(url, fn(spec_opt) {
match self.requests.get(id) {
Some(done_req) => {
done_req.image_spec = spec_opt
done_req.status = match spec_opt {
Some(_) => AsyncImageStatus::Succeeded
None => AsyncImageStatus::Failed
}
self.completed_ids.push(id)
}
None => ()
}
})
}
_ => ()
}
None => ()
}
}
started
}
///|
pub fn AsyncImageLoader::request(
self : AsyncImageLoader,
id : Int,
) -> AsyncImageRequest? {
self.requests.get(id)
}
///|
pub fn AsyncImageLoader::drain_completed(self : AsyncImageLoader) -> Array[Int] {
let done = self.completed_ids
self.completed_ids = []
done
}
///|
pub fn AsyncImageLoader::progress(
self : AsyncImageLoader,
) -> AsyncImageProgress {
let mut pending = 0
let mut loading = 0
let mut succeeded = 0
let mut failed = 0
self.requests.each(fn(_id, req) {
match req.status {
AsyncImageStatus::Pending => pending = pending + 1
AsyncImageStatus::Loading => loading = loading + 1
AsyncImageStatus::Succeeded => succeeded = succeeded + 1
AsyncImageStatus::Failed => failed = failed + 1
}
})
{
total: pending + loading + succeeded + failed,
pending,
loading,
succeeded,
failed,
}
}
///|
pub fn AsyncImageLoader::commit_image(
self : AsyncImageLoader,
id : Int,
repo : @atlas.SimpleImageRepository,
) -> @gfx.ImageHandle? {
match self.requests.get(id) {
Some(req) =>
match (req.status, req.image_spec) {
(AsyncImageStatus::Succeeded, Some(spec)) =>
match repo.get_image(req.key) {
Some(handle) =>
match @atlas.update_image_spec(repo, req.key, spec) {
Some(updated) => Some(updated)
None => Some(handle)
}
None => Some(repo.create_image(req.key, spec)) catch { _ => None }
}
_ => None
}
None => None
}
}
///|
pub extend AsyncImageStatus with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend AsyncImageStatus with Show::{to_string, output}
///|
pub extend AsyncImageRequest with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend AsyncImageRequest with Show::{to_string, output}
///|
pub extend AsyncImageProgress with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend AsyncImageProgress with Show::{to_string, output}