@quenty/avatareditorutils 7.14.0 → 7.14.1-canary.522.b5d379b.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [7.14.1-canary.522.b5d379b.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/avatareditorutils@7.14.0...@quenty/avatareditorutils@7.14.1-canary.522.b5d379b.0) (2024-11-24)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Reject promises when failing to succeed prompt ([8995ffc](https://github.com/Quenty/NevermoreEngine/commit/8995ffc94abe3e6e34f6a924f275177e7a22b641))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* Break up logic into inventory and catalog to allow centralized querying ([939f8b1](https://github.com/Quenty/NevermoreEngine/commit/939f8b10398b9955129195b71e95f12bb29bd219))
|
|
17
|
+
* PlayerProductManagerClient queries inventory when inventory is available ([d159eea](https://github.com/Quenty/NevermoreEngine/commit/d159eeade8701bcbcd87e97126df3c3dfb155b8c))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
6
23
|
# [7.14.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/avatareditorutils@7.13.0...@quenty/avatareditorutils@7.14.0) (2024-11-13)
|
|
7
24
|
|
|
8
25
|
**Note:** Version bump only for package @quenty/avatareditorutils
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/avatareditorutils",
|
|
3
|
-
"version": "7.14.0",
|
|
3
|
+
"version": "7.14.1-canary.522.b5d379b.0",
|
|
4
4
|
"description": "Provides utility functions to work with the Roblox AvatarEditorService",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -25,17 +25,22 @@
|
|
|
25
25
|
"Quenty"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@quenty/
|
|
29
|
-
"@quenty/
|
|
30
|
-
"@quenty/
|
|
31
|
-
"@quenty/
|
|
32
|
-
"@quenty/
|
|
33
|
-
"@quenty/
|
|
34
|
-
"@quenty/
|
|
35
|
-
"@quenty/
|
|
28
|
+
"@quenty/aggregator": "1.0.1-canary.522.b5d379b.0",
|
|
29
|
+
"@quenty/brio": "14.13.1-canary.522.b5d379b.0",
|
|
30
|
+
"@quenty/enumutils": "3.3.0",
|
|
31
|
+
"@quenty/loader": "10.7.1",
|
|
32
|
+
"@quenty/maid": "3.4.0",
|
|
33
|
+
"@quenty/memoize": "1.5.1",
|
|
34
|
+
"@quenty/observablecollection": "12.14.1-canary.522.b5d379b.0",
|
|
35
|
+
"@quenty/pagesutils": "5.8.1-canary.522.b5d379b.0",
|
|
36
|
+
"@quenty/promise": "10.8.0",
|
|
37
|
+
"@quenty/rx": "13.13.1-canary.522.b5d379b.0",
|
|
38
|
+
"@quenty/servicebag": "11.10.0",
|
|
39
|
+
"@quenty/symbol": "3.3.0",
|
|
40
|
+
"@quenty/valueobject": "13.13.1-canary.522.b5d379b.0"
|
|
36
41
|
},
|
|
37
42
|
"publishConfig": {
|
|
38
43
|
"access": "public"
|
|
39
44
|
},
|
|
40
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "b5d379b3553fb94b954870562e1bd2a57e9a1c10"
|
|
41
46
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class AvatarEditorInventory
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local BaseObject = require("BaseObject")
|
|
8
|
+
local ObservableMap = require("ObservableMap")
|
|
9
|
+
local Rx = require("Rx")
|
|
10
|
+
local PagesUtils = require("PagesUtils")
|
|
11
|
+
local Promise = require("Promise")
|
|
12
|
+
|
|
13
|
+
local AvatarEditorInventory = setmetatable({}, BaseObject)
|
|
14
|
+
AvatarEditorInventory.ClassName = "AvatarEditorInventory"
|
|
15
|
+
AvatarEditorInventory.__index = AvatarEditorInventory
|
|
16
|
+
|
|
17
|
+
function AvatarEditorInventory.new()
|
|
18
|
+
local self = setmetatable(BaseObject.new(), AvatarEditorInventory)
|
|
19
|
+
|
|
20
|
+
self._assetIdToAsset = self._maid:Add(ObservableMap.new())
|
|
21
|
+
|
|
22
|
+
return self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
function AvatarEditorInventory:PromiseProcessPages(inventoryPages)
|
|
26
|
+
return Promise.spawn(function(resolve, reject)
|
|
27
|
+
local pageData = inventoryPages:GetCurrentPage()
|
|
28
|
+
while pageData do
|
|
29
|
+
for _, data in pairs(pageData) do
|
|
30
|
+
self._assetIdToAsset:Set(data.AssetId, data)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
pageData = nil
|
|
34
|
+
|
|
35
|
+
if not inventoryPages.IsFinished then
|
|
36
|
+
local ok, err = PagesUtils.promiseAdvanceToNextPage(inventoryPages):Yield()
|
|
37
|
+
if not ok then
|
|
38
|
+
return reject(string.format("Failed to advance to next page due to %s", tostring(err)))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
pageData = err
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
resolve()
|
|
46
|
+
end)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
function AvatarEditorInventory:IsAssetIdInInventory(assetId)
|
|
50
|
+
return self._assetIdToAsset:Get(assetId) ~= nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
function AvatarEditorInventory:ObserveAssetIdInInventory(assetId)
|
|
54
|
+
assert(type(assetId) == "number", "Bad assetId")
|
|
55
|
+
|
|
56
|
+
return self._assetIdToAsset:ObserveAtKey(assetId):Pipe({
|
|
57
|
+
Rx.map(function(data)
|
|
58
|
+
return data ~= nil
|
|
59
|
+
end)
|
|
60
|
+
})
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
return AvatarEditorInventory
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class AvatarEditorInventoryServiceClient
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local AvatarEditorService = game:GetService("AvatarEditorService")
|
|
8
|
+
|
|
9
|
+
local AvatarEditorInventory = require("AvatarEditorInventory")
|
|
10
|
+
local AvatarEditorUtils = require("AvatarEditorUtils")
|
|
11
|
+
local EnumUtils = require("EnumUtils")
|
|
12
|
+
local Maid = require("Maid")
|
|
13
|
+
local MemorizeUtils = require("MemorizeUtils")
|
|
14
|
+
local Promise = require("Promise")
|
|
15
|
+
local ValueObject = require("ValueObject")
|
|
16
|
+
local PagesProxy = require("PagesProxy")
|
|
17
|
+
|
|
18
|
+
local AvatarEditorInventoryServiceClient = {}
|
|
19
|
+
AvatarEditorInventoryServiceClient.ServiceName = "AvatarEditorInventoryServiceClient"
|
|
20
|
+
|
|
21
|
+
function AvatarEditorInventoryServiceClient:Init(serviceBag)
|
|
22
|
+
assert(not self._serviceBag, "Already initialized")
|
|
23
|
+
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
24
|
+
self._maid = Maid.new()
|
|
25
|
+
|
|
26
|
+
self._isAccessAllowed = self._maid:Add(ValueObject.new(false, "boolean"))
|
|
27
|
+
self._assetTypeToInventoryPromises = {}
|
|
28
|
+
|
|
29
|
+
self._maid:GiveTask(AvatarEditorService.PromptAllowInventoryReadAccessCompleted:Connect(function(avatarPromptResult)
|
|
30
|
+
if avatarPromptResult == Enum.AvatarPromptResult.Success then
|
|
31
|
+
self._isAccessAllowed.Value = true
|
|
32
|
+
end
|
|
33
|
+
end))
|
|
34
|
+
|
|
35
|
+
self._promiseInventoryPages = MemorizeUtils.memoize(function(avatarAssetTypes)
|
|
36
|
+
return AvatarEditorUtils.promiseInventoryPages(avatarAssetTypes)
|
|
37
|
+
:Then(function(catalogPages)
|
|
38
|
+
-- Allow for replay
|
|
39
|
+
return PagesProxy.new(catalogPages)
|
|
40
|
+
end)
|
|
41
|
+
end)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
function AvatarEditorInventoryServiceClient:PromiseInventoryPages(avatarAssetTypes)
|
|
45
|
+
return self:PromiseEnsureAccess()
|
|
46
|
+
:Then(function()
|
|
47
|
+
return self._promiseInventoryPages(avatarAssetTypes)
|
|
48
|
+
end)
|
|
49
|
+
:Then(function(pagesProxy)
|
|
50
|
+
return pagesProxy:Clone()
|
|
51
|
+
end)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
function AvatarEditorInventoryServiceClient:PromiseInventoryForAvatarAssetType(avatarAssetType)
|
|
55
|
+
assert(EnumUtils.isOfType(Enum.AvatarAssetType, avatarAssetType), "Bad avatarAssetType")
|
|
56
|
+
|
|
57
|
+
if self._assetTypeToInventoryPromises[avatarAssetType] then
|
|
58
|
+
return self._assetTypeToInventoryPromises[avatarAssetType]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
local inventory = self._maid:Add(AvatarEditorInventory.new())
|
|
62
|
+
|
|
63
|
+
self._assetTypeToInventoryPromises[avatarAssetType] = AvatarEditorUtils.promiseInventoryPages({
|
|
64
|
+
avatarAssetType
|
|
65
|
+
})
|
|
66
|
+
:Then(function(inventoryPages)
|
|
67
|
+
return inventory:PromiseProcessPages(inventoryPages)
|
|
68
|
+
end)
|
|
69
|
+
:Then(function()
|
|
70
|
+
return inventory
|
|
71
|
+
end)
|
|
72
|
+
|
|
73
|
+
return self._assetTypeToInventoryPromises[avatarAssetType]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
function AvatarEditorInventoryServiceClient:IsInventoryAccessAllowed()
|
|
77
|
+
return self._isAccessAllowed.Value
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
function AvatarEditorInventoryServiceClient:ObserveIsInventoryAccessAllowed()
|
|
81
|
+
return self._isAccessAllowed:Observe()
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
function AvatarEditorInventoryServiceClient:PromiseEnsureAccess()
|
|
85
|
+
if self._isAccessAllowed.Value then
|
|
86
|
+
return Promise.resolved()
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if self._currentAccessPromise and self._currentAccessPromise:IsPending() then
|
|
90
|
+
return self._currentAccessPromise
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
local promise = self._maid:GivePromise(AvatarEditorUtils.promptAllowInventoryReadAccess())
|
|
94
|
+
|
|
95
|
+
promise:Then(function(avatarPromptResult)
|
|
96
|
+
if avatarPromptResult == Enum.AvatarPromptResult.Success then
|
|
97
|
+
self._isAccessAllowed.Value = true
|
|
98
|
+
end
|
|
99
|
+
end)
|
|
100
|
+
|
|
101
|
+
self._currentAccessPromise = promise
|
|
102
|
+
|
|
103
|
+
return self._currentAccessPromise
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
function AvatarEditorInventoryServiceClient:Destroy()
|
|
107
|
+
self._maid:DoCleaning()
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
return AvatarEditorInventoryServiceClient
|
|
@@ -456,7 +456,11 @@ function AvatarEditorUtils.promptAllowInventoryReadAccess()
|
|
|
456
456
|
end)
|
|
457
457
|
|
|
458
458
|
maid:GiveTask(AvatarEditorService.PromptAllowInventoryReadAccessCompleted:Connect(function(avatarPromptResult)
|
|
459
|
-
|
|
459
|
+
if avatarPromptResult == Enum.AvatarPromptResult.Success then
|
|
460
|
+
promise:Resolve(avatarPromptResult)
|
|
461
|
+
else
|
|
462
|
+
promise:Reject(avatarPromptResult)
|
|
463
|
+
end
|
|
460
464
|
end))
|
|
461
465
|
|
|
462
466
|
local ok, err = pcall(function()
|
|
@@ -493,8 +497,12 @@ function AvatarEditorUtils.promptCreateOutfit(outfit: HumanoidDescription, rigTy
|
|
|
493
497
|
maid:DoCleaning()
|
|
494
498
|
end)
|
|
495
499
|
|
|
496
|
-
maid:GiveTask(AvatarEditorService.PromptCreateOutfitCompleted:Connect(function(avatarPromptResult)
|
|
497
|
-
|
|
500
|
+
maid:GiveTask(AvatarEditorService.PromptCreateOutfitCompleted:Connect(function(avatarPromptResult, failureType)
|
|
501
|
+
if avatarPromptResult == Enum.AvatarPromptResult.Success then
|
|
502
|
+
promise:Resolve(avatarPromptResult)
|
|
503
|
+
else
|
|
504
|
+
promise:Reject(avatarPromptResult, failureType)
|
|
505
|
+
end
|
|
498
506
|
end))
|
|
499
507
|
|
|
500
508
|
local ok, err = pcall(function()
|
|
@@ -526,7 +534,11 @@ function AvatarEditorUtils.promptDeleteOutfit(outfitId: number)
|
|
|
526
534
|
end)
|
|
527
535
|
|
|
528
536
|
maid:GiveTask(AvatarEditorService.PromptDeleteOutfitCompleted:Connect(function(avatarPromptResult)
|
|
529
|
-
|
|
537
|
+
if avatarPromptResult == Enum.AvatarPromptResult.Success then
|
|
538
|
+
promise:Resolve(avatarPromptResult)
|
|
539
|
+
else
|
|
540
|
+
promise:Reject(avatarPromptResult)
|
|
541
|
+
end
|
|
530
542
|
end))
|
|
531
543
|
|
|
532
544
|
local ok, err = pcall(function()
|
|
@@ -558,7 +570,11 @@ function AvatarEditorUtils.promptRenameOutfit(outfitId: number)
|
|
|
558
570
|
end)
|
|
559
571
|
|
|
560
572
|
maid:GiveTask(AvatarEditorService.PromptRenameOutfitCompleted:Connect(function(avatarPromptResult)
|
|
561
|
-
|
|
573
|
+
if avatarPromptResult == Enum.AvatarPromptResult.Success then
|
|
574
|
+
promise:Resolve(avatarPromptResult)
|
|
575
|
+
else
|
|
576
|
+
promise:Reject(avatarPromptResult)
|
|
577
|
+
end
|
|
562
578
|
end))
|
|
563
579
|
|
|
564
580
|
local ok, err = pcall(function()
|
|
@@ -590,7 +606,11 @@ function AvatarEditorUtils.promptSaveAvatar(humanoidDescription: HumanoidDescrip
|
|
|
590
606
|
end)
|
|
591
607
|
|
|
592
608
|
maid:GiveTask(AvatarEditorService.PromptSaveAvatarCompleted:Connect(function(avatarPromptResult)
|
|
593
|
-
|
|
609
|
+
if avatarPromptResult == Enum.AvatarPromptResult.Success then
|
|
610
|
+
promise:Resolve(avatarPromptResult)
|
|
611
|
+
else
|
|
612
|
+
promise:Reject(avatarPromptResult)
|
|
613
|
+
end
|
|
594
614
|
end))
|
|
595
615
|
|
|
596
616
|
local ok, err = pcall(function()
|
|
@@ -626,7 +646,11 @@ function AvatarEditorUtils.promptSetFavorite(itemId: number, itemType: AvatarIte
|
|
|
626
646
|
end)
|
|
627
647
|
|
|
628
648
|
maid:GiveTask(AvatarEditorService.PromptSetFavoriteCompleted:Connect(function(avatarPromptResult)
|
|
629
|
-
|
|
649
|
+
if avatarPromptResult == Enum.AvatarPromptResult.Success then
|
|
650
|
+
promise:Resolve(avatarPromptResult)
|
|
651
|
+
else
|
|
652
|
+
promise:Reject(avatarPromptResult)
|
|
653
|
+
end
|
|
630
654
|
end))
|
|
631
655
|
|
|
632
656
|
local ok, err = pcall(function()
|
|
@@ -662,7 +686,11 @@ function AvatarEditorUtils.promptUpdateOutfit(outfitId: number, updatedOutfit: H
|
|
|
662
686
|
end)
|
|
663
687
|
|
|
664
688
|
maid:GiveTask(AvatarEditorService.PromptUpdateOutfitCompleted:Connect(function(avatarPromptResult)
|
|
665
|
-
|
|
689
|
+
if avatarPromptResult == Enum.AvatarPromptResult.Success then
|
|
690
|
+
promise:Resolve(avatarPromptResult)
|
|
691
|
+
else
|
|
692
|
+
promise:Reject(avatarPromptResult)
|
|
693
|
+
end
|
|
666
694
|
end))
|
|
667
695
|
|
|
668
696
|
local ok, err = pcall(function()
|
|
@@ -6,6 +6,9 @@ local require = require(script.Parent.loader).load(script)
|
|
|
6
6
|
|
|
7
7
|
local MemorizeUtils = require("MemorizeUtils")
|
|
8
8
|
local AvatarEditorUtils = require("AvatarEditorUtils")
|
|
9
|
+
local Aggregator = require("Aggregator")
|
|
10
|
+
local Maid = require("Maid")
|
|
11
|
+
local PagesProxy = require("PagesProxy")
|
|
9
12
|
|
|
10
13
|
local CatalogSearchServiceCache = {}
|
|
11
14
|
CatalogSearchServiceCache.ServiceName = "CatalogSearchServiceCache"
|
|
@@ -13,14 +16,22 @@ CatalogSearchServiceCache.ServiceName = "CatalogSearchServiceCache"
|
|
|
13
16
|
function CatalogSearchServiceCache:Init(serviceBag)
|
|
14
17
|
assert(not self._serviceBag, "Already initialized")
|
|
15
18
|
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
19
|
+
self._maid = Maid.new()
|
|
16
20
|
|
|
21
|
+
-- TODO: If you scroll down long enough this leaks memory
|
|
17
22
|
self._promiseSearchCatalog = MemorizeUtils.memoize(function(params)
|
|
18
23
|
return AvatarEditorUtils.promiseSearchCatalog(params)
|
|
24
|
+
:Then(function(catalogPages)
|
|
25
|
+
return PagesProxy.new(catalogPages)
|
|
26
|
+
end)
|
|
19
27
|
end)
|
|
20
28
|
|
|
21
|
-
self.
|
|
22
|
-
return AvatarEditorUtils.
|
|
23
|
-
end)
|
|
29
|
+
self._assetAggregator = self._maid:Add(Aggregator.new("AvatarEditorUtils.promiseBatchItemDetails", function(itemIds)
|
|
30
|
+
return AvatarEditorUtils.promiseBatchItemDetails(itemIds, Enum.AvatarItemType.Asset)
|
|
31
|
+
end))
|
|
32
|
+
self._bundleAggregator = self._maid:Add(Aggregator.new("AvatarEditorUtils.promiseBatchItemDetails", function(itemIds)
|
|
33
|
+
return AvatarEditorUtils.promiseBatchItemDetails(itemIds, Enum.AvatarItemType.Bundle)
|
|
34
|
+
end))
|
|
24
35
|
end
|
|
25
36
|
|
|
26
37
|
function CatalogSearchServiceCache:PromiseAvatarRules()
|
|
@@ -32,12 +43,25 @@ function CatalogSearchServiceCache:PromiseAvatarRules()
|
|
|
32
43
|
return self._avatarRulesPromise
|
|
33
44
|
end
|
|
34
45
|
|
|
46
|
+
function CatalogSearchServiceCache:PromiseItemDetails(assetId, avatarItemType)
|
|
47
|
+
if avatarItemType == Enum.AvatarItemType.Asset then
|
|
48
|
+
return self._assetAggregator:Promise(assetId)
|
|
49
|
+
elseif avatarItemType == Enum.AvatarItemType.Bundle then
|
|
50
|
+
return self._bundleAggregator:Promise(assetId)
|
|
51
|
+
else
|
|
52
|
+
error("Unknown avatarItemType")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
35
56
|
function CatalogSearchServiceCache:PromiseSearchCatalog(params)
|
|
36
57
|
return self._promiseSearchCatalog(params)
|
|
58
|
+
:Then(function(pagesProxy)
|
|
59
|
+
return pagesProxy:Clone()
|
|
60
|
+
end)
|
|
37
61
|
end
|
|
38
62
|
|
|
39
|
-
function CatalogSearchServiceCache:
|
|
40
|
-
|
|
63
|
+
function CatalogSearchServiceCache:Destroy()
|
|
64
|
+
self._maid:DoCleaning()
|
|
41
65
|
end
|
|
42
66
|
|
|
43
67
|
return CatalogSearchServiceCache
|