فهرست منبع

Make browser GetItemsAsync trully async and lazy

Max Katz 2 سال پیش
والد
کامیت
2fd5e6b9d9

+ 12 - 4
src/Browser/Avalonia.Browser/Interop/GeneralHelpers.cs

@@ -1,4 +1,5 @@
 using System.Runtime.InteropServices.JavaScript;
+using System.Threading.Tasks;
 
 namespace Avalonia.Browser.Interop;
 
@@ -8,15 +9,22 @@ internal static partial class GeneralHelpers
     public static partial JSObject[] ItemsArrayAt(JSObject jsObject, string key);
     public static JSObject[] GetPropertyAsJSObjectArray(this JSObject jsObject, string key) => ItemsArrayAt(jsObject, key);
     
+    [JSImport("GeneralHelpers.itemAt", AvaloniaModule.MainModuleName)]
+    public static partial JSObject ItemAtInt(JSObject jsObject, int key);
+    public static JSObject GetArrayItem(this JSObject jsObject, int key) => ItemAtInt(jsObject, key);
+    
     [JSImport("GeneralHelpers.itemsArrayAt", AvaloniaModule.MainModuleName)]
     public static partial string[] ItemsArrayAtAsStrings(JSObject jsObject, string key);
     public static string[] GetPropertyAsStringArray(this JSObject jsObject, string key) => ItemsArrayAtAsStrings(jsObject, key);
     
     [JSImport("GeneralHelpers.callMethod", AvaloniaModule.MainModuleName)]
-    public static partial string IntCallMethodString(JSObject jsObject, string name);
+    public static partial string IntCallMethodStr(JSObject jsObject, string name);
+    [JSImport("GeneralHelpers.callMethod", AvaloniaModule.MainModuleName)]
+    public static partial string IntCallMethodStrStr(JSObject jsObject, string name, string arg1);
     [JSImport("GeneralHelpers.callMethod", AvaloniaModule.MainModuleName)]
-    public static partial string IntCallMethodStringString(JSObject jsObject, string name, string arg1);
+    public static partial Task<JSObject?> IntCallMethodPromiseObj(JSObject jsObject, string name);
 
-    public static string CallMethodString(this JSObject jsObject, string name) => IntCallMethodString(jsObject, name);
-    public static string CallMethodString(this JSObject jsObject, string name, string arg1) => IntCallMethodStringString(jsObject, name, arg1);
+    public static string CallMethodString(this JSObject jsObject, string name) => IntCallMethodStr(jsObject, name);
+    public static string CallMethodString(this JSObject jsObject, string name, string arg1) => IntCallMethodStrStr(jsObject, name, arg1);
+    public static Task<JSObject?> CallMethodObjectAsync(this JSObject jsObject, string name) => IntCallMethodPromiseObj(jsObject, name);
 }

+ 3 - 3
src/Browser/Avalonia.Browser/Interop/StorageHelper.cs

@@ -40,9 +40,9 @@ internal static partial class StorageHelper
     [JSImport("StorageItem.openRead", AvaloniaModule.StorageModuleName)]
     public static partial Task<JSObject> OpenRead(JSObject item);
 
-    [JSImport("StorageItem.getItems", AvaloniaModule.StorageModuleName)]
-    [return: JSMarshalAs<JSType.Promise<JSType.Object>>]
-    public static partial Task<JSObject?> GetItems(JSObject item);
+    [JSImport("StorageItem.getItemsIterator", AvaloniaModule.StorageModuleName)]
+    [return: JSMarshalAs<JSType.Object>]
+    public static partial JSObject? GetItemsIterator(JSObject item);
 
     [JSImport("StorageItems.itemsArray", AvaloniaModule.StorageModuleName)]
     public static partial JSObject[] ItemsArray(JSObject item);

+ 34 - 13
src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs

@@ -258,24 +258,45 @@ internal class JSStorageFolder : JSStorageItem, IStorageBookmarkFolder
     {
     }
 
-    public async Task<IReadOnlyList<IStorageItem>> GetItemsAsync()
+    public async IAsyncEnumerable<IStorageItem> GetItemsAsync()
     {
-        using var items = await StorageHelper.GetItems(FileHandle);
-        if (items is null)
+        using var itemsIterator = StorageHelper.GetItemsIterator(FileHandle);
+        if (itemsIterator is null)
         {
-            return Array.Empty<IStorageItem>();
+            yield break;
         }
 
-        var itemsArray = StorageHelper.ItemsArray(items);
+        while (true)
+        {
+            var nextResult = await itemsIterator.CallMethodObjectAsync("next");
+            if (nextResult is null)
+            {
+                yield break;
+            }
+
+            var isDone = nextResult.GetPropertyAsBoolean("done");
+            if (isDone)
+            {
+                yield break;
+            }
 
-        return itemsArray
-            .Select(reference => reference.GetPropertyAsString("kind") switch
+            var valArray = nextResult.GetPropertyAsJSObject("value");
+            var storageItem = valArray?.GetArrayItem(1); // 0 - item name, 1 - item instance
+            if (storageItem is null)
             {
-                "directory" => (IStorageItem)new JSStorageFolder(reference),
-                "file" => new JSStorageFile(reference),
-                _ => null
-            })
-            .Where(i => i is not null)
-            .ToArray()!;
+                yield break;
+            }
+
+            var kind = storageItem.GetPropertyAsString("kind");
+            switch (kind)
+            {
+                case "directory":
+                    yield return new JSStorageFolder(storageItem);
+                    break;
+                case "file":
+                    yield return new JSStorageFile(storageItem);
+                    break;
+            }
+        }
     }
 }

+ 6 - 1
src/Browser/Avalonia.Browser/webapp/modules/avalonia/generalHelpers.ts

@@ -1,5 +1,5 @@
 export class GeneralHelpers {
-    public static itemsArrayAt(instance: any, key: string): any[] {
+    public static itemsArrayAt(instance: any, key: any): any[] {
         const items = instance[key];
         if (!items) {
             return [];
@@ -12,6 +12,11 @@ export class GeneralHelpers {
         return retItems;
     }
 
+    public static itemAt(instance: any, key: any): any {
+        const item = instance[key];
+        return item;
+    }
+
     public static callMethod(instance: any, name: string /*, args */): any {
         const args = Array.prototype.slice.call(arguments, 2);
         return instance[name].apply(instance, args);

+ 3 - 7
src/Browser/Avalonia.Browser/webapp/modules/storage/storageItem.ts

@@ -89,16 +89,12 @@ export class StorageItem {
         }
     }
 
-    public static async getItems(item: StorageItem): Promise<StorageItems> {
+    public static getItemsIterator(item: StorageItem): any | null {
         if (item.kind !== "directory" || !item.handle) {
-            return new StorageItems([]);
+            return null;
         }
 
-        const items: StorageItem[] = [];
-        for await (const [, value] of (item.handle as any).entries()) {
-            items.push(new StorageItem(value));
-        }
-        return new StorageItems(items);
+        return (item.handle as any).entries();
     }
 
     private async verityPermissions(mode: "read" | "readwrite"): Promise<void | never> {