@eluvio/elv-client-js 3.2.16 → 3.2.18

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.
@@ -1,6 +1,7 @@
1
1
  let WalletConfiguration = {
2
2
  demo: {
3
3
  configUrl: "https://demov3.net955210.contentfabric.io/config",
4
+ stateStoreUrls: ["https://appsvc.svc.eluv.io/dv3"],
4
5
  staging: {
5
6
  siteId: "iq__2gkNh8CCZqFFnoRpEUmz7P3PaBQG",
6
7
  purchaseMode: "develop",
@@ -9,6 +10,7 @@ let WalletConfiguration = {
9
10
  },
10
11
  main: {
11
12
  configUrl: "https://main.net955305.contentfabric.io/config",
13
+ stateStoreUrls: ["https://appsvc.svc.eluv.io/main"],
12
14
  staging: {
13
15
  siteId: "iq__inauxD1KLyKWPHargCWjdCh2ayr",
14
16
  purchaseMode: "production",
@@ -0,0 +1,182 @@
1
+ const Utils = require("../Utils");
2
+ const UrlJoin = require("url-join");
3
+
4
+ const StateStorePath = ({network, path}) => {
5
+ return UrlJoin(network === "main" ? "/main" : "/dv3", path);
6
+ };
7
+
8
+ const UserProfilePath = ({network, appId, userAddress, key, type, mode}) => {
9
+ return StateStorePath({network, path: UrlJoin(type === "app" ? "app" : "usr", type === "app" ? appId : "", userAddress, mode === "public" ? "pub" : "pri", key || "")});
10
+ };
11
+
12
+ /**
13
+ * Methods related to getting and setting user profile data.
14
+ *
15
+ * @module ProfileMethods
16
+ */
17
+
18
+ /**
19
+ * Retrieve user profile metadata for the specified user
20
+ *
21
+ * @methodGroup ProfileMetadata
22
+ * @namedParams
23
+ * @param {string=} type="app" - Specify `app` or `user` metadata.
24
+ * @param {string=} mode="public" - Specify `public` or `private` metadata. If private is specified, you may only retrieve metadata for the current user.
25
+ * @param {string=} appId - Namespace to use for the metadata, if retrieving app metadata. Uses the app ID specified on client initialization by default.
26
+ * @param {string=} userAddress - User to retrieve metadata for. If not specified, will retrieve metadata for the current user
27
+ * @param {string=} key - The metadata key to retrieve
28
+ *
29
+ * @returns {Promise<Object|String>} - Returns the specified metadata
30
+ */
31
+ exports.ProfileMetadata = async function({type="app", mode="public", appId, userAddress, key}) {
32
+ try {
33
+ const response = await this.stateStoreClient.Request({
34
+ path: UserProfilePath({
35
+ network: this.network,
36
+ appId: appId || this.appId,
37
+ userAddress: userAddress || this.UserAddress(),
38
+ type,
39
+ mode,
40
+ key
41
+ }),
42
+ headers: mode === "private" ?
43
+ {Authorization: `Bearer ${this.AuthToken()}`} : undefined
44
+ });
45
+
46
+ if(!response.ok) {
47
+ throw response;
48
+ }
49
+
50
+ return (await Utils.ResponseToJson(response))[key];
51
+ } catch(error) {
52
+ if(error.status === 404) {
53
+ return undefined;
54
+ }
55
+
56
+ throw error;
57
+ }
58
+ };
59
+
60
+ /**
61
+ * Set user profile metadata for the current user
62
+ *
63
+ * @methodGroup ProfileMetadata
64
+ * @namedParams
65
+ * @param {string=} type="app" - Specify `app` or `user` metadata.
66
+ * @param {string=} mode="public" - Specify `public` or `private` metadata.
67
+ * @param {string=} appId - Namespace to use for the metadata, if retrieving app metadata. Uses the app ID specified on client initialization by default.
68
+ * @param {string} key - The metadata key to set
69
+ * @param {string} value - The metadata value to set
70
+ */
71
+ exports.SetProfileMetadata = async function({type="app", mode="public", appId, key, value}) {
72
+ await this.stateStoreClient.Request({
73
+ method: "POST",
74
+ path: UserProfilePath({
75
+ network: this.network,
76
+ appId: appId || this.appId,
77
+ userAddress: this.UserAddress(),
78
+ type,
79
+ mode,
80
+ key
81
+ }),
82
+ body: value,
83
+ bodyType: typeof value === "object" ? "JSON" : "string",
84
+ headers: {
85
+ Authorization: `Bearer ${this.AuthToken()}`
86
+ }
87
+ });
88
+ };
89
+
90
+
91
+ /**
92
+ * Remove user profile metadata for the current user
93
+ *
94
+ * @methodGroup ProfileMetadata
95
+ * @namedParams
96
+ * @param {string=} type="app" - Specify `app` or `user` metadata.
97
+ * @param {string=} mode="public" - Specify `public` or `private` metadata.
98
+ * @param {string=} appId - Namespace to use for the metadata, if retrieving app metadata.. Uses the app ID specified on client initialization by default.
99
+ * @param {string} key - The metadata key to set
100
+ * @param {string} value - The metadata value to set
101
+ */
102
+ exports.RemoveProfileMetadata = async function({type="app", mode="public", appId, key, value}) {
103
+ await this.stateStoreClient.Request({
104
+ method: "DELETE",
105
+ path: UserProfilePath({
106
+ network: this.network,
107
+ appId: appId || this.appId,
108
+ userAddress: this.UserAddress(),
109
+ type,
110
+ mode,
111
+ key
112
+ }),
113
+ body: value,
114
+ headers: {
115
+ Authorization: `Bearer ${this.AuthToken()}`
116
+ }
117
+ });
118
+ };
119
+
120
+ /**
121
+ * Retrieve profile info for the specified user, including address, username and profile image (if set)
122
+ *
123
+ * @methodGroup Profile
124
+ * @param {string=} userAddress - Address of the user
125
+ * @param {string=} userName - Username of the user
126
+ *
127
+ * @returns {Promise<Object>} - Profile info of the specified user
128
+ */
129
+ exports.Profile = async function({userAddress, userName}) {
130
+ if(userName) {
131
+ userAddress = await this.UserNameToAddress({userName});
132
+ }
133
+
134
+ if(!userAddress) {
135
+ throw Error("Eluvio Wallet Client: Unable to determine profile - user address not specified");
136
+ }
137
+
138
+ if(!userName) {
139
+ userName = await this.ProfileMetadata({type: "user", userAddress, key: "username"});
140
+ }
141
+
142
+ const imageUrl = await this.ProfileMetadata({type: "user", userAddress, key: "icon_url"});
143
+
144
+ return {
145
+ userAddress: Utils.FormatAddress(userAddress),
146
+ userName,
147
+ imageUrl
148
+ };
149
+ };
150
+
151
+ exports.UserNameToAddress = async function({userName}) {
152
+ try {
153
+ const response = await this.stateStoreClient.Request({
154
+ method: "GET",
155
+ path: StateStorePath({network: this.network, path: UrlJoin("usr", "profile_for_username", userName)}),
156
+ });
157
+
158
+ if(!response.ok) {
159
+ throw response;
160
+ }
161
+
162
+ return (await Utils.ResponseToJson(response)).address;
163
+ } catch(error) {
164
+ if(error.status !== 404) {
165
+ throw error;
166
+ }
167
+ }
168
+
169
+ return undefined;
170
+ };
171
+
172
+
173
+
174
+
175
+
176
+
177
+
178
+
179
+
180
+
181
+
182
+
@@ -23,6 +23,24 @@ const RarityToPercentage = (rarity) => {
23
23
  return percentage;
24
24
  };
25
25
 
26
+ const LinkTargetHash = function(link) {
27
+ if(!link) { return; }
28
+
29
+ if(link["."] && link["."].source) {
30
+ return link["."].source;
31
+ }
32
+
33
+ if(link["/"] && link["/"].startsWith("/qfab/")) {
34
+ return link["/"].split("/").find(segment => segment.startsWith("hq__"));
35
+ }
36
+
37
+ if(link["."] && link["."].container) {
38
+ return link["."].container;
39
+ }
40
+ };
41
+
42
+ exports.LinkTargetHash = LinkTargetHash;
43
+
26
44
  // Format NFT or listing result into consistent format
27
45
  const FormatNFTDetails = function(entry) {
28
46
  const isListing = !!entry.id;
@@ -70,7 +88,7 @@ const FormatNFTDetails = function(entry) {
70
88
  exports.FormatNFTDetails = FormatNFTDetails;
71
89
 
72
90
 
73
- const FormatNFTMetadata = function(nft) {
91
+ const FormatNFTMetadata = function(walletClient, nft) {
74
92
  nft.formatted = true;
75
93
 
76
94
  // Surface relevant details to top level
@@ -105,10 +123,13 @@ const FormatNFTMetadata = function(nft) {
105
123
 
106
124
  let embedUrl = new URL("https://embed.v3.contentfabric.io");
107
125
  embedUrl.searchParams.set("p", "");
108
- embedUrl.searchParams.set("net", rootStore.network === "demo" ? "demo" : "main");
109
- embedUrl.searchParams.set("ath", rootStore.authToken);
126
+ embedUrl.searchParams.set("net", walletClient.network === "demo" ? "demo" : "main");
110
127
 
111
- if(mediaType === "video") {
128
+ if(media.requires_permissions) {
129
+ embedUrl.searchParams.set("ath", walletClient.AuthToken());
130
+ }
131
+
132
+ if(["video", "audio"].includes(mediaType)) {
112
133
  embedUrl.searchParams.set("vid", LinkTargetHash(media.media_link));
113
134
  embedUrl.searchParams.set("ct", "h");
114
135
  embedUrl.searchParams.set("ap", "");
@@ -123,6 +144,7 @@ const FormatNFTMetadata = function(nft) {
123
144
  embed_url: embedUrl.toString()
124
145
  };
125
146
  } catch(error) {
147
+ walletClient.Log(error, true);
126
148
  return media;
127
149
  }
128
150
  });
@@ -134,8 +156,7 @@ const FormatNFTMetadata = function(nft) {
134
156
  if(nft.metadata.pack_options && nft.metadata.pack_options[key]) {
135
157
  let embedUrl = new URL("https://embed.v3.contentfabric.io");
136
158
  embedUrl.searchParams.set("p", "");
137
- embedUrl.searchParams.set("net", rootStore.network === "demo" ? "demo" : "main");
138
- embedUrl.searchParams.set("ath", rootStore.authToken || rootStore.staticToken);
159
+ embedUrl.searchParams.set("net", walletClient.network === "demo" ? "demo" : "main");
139
160
  embedUrl.searchParams.set("vid", LinkTargetHash(nft.metadata.pack_options[key]));
140
161
  embedUrl.searchParams.set("ap", "");
141
162
 
@@ -155,27 +176,11 @@ const FormatNFTMetadata = function(nft) {
155
176
 
156
177
  exports.FormatNFTMetadata = FormatNFTMetadata;
157
178
 
158
- exports.FormatNFT = function (item) {
159
- return FormatNFTMetadata(FormatNFTDetails(item));
179
+ exports.FormatNFT = function (walletClient, item) {
180
+ return FormatNFTMetadata(walletClient, FormatNFTDetails(item));
160
181
  };
161
182
 
162
183
 
163
- exports.LinkTargetHash = function(link) {
164
- if(!link) { return; }
165
-
166
- if(link["."] && link["."].source) {
167
- return link["."].source;
168
- }
169
-
170
- if(link["/"] && link["/"].startsWith("/qfab/")) {
171
- return link["/"].split("/").find(segment => segment.startsWith("hq__"));
172
- }
173
-
174
- if(link["."] && link["."].container) {
175
- return link["."].container;
176
- }
177
- };
178
-
179
184
  // https://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
180
185
  const Popup = ({url, title, w, h}) => {
181
186
  // Fixes dual-screen position
@@ -1,6 +1,7 @@
1
1
  const {ElvClient} = require("../ElvClient");
2
2
  const Configuration = require("./Configuration");
3
3
  const {LinkTargetHash, FormatNFT, ActionPopup} = require("./Utils");
4
+ const HTTPClient = require("../HttpClient");
4
5
  const UrlJoin = require("url-join");
5
6
  const Utils = require("../Utils");
6
7
  const Ethers = require("ethers");
@@ -15,7 +16,9 @@ const embedded = inBrowser && window.top !== window.self;
15
16
  * See the Modules section on the sidebar for all client methods unrelated to login and authorization
16
17
  */
17
18
  class ElvWalletClient {
18
- constructor({client, network, mode, marketplaceInfo, storeAuthToken}) {
19
+ constructor({appId, client, network, mode, marketplaceInfo, storeAuthToken}) {
20
+ this.appId = appId;
21
+
19
22
  this.client = client;
20
23
  this.loggedIn = false;
21
24
 
@@ -33,6 +36,9 @@ class ElvWalletClient {
33
36
  this.availableMarketplacesById = {};
34
37
  this.marketplaceHashes = {};
35
38
 
39
+ this.stateStoreUrls = Configuration[network].stateStoreUrls;
40
+ this.stateStoreClient = new HTTPClient({uris: this.stateStoreUrls});
41
+
36
42
  // Caches
37
43
  this.cachedMarketplaces = {};
38
44
  this.cachedCSS = {};
@@ -60,8 +66,10 @@ class ElvWalletClient {
60
66
  *
61
67
  * Specify tenantSlug and marketplaceSlug to automatically associate this tenant with a particular marketplace.
62
68
  *
69
+ *
63
70
  * @methodGroup Initialization
64
71
  * @namedParams
72
+ * @param {string} appId - A string identifying your app. This is used for namespacing user profile data.
65
73
  * @param {string} network=main - Name of the Fabric network to use (`main`, `demo`)
66
74
  * @param {string} mode=production - Environment to use (`production`, `staging`)
67
75
  * @param {Object=} marketplaceParams - Marketplace parameters
@@ -70,6 +78,7 @@ class ElvWalletClient {
70
78
  * @returns {Promise<ElvWalletClient>}
71
79
  */
72
80
  static async Initialize({
81
+ appId="general",
73
82
  network="main",
74
83
  mode="production",
75
84
  marketplaceParams,
@@ -86,6 +95,7 @@ class ElvWalletClient {
86
95
  const client = await ElvClient.FromNetworkName({networkName: network, assumeV3: true});
87
96
 
88
97
  const walletClient = new ElvWalletClient({
98
+ appId,
89
99
  client,
90
100
  network,
91
101
  mode,
@@ -217,6 +227,9 @@ class ElvWalletClient {
217
227
  /**
218
228
  * Direct the user to the Eluvio Media Wallet login page.
219
229
  *
230
+ * For redirect login, the authorization token will be included in the URL parameters of the callbackUrl. Simply re-initialize the wallet client and it will authorize with this token,
231
+ * or you can retrieve the parameter (`elvToken`) yourself and use it in the <a href="#Authenticate">Authenticate</a> method.
232
+ *
220
233
  * <b>NOTE:</b> The domain of the opening window (popup flow) or domain of the `callbackUrl` (redirect flow) MUST be allowed in the metadata of the specified marketplace.
221
234
  *
222
235
  * @methodGroup Login
@@ -744,20 +757,29 @@ class ElvWalletClient {
744
757
  priceRange,
745
758
  tokenIdRange,
746
759
  capLimit,
760
+ userAddress,
747
761
  sellerAddress,
748
762
  lastNDays=-1,
763
+ includeCheckoutLocked=false,
749
764
  start=0,
750
765
  limit=50
751
766
  }={}) {
752
767
  collectionIndexes = (collectionIndexes || []).map(i => parseInt(i));
753
768
 
754
769
  let params = {
755
- sort_by: sortBy,
756
- sort_descending: sortDesc,
757
770
  start,
758
- limit
771
+ limit,
772
+ sort_descending: sortDesc
759
773
  };
760
774
 
775
+ if(mode !== "leaderboard") {
776
+ params.sort_by = sortBy;
777
+ }
778
+
779
+ if(mode.includes("listings") && includeCheckoutLocked) {
780
+ params.checkout = true;
781
+ }
782
+
761
783
  let marketplaceInfo, marketplace;
762
784
  if(marketplaceParams) {
763
785
  marketplaceInfo = await this.MarketplaceInfo({marketplaceParams});
@@ -772,6 +794,8 @@ class ElvWalletClient {
772
794
 
773
795
  if(sellerAddress) {
774
796
  filters.push(`seller:eq:${this.client.utils.FormatAddress(sellerAddress)}`);
797
+ } else if(userAddress && mode !== "owned") {
798
+ filters.push(`addr:eq:${this.client.utils.FormatAddress(userAddress)}`);
775
799
  }
776
800
 
777
801
  if(marketplace && collectionIndexes.length >= 0) {
@@ -798,7 +822,7 @@ class ElvWalletClient {
798
822
  }
799
823
  });
800
824
  });
801
- } else if(mode !== "owned" && marketplaceInfo || tenantId) {
825
+ } else if(marketplaceInfo || tenantId) {
802
826
  filters.push(`tenant:eq:${marketplaceInfo ? marketplaceInfo.tenantId : tenantId}`);
803
827
  }
804
828
 
@@ -813,11 +837,10 @@ class ElvWalletClient {
813
837
  filters.push(`token:eq:${tokenId}`);
814
838
  }
815
839
  } else if(filter) {
816
- if(mode.includes("listing")) {
840
+ if(mode === "listing") {
817
841
  filters.push(`nft/display_name:eq:${filter}`);
818
842
  } else if(mode === "owned") {
819
- filters.push(`meta:@>:{"display_name":"${filter}"}`);
820
- params.exact = false;
843
+ filters.push(`meta/display_name:eq:${filter}`);
821
844
  } else {
822
845
  filters.push(`name:eq:${filter}`);
823
846
  }
@@ -880,12 +903,7 @@ class ElvWalletClient {
880
903
  let path;
881
904
  switch(mode) {
882
905
  case "owned":
883
- path = UrlJoin("as", "wlt", "nfts");
884
-
885
- if(marketplaceInfo) {
886
- path = UrlJoin("as", "wlt", "nfts", marketplaceInfo.tenantId);
887
- }
888
-
906
+ path = UrlJoin("as", "wlt", userAddress || this.UserAddress());
889
907
  break;
890
908
 
891
909
  case "listings":
@@ -912,6 +930,10 @@ class ElvWalletClient {
912
930
  path = UrlJoin("as", "mkt", "stats", "sold");
913
931
  filters.push("seller:co:0x");
914
932
  break;
933
+
934
+ case "leaderboard":
935
+ path = UrlJoin("as", "wlt", "leaders");
936
+ break;
915
937
  }
916
938
 
917
939
  if(filters.length > 0) {
@@ -932,10 +954,7 @@ class ElvWalletClient {
932
954
  await this.client.authClient.MakeAuthServiceRequest({
933
955
  path,
934
956
  method: "GET",
935
- queryParams: params,
936
- headers: mode === "owned" ?
937
- { Authorization: `Bearer ${this.AuthToken()}` } :
938
- {}
957
+ queryParams: params
939
958
  })
940
959
  ) || [];
941
960
 
@@ -946,7 +965,7 @@ class ElvWalletClient {
946
965
  total: paging.total,
947
966
  more: paging.total > start + limit
948
967
  },
949
- results: (contents || []).map(item => ["owned", "listings"].includes(mode) ? FormatNFT(item) : item)
968
+ results: (contents || []).map(item => ["owned", "listings"].includes(mode) ? FormatNFT(this, item) : item)
950
969
  };
951
970
  } catch(error) {
952
971
  if(error.status && error.status.toString() === "404") {
@@ -1029,5 +1048,6 @@ class ElvWalletClient {
1029
1048
  }
1030
1049
 
1031
1050
  Object.assign(ElvWalletClient.prototype, require("./ClientMethods"));
1051
+ Object.assign(ElvWalletClient.prototype, require("./Profile"));
1032
1052
 
1033
1053
  exports.ElvWalletClient = ElvWalletClient;