@devvit/client 0.12.6-next-2025-12-08-21-19-08-b51bf4efc.0 → 0.12.6-next-2025-12-08-21-42-15-9f008f8f0.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/client-meta.min.json +7 -13
- package/client.min.js +1 -1
- package/client.min.js.map +4 -4
- package/effects/web-view-mode.d.ts +6 -2
- package/effects/web-view-mode.d.ts.map +1 -1
- package/effects/web-view-mode.js +6 -25
- package/index.js +0 -2
- package/package.json +7 -7
package/client-meta.min.json
CHANGED
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
"format": "esm"
|
|
158
158
|
},
|
|
159
159
|
"src/effects/web-view-mode.ts": {
|
|
160
|
-
"bytes":
|
|
160
|
+
"bytes": 4795,
|
|
161
161
|
"imports": [
|
|
162
162
|
{
|
|
163
163
|
"path": "../protos/dist/json/devvit/ui/effects/web_view/v1alpha/immersive_mode.js",
|
|
@@ -183,7 +183,7 @@
|
|
|
183
183
|
"format": "esm"
|
|
184
184
|
},
|
|
185
185
|
"src/index.ts": {
|
|
186
|
-
"bytes":
|
|
186
|
+
"bytes": 409,
|
|
187
187
|
"imports": [
|
|
188
188
|
{
|
|
189
189
|
"path": "src/clientContext.ts",
|
|
@@ -224,11 +224,6 @@
|
|
|
224
224
|
"path": "../shared-types/dist/PostData.js",
|
|
225
225
|
"kind": "import-statement",
|
|
226
226
|
"original": "@devvit/shared-types/PostData.js"
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
"path": "src/effects/web-view-mode.ts",
|
|
230
|
-
"kind": "import-statement",
|
|
231
|
-
"original": "./effects/web-view-mode.js"
|
|
232
227
|
}
|
|
233
228
|
],
|
|
234
229
|
"format": "esm"
|
|
@@ -239,7 +234,7 @@
|
|
|
239
234
|
"imports": [],
|
|
240
235
|
"exports": [],
|
|
241
236
|
"inputs": {},
|
|
242
|
-
"bytes":
|
|
237
|
+
"bytes": 39630
|
|
243
238
|
},
|
|
244
239
|
"dist/client.min.js": {
|
|
245
240
|
"imports": [],
|
|
@@ -251,7 +246,6 @@
|
|
|
251
246
|
"getShareData",
|
|
252
247
|
"getWebViewMode",
|
|
253
248
|
"navigateTo",
|
|
254
|
-
"registerListener",
|
|
255
249
|
"removeWebViewModeListener",
|
|
256
250
|
"requestExpandedMode",
|
|
257
251
|
"showForm",
|
|
@@ -264,7 +258,7 @@
|
|
|
264
258
|
"bytesInOutput": 33
|
|
265
259
|
},
|
|
266
260
|
"src/index.ts": {
|
|
267
|
-
"bytesInOutput":
|
|
261
|
+
"bytesInOutput": 0
|
|
268
262
|
},
|
|
269
263
|
"../protos/dist/json/devvit/ui/effects/v1alpha/effect.js": {
|
|
270
264
|
"bytesInOutput": 595
|
|
@@ -288,7 +282,7 @@
|
|
|
288
282
|
"bytesInOutput": 25
|
|
289
283
|
},
|
|
290
284
|
"src/effects/share.ts": {
|
|
291
|
-
"bytesInOutput":
|
|
285
|
+
"bytesInOutput": 358
|
|
292
286
|
},
|
|
293
287
|
"src/effects/helpers/assert-valid-form-fields.ts": {
|
|
294
288
|
"bytesInOutput": 357
|
|
@@ -312,10 +306,10 @@
|
|
|
312
306
|
"bytesInOutput": 14
|
|
313
307
|
},
|
|
314
308
|
"src/effects/web-view-mode.ts": {
|
|
315
|
-
"bytesInOutput":
|
|
309
|
+
"bytesInOutput": 993
|
|
316
310
|
}
|
|
317
311
|
},
|
|
318
|
-
"bytes":
|
|
312
|
+
"bytes": 7036
|
|
319
313
|
}
|
|
320
314
|
}
|
|
321
315
|
}
|
package/client.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var M=globalThis.devvit?.context;var n;(function(e){e[e.EFFECT_REALTIME_SUB=0]="EFFECT_REALTIME_SUB",e[e.EFFECT_RERENDER_UI=1]="EFFECT_RERENDER_UI",e[e.EFFECT_RELOAD_PART=2]="EFFECT_RELOAD_PART",e[e.EFFECT_SHOW_FORM=3]="EFFECT_SHOW_FORM",e[e.EFFECT_SHOW_TOAST=4]="EFFECT_SHOW_TOAST",e[e.EFFECT_NAVIGATE_TO_URL=5]="EFFECT_NAVIGATE_TO_URL",e[e.EFFECT_SET_INTERVALS=7]="EFFECT_SET_INTERVALS",e[e.EFFECT_CREATE_ORDER=8]="EFFECT_CREATE_ORDER",e[e.EFFECT_WEB_VIEW=9]="EFFECT_WEB_VIEW",e[e.EFFECT_CAN_RUN_AS_USER=11]="EFFECT_CAN_RUN_AS_USER",e[e.EFFECT_TELEMETRY=12]="EFFECT_TELEMETRY",e[e.UNRECOGNIZED=-1]="UNRECOGNIZED"})(n||(n={}));var p;(function(e){e[e.CLIENT=0]="CLIENT",e[e.UNRECOGNIZED=-1]="UNRECOGNIZED"})(p||(p={}));var R="devvit-internal",S={[n.EFFECT_SHOW_FORM]:!0,[n.EFFECT_CAN_RUN_AS_USER]:!0,[n.EFFECT_CREATE_ORDER]:!0},i=e=>new Promise(t=>{let r={...e,realtimeEffect:e.realtime,scope:p.CLIENT,type:R};if((e.showToast||e.navigateToUrl||e.showForm||e.type===n.EFFECT_CAN_RUN_AS_USER)&&(r.effect=e),S[e.type]){let o=crypto.randomUUID();r.id=o;let l=d=>{d.data?.type==="devvit-message"&&d.data?.data?.id===o&&(t(d.data.data),removeEventListener("message",l))};addEventListener("message",l),parent.postMessage(r,"*")}else parent.postMessage(r,"*"),t(void 0)});function B(e){let t=typeof e=="string"?e:e.url,r;try{r=new URL(t).toString()}catch{throw new TypeError(`Invalid URL: ${t}`)}i({navigateToUrl:{url:r},type:5})}var a;(function(e){e[e.CONSENT_STATUS_UNKNOWN=0]="CONSENT_STATUS_UNKNOWN",e[e.REVOKED=1]="REVOKED",e[e.GRANTED=2]="GRANTED",e[e.UNRECOGNIZED=-1]="UNRECOGNIZED"})(a||(a={}));var F;(function(e){e[e.SCOPE_UNKNOWN=0]="SCOPE_UNKNOWN",e[e.SUBMIT_POST=1]="SUBMIT_POST",e[e.SUBMIT_COMMENT=2]="SUBMIT_COMMENT",e[e.SUBSCRIBE_TO_SUBREDDIT=3]="SUBSCRIBE_TO_SUBREDDIT",e[e.UNRECOGNIZED=-1]="UNRECOGNIZED"})(F||(F={}));var k=async()=>{let e=devvit.appPermissionState;if(!e)return!0;if(e.requestedScopes.length===0)return!1;switch(e.consentStatus){case a.REVOKED:return!1;case a.GRANTED:if(e.requestedScopes.every(r=>e.grantedScopes.includes(r)))return!0;break;case a.CONSENT_STATUS_UNKNOWN:case a.UNRECOGNIZED:break;default:{e.consentStatus;break}}return(await i({canRunAsUser:{postId:devvit.context.postId,appSlug:devvit.context.appName,subredditId:devvit.context.subredditId},type:11}))?.consentStatus?.consentStatus===a.GRANTED};var u="$devvit_icon.png";async function Q(e){if(e.data&&e.data.length>1024)throw Error(`data must be <= ${1024} characters but was ${e.data.length} characters`);let t=`${new URL(u,location.origin)}`,r;try{r=await fetch(t,{method:"HEAD"})}catch{}await i({type:9,share:{appIconUri:r?.ok?t:void 0,userData:e.data,text:e.text,title:e.title}})}function ee(){return devvit.share?.userData}function E(e,t=new Set){for(let r of e){if(r.type==="group"){E(r.fields,t);continue}let o=r.name;if(t.has(o))throw new Error(`Duplicate field name: ${o}`);t.add(o)}N(e)}function N(e){for(let t of e)if(t.type==="string"&&t.isSecret&&t.scope!=="app")throw`Invalid setting: only app settings can be secrets. Add "scope: SettingScope.App" to field "${t.name}"`}function I(e){switch(e.fieldType){case 0:return e.stringValue;case 7:return e.stringValue;case 1:return e.stringValue;case 2:return e.numberValue;case 3:return e.boolValue;case 5:return e.selectionValue?.values??[];default:return}}function f(e){return Object.keys(e).reduce((t,r)=>{let o=I(e[r]);return o!==void 0&&(t[r]=o),t},{})}function m(e){return e.map(t=>{switch(t.type){case"string":return C(t);case"image":return y(t);case"paragraph":return x(t);case"number":return h(t);case"select":return O(t);case"boolean":return g(t);case"group":return U(t);default:throw new Error("Unknown field type.")}})}function C(e){return{defaultValue:{fieldType:0,stringValue:e.defaultValue},disabled:e.disabled,fieldConfig:{stringConfig:{placeholder:e.placeholder}},fieldId:e.name,fieldType:0,helpText:e.helpText,label:e.label,required:e.required,isSecret:e.isSecret}}function y(e){return{disabled:e.disabled,fieldId:e.name,fieldType:7,helpText:e.helpText,label:e.label,required:e.required}}function x(e){return{defaultValue:{fieldType:1,stringValue:e.defaultValue},disabled:e.disabled,fieldConfig:{paragraphConfig:{lineHeight:e.lineHeight,placeholder:e.placeholder}},fieldId:e.name,fieldType:1,helpText:e.helpText,label:e.label,required:e.required}}function h(e){return{defaultValue:{fieldType:2,numberValue:e.defaultValue},disabled:e.disabled,fieldConfig:{numberConfig:{}},fieldId:e.name,fieldType:2,helpText:e.helpText,label:e.label,required:e.required}}function O(e){return{defaultValue:{fieldType:5,selectionValue:{values:e.defaultValue??[]}},disabled:e.disabled,fieldConfig:{selectionConfig:{choices:e.options,multiSelect:e.multiSelect}},fieldId:e.name,fieldType:5,helpText:e.helpText,label:e.label,required:e.required}}function g(e){return{defaultValue:{fieldType:3,boolValue:e.defaultValue},disabled:e.disabled,fieldId:e.name,fieldType:3,helpText:e.helpText,label:e.label}}function U(e){return{fieldId:"",fieldType:6,fieldConfig:{groupConfig:{fields:m(e.fields)}},label:e.label,helpText:e.helpText}}var T=1,A=()=>(T++,`form.${T}`),pe=async e=>{let t={fields:[],id:A(),title:e.title,acceptLabel:e.acceptLabel,cancelLabel:e.cancelLabel,shortDescription:e.description};E(e.fields),t.fields=m(e.fields);let r=await i({showForm:{form:t},type:3});return!r||!r.formSubmitted?{action:"CANCELED"}:{action:"SUBMITTED",values:f(r.formSubmitted.results)}};function me(e){let t;e instanceof Object?t={text:e.text,appearance:e.appearance==="success"?1:0}:t={text:e},i({showToast:{toast:t},type:4})}var s;(function(e){e[e.UNSPECIFIED=0]="UNSPECIFIED",e[e.INLINE_MODE=1]="INLINE_MODE",e[e.IMMERSIVE_MODE=2]="IMMERSIVE_MODE",e[e.UNRECOGNIZED=-1]="UNRECOGNIZED"})(s||(s={}));var _="token";var ve=()=>b(devvit.webViewMode);function Re(e,t){if(devvit.webViewMode===s.IMMERSIVE_MODE)throw Error("web view is already expanded");return v(s.IMMERSIVE_MODE,e,t)}function Se(e){if(devvit.webViewMode===s.INLINE_MODE)throw Error("web view is already inlined");return v(s.INLINE_MODE,e,void 0)}function Ne(e){}function Ie(e){}async function v(e,t,r){if(!t.isTrusted||t.type!=="click")throw console.error("Expanded mode effect ignored due to untrusted event"),new Error("Untrusted event");if(r!=null&&!devvit.entrypoints[r])throw Error(`no entrypoint named "${r}"; all entrypoints must appear in \`devvit.json\` \`post.entrypoints\``);let o;if(r){let d=new URL(devvit.entrypoints[r]);d.searchParams.set(_,devvit.token),o=`${d}`}await i({type:9,immersiveMode:{entryUrl:o,immersiveMode:e}})}function b(e){switch(e){case s.IMMERSIVE_MODE:return"expanded";case s.INLINE_MODE:case s.UNRECOGNIZED:case s.UNSPECIFIED:case void 0:return"inline";default:throw Error(`${e} not a WebViewImmersiveMode`)}}export{Ne as addWebViewModeListener,k as canRunAsUser,M as context,Se as exitExpandedMode,ee as getShareData,ve as getWebViewMode,B as navigateTo,Ie as removeWebViewModeListener,Re as requestExpandedMode,pe as showForm,Q as showShareSheet,me as showToast};
|
|
2
2
|
//# sourceMappingURL=client.min.js.map
|
package/client.min.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/clientContext.ts", "../../protos/dist/json/devvit/ui/effects/v1alpha/effect.js", "../../protos/dist/json/devvit/ui/effects/web_view/v1alpha/post_message.js", "../../shared-types/dist/client/emit-effect.js", "../src/effects/navigate-to.ts", "../../protos/dist/json/reddit/devvit/app_permission/v1/app_permission.js", "../src/effects/run-as-user.ts", "../../shared-types/dist/constants.js", "../src/effects/share.ts", "../src/effects/helpers/assert-valid-form-fields.ts", "../src/effects/helpers/get-form-values.ts", "../src/effects/helpers/transform-form.ts", "../src/effects/show-form.ts", "../src/effects/show-toast.ts", "../../protos/dist/json/devvit/ui/effects/web_view/v1alpha/immersive_mode.js", "../../shared-types/dist/webbit.js", "../src/effects/web-view-mode.ts"
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@devvit/shared-types/client/client-context.js';\n\n// Use nullish coalescing for local development.\nexport const context: Context = globalThis.devvit?.context!;\n", "/**\n * #effect.ts\n *\n * Code generated by ts-proto. DO NOT EDIT.\n * @packageDocumentation\n */\n/* eslint-disable */\nexport var EffectType;\n(function (EffectType) {\n /** EFFECT_REALTIME_SUB - Notify client to synchronize its realtime subscriptions */\n EffectType[EffectType[\"EFFECT_REALTIME_SUB\"] = 0] = \"EFFECT_REALTIME_SUB\";\n /** EFFECT_RERENDER_UI - Trigger a re-render for apps that have visible UI */\n EffectType[EffectType[\"EFFECT_RERENDER_UI\"] = 1] = \"EFFECT_RERENDER_UI\";\n /**\n * EFFECT_RELOAD_PART - Notify the client that parts of a subreddit, post, or comment should be reloaded\n *\n * @deprecated\n */\n EffectType[EffectType[\"EFFECT_RELOAD_PART\"] = 2] = \"EFFECT_RELOAD_PART\";\n /** EFFECT_SHOW_FORM - Display a user input form */\n EffectType[EffectType[\"EFFECT_SHOW_FORM\"] = 3] = \"EFFECT_SHOW_FORM\";\n /** EFFECT_SHOW_TOAST - Display a transient toast message */\n EffectType[EffectType[\"EFFECT_SHOW_TOAST\"] = 4] = \"EFFECT_SHOW_TOAST\";\n /** EFFECT_NAVIGATE_TO_URL - Notify the client to navigate to a URL */\n EffectType[EffectType[\"EFFECT_NAVIGATE_TO_URL\"] = 5] = \"EFFECT_NAVIGATE_TO_URL\";\n /** EFFECT_SET_INTERVALS - This updates the list of active timers. */\n EffectType[EffectType[\"EFFECT_SET_INTERVALS\"] = 7] = \"EFFECT_SET_INTERVALS\";\n /** EFFECT_CREATE_ORDER - This starts a purchase flow */\n EffectType[EffectType[\"EFFECT_CREATE_ORDER\"] = 8] = \"EFFECT_CREATE_ORDER\";\n /** EFFECT_WEB_VIEW - Control and communicate with WebViews */\n EffectType[EffectType[\"EFFECT_WEB_VIEW\"] = 9] = \"EFFECT_WEB_VIEW\";\n /** EFFECT_CAN_RUN_AS_USER - Check if the app can execute some actions as the user */\n EffectType[EffectType[\"EFFECT_CAN_RUN_AS_USER\"] = 11] = \"EFFECT_CAN_RUN_AS_USER\";\n /** EFFECT_TELEMETRY - Statistical data event for analytics. */\n EffectType[EffectType[\"EFFECT_TELEMETRY\"] = 12] = \"EFFECT_TELEMETRY\";\n EffectType[EffectType[\"UNRECOGNIZED\"] = -1] = \"UNRECOGNIZED\";\n})(EffectType || (EffectType = {}));\n", "/**\n * #post_message.ts\n *\n * Code generated by ts-proto. DO NOT EDIT.\n * @packageDocumentation\n */\n/* eslint-disable */\n/**\n * Blocks only.\n *\n * @deprecated\n */\nexport var WebViewInternalMessageScope;\n(function (WebViewInternalMessageScope) {\n /** CLIENT - Message is intended for the client only */\n WebViewInternalMessageScope[WebViewInternalMessageScope[\"CLIENT\"] = 0] = \"CLIENT\";\n WebViewInternalMessageScope[WebViewInternalMessageScope[\"UNRECOGNIZED\"] = -1] = \"UNRECOGNIZED\";\n})(WebViewInternalMessageScope || (WebViewInternalMessageScope = {}));\n", "import { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport { WebViewInternalMessageScope, } from '@devvit/protos/json/devvit/ui/effects/web_view/v1alpha/post_message.js';\n/** `WebViewInternalMessage.type`. */\nexport const webViewInternalMessageType = 'devvit-internal';\nconst EFFECTS_WITH_RESPONSE = {\n [EffectType.EFFECT_SHOW_FORM]: true,\n [EffectType.EFFECT_CAN_RUN_AS_USER]: true,\n [EffectType.EFFECT_CREATE_ORDER]: true,\n};\n/**\n * Emits an effect to the parent window and handles the response if required.\n *\n * @param effect - The effect to be emitted to the parent window\n * @returns A promise that resolves with the response message for effects that require\n * a response, or resolves immediately with undefined for effects that don't\n *\n * @description\n * This function handles two types of effects:\n * 1. Effects that require a response: Creates a unique ID, sets up a message listener,\n * and resolves the promise when a matching response is received\n * 2. Effects that don't require a response: Posts the message and resolves immediately\n */\nexport const emitEffect = (effect) => {\n return new Promise((resolve) => {\n const message = {\n ...effect,\n realtimeEffect: effect.realtime, // to-do: remove deprecated field.\n scope: WebViewInternalMessageScope.CLIENT,\n type: webViewInternalMessageType,\n };\n // For temporary backward compatibility, we set both `message.effect_type` above, and `effect` below\n // Once mobile clients are updated to consume the strongly typed properties above, we can remove this block\n // *Do not* add new effects here, use the strongly typed properties above\n if (effect.showToast ||\n effect.navigateToUrl ||\n effect.showForm ||\n effect.type === EffectType.EFFECT_CAN_RUN_AS_USER) {\n message.effect = effect;\n }\n // Only set message id and add a listener for effects which require a response\n if (EFFECTS_WITH_RESPONSE[effect.type]) {\n const id = crypto.randomUUID();\n message.id = id;\n const handleEffect = (event) => {\n if (event.data?.type === 'devvit-message' && event.data?.data?.id === id) {\n resolve(event.data.data);\n removeEventListener('message', handleEffect);\n }\n };\n addEventListener('message', handleEffect);\n // Post message to the parent window, handled by client web view component\n parent.postMessage(message, '*');\n }\n else {\n parent.postMessage(message, '*');\n // Resolve immediately for effects that don't expect a response.\n resolve(undefined);\n }\n });\n};\n", "import type { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport { emitEffect } from '@devvit/shared-types/client/emit-effect.js';\n\n/**\n * Navigates to a URL, subreddit, post, comment, or user.\n *\n * @param thingOrUrl - The URL, subreddit, post, comment, or user to navigate to\n */\nexport function navigateTo(thingOrUrl: string | { readonly url: string }): void {\n const inputUrl = typeof thingOrUrl === 'string' ? thingOrUrl : thingOrUrl.url;\n let normalizedUrl: string;\n try {\n normalizedUrl = new URL(inputUrl).toString();\n } catch {\n throw new TypeError(`Invalid URL: ${inputUrl}`);\n }\n void emitEffect({\n navigateToUrl: {\n url: normalizedUrl,\n },\n type: 5 satisfies EffectType.EFFECT_NAVIGATE_TO_URL,\n });\n}\n", "/**\n * #app_permission.ts\n *\n * Code generated by ts-proto. DO NOT EDIT.\n * @packageDocumentation\n */\n/* eslint-disable */\n/** This enum is used to represent the consent status of an app permission. */\nexport var ConsentStatus;\n(function (ConsentStatus) {\n /** CONSENT_STATUS_UNKNOWN - The consent status is unknown, which means it has not been set or is not applicable. */\n ConsentStatus[ConsentStatus[\"CONSENT_STATUS_UNKNOWN\"] = 0] = \"CONSENT_STATUS_UNKNOWN\";\n /** REVOKED - The user has explicitly denied consent for the app permissions. */\n ConsentStatus[ConsentStatus[\"REVOKED\"] = 1] = \"REVOKED\";\n /** GRANTED - The user has granted consent for the app permissions. */\n ConsentStatus[ConsentStatus[\"GRANTED\"] = 2] = \"GRANTED\";\n ConsentStatus[ConsentStatus[\"UNRECOGNIZED\"] = -1] = \"UNRECOGNIZED\";\n})(ConsentStatus || (ConsentStatus = {}));\n/** This enum is used to represent the scopes of permissions that can be granted to an app. */\nexport var Scope;\n(function (Scope) {\n /** SCOPE_UNKNOWN - The scope is unknown, which means it has not been set or is not applicable. */\n Scope[Scope[\"SCOPE_UNKNOWN\"] = 0] = \"SCOPE_UNKNOWN\";\n /** SUBMIT_POST - Allows the app to submit posts on behalf of the user. */\n Scope[Scope[\"SUBMIT_POST\"] = 1] = \"SUBMIT_POST\";\n /** SUBMIT_COMMENT - Allows the app to submit comments on behalf of the user. */\n Scope[Scope[\"SUBMIT_COMMENT\"] = 2] = \"SUBMIT_COMMENT\";\n /** SUBSCRIBE_TO_SUBREDDIT - Allows the app to subscribe the user to a subreddit. */\n Scope[Scope[\"SUBSCRIBE_TO_SUBREDDIT\"] = 3] = \"SUBSCRIBE_TO_SUBREDDIT\";\n Scope[Scope[\"UNRECOGNIZED\"] = -1] = \"UNRECOGNIZED\";\n})(Scope || (Scope = {}));\n", "import type { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport { ConsentStatus } from '@devvit/protos/json/reddit/devvit/app_permission/v1/app_permission.js';\nimport { emitEffect } from '@devvit/shared-types/client/emit-effect.js';\n\n/**\n * This method is used to check if the app has been granted all the requested scopes.\n *\n * @experimental\n * @returns true if the app has been granted all the requested scopes, false otherwise.\n */\nexport const canRunAsUser = async (): Promise<boolean> => {\n const appPermissionState = devvit.appPermissionState;\n\n if (!appPermissionState) {\n // Note: The app permission state will be missing when the app runs on clients that haven't implemented this feature yet.\n // \"true\" is the correct value for the time being, until we have all clients supporting this feature.\n return true;\n }\n\n if (appPermissionState.requestedScopes.length === 0) {\n // the app does not have permission to do anything as the user\n return false;\n }\n\n switch (appPermissionState.consentStatus) {\n case ConsentStatus.REVOKED:\n return false;\n case ConsentStatus.GRANTED:\n // If the app has been granted all the requested scopes, return true\n if (\n appPermissionState.requestedScopes.every((scope) =>\n appPermissionState.grantedScopes.includes(scope)\n )\n ) {\n return true;\n }\n // Otherwise, the sets of scopes do not overlap we emit the effect to request the scopes; continue\n break;\n case ConsentStatus.CONSENT_STATUS_UNKNOWN:\n case ConsentStatus.UNRECOGNIZED:\n // We don't know the status, so we'll have to ask.\n break;\n default: {\n appPermissionState.consentStatus satisfies never;\n break;\n }\n }\n\n const response = await emitEffect({\n canRunAsUser: {\n postId: devvit.context.postId,\n appSlug: devvit.context.appName,\n subredditId: devvit.context.subredditId,\n },\n type: 11 satisfies EffectType.EFFECT_CAN_RUN_AS_USER,\n });\n\n return response?.consentStatus?.consentStatus === ConsentStatus.GRANTED;\n};\n", "// to-do: move to service + CLI package.\n// Do not use in @devvit/public-api. Protos not in externalized @devvit/protos\n// barrel.\nexport const ACTOR_SRC_DIR = 'src';\nexport const ACTOR_SRC_PRIMARY_NAME = 'main';\n/** payments stuff. */\nexport const PRODUCTS_JSON_FILE = 'products.json';\n/**\n * The hashing algorithm used - pass this to `createHash`\n */\nexport const ASSET_HASHING_ALGO = 'sha256';\n/**\n * The max number of subscribers allowed in a test subreddit (ie where a user can install an uploaded, private app\n * Determines if a PRIVATE (unpublished) version of an app can be installed or playtested\n *\n * TODO: turn this off before we go live - see DX-1336\n * */\nexport const MAX_ALLOWED_SUBSCRIBER_COUNT = 200;\n/**\n * How many assets to upload at once. Sending too many at once can cause the\n * server to become overwhelmed and start erroring clients out.\n * @type {number}\n */\nexport const ASSET_UPLOAD_BATCH_SIZE = 10;\nexport const REDDIT_OAUTH_COPY_PASTE_CLIENT_ID = 'TWTsqXa53CexlrYGBWaesQ';\n/** All server endpoints must start with this prefix. */\nexport const apiPathPrefix = '/api/';\n/** All unexposed APIs must start with this prefix. */\nexport const internalPathPrefix = '/internal/';\n/** When an app's icon is present in the media assets list, it'll be with this special name. */\nexport const ICON_FILE_PATH = '$devvit_icon.png'; // Uses special characters intentionally to avoid conflicts with real asset paths\nexport const REDDIT_DISCORD_INVITE_URL = 'https://discord.gg/Cd43ExtEFS';\nexport const REDDIT_SUBREDDIT_URL = 'https://www.reddit.com/r/devvit';\n", "import type { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport { emitEffect } from '@devvit/shared-types/client/emit-effect.js';\nimport { maxShareParamUserDataChars } from '@devvit/shared-types/client/share.js';\nimport { ICON_FILE_PATH } from '@devvit/shared-types/constants.js';\n\nexport type ShareSheetOpts = {\n /** Data to share. Must be 1024 characters or less. */\n data?: string | undefined;\n /** Title of share sheet. */\n title?: string | undefined;\n /** Body text of share sheet. */\n text?: string | undefined;\n};\n\n// to-do: unit test.\n// to-do: move to web view scripts.\n/** Show the native share sheet or copy to clipboard depending on device. */\nexport async function showShareSheet(opts: Readonly<ShareSheetOpts>): Promise<void> {\n if (opts.data && opts.data.length > maxShareParamUserDataChars)\n throw Error(\n `data must be <= ${maxShareParamUserDataChars} characters but was ${opts.data.length} characters`\n );\n\n const iconURL = `${new URL(ICON_FILE_PATH, location.origin)}`;\n let iconRsp;\n try {\n iconRsp = await fetch(iconURL, { method: 'HEAD' });\n } catch {\n //\n }\n\n await emitEffect({\n type: 9 satisfies EffectType.EFFECT_WEB_VIEW,\n share: {\n appIconUri: iconRsp?.ok ? iconURL : undefined,\n userData: opts.data,\n text: opts.text,\n title: opts.title,\n },\n });\n}\n\nexport function getShareData(): string | undefined {\n return devvit.share?.userData;\n}\n", "import type { FormField, SettingScope } from './form-types.js';\n\n/**\n * Make sure that the form fields have unique names.\n *\n * This is a carbon copy of the assertValidFormFields function in the public-api package\n * We copy it here so that @devvit/client does not need to depend on public-api\n * Any changes to this function should be reflected in the public-api version\n */\nexport function assertValidFormFields(\n fields: readonly FormField[],\n seenNames: Set<string> = new Set()\n): void {\n for (const field of fields) {\n if (field.type === 'group') {\n assertValidFormFields(field.fields, seenNames);\n continue;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fieldName = (field as any).name as string;\n\n if (seenNames.has(fieldName)) {\n throw new Error(`Duplicate field name: ${fieldName}`);\n }\n\n seenNames.add(fieldName);\n }\n assertAppSecretsOnly(fields);\n}\n\nexport function assertAppSecretsOnly(fields: readonly FormField[]): void {\n for (const field of fields) {\n if (field.type === 'string' && field.isSecret && field.scope !== ('app' as SettingScope.App)) {\n throw `Invalid setting: only app settings can be secrets. Add \"scope: SettingScope.App\" to field \"${field.name}\"`;\n }\n }\n}\n", "import type { FormFieldType, FormFieldValue } from '@devvit/protos';\n\nimport type { FormValues } from './form-types.js';\n\nfunction flattenFormFieldValue(\n value: FormFieldValue\n): undefined | string | string[] | number | boolean {\n switch (value.fieldType) {\n case 0 satisfies FormFieldType.STRING:\n return value.stringValue;\n case 7 satisfies FormFieldType.IMAGE:\n // the string value is the URL\n return value.stringValue;\n case 1 satisfies FormFieldType.PARAGRAPH:\n return value.stringValue;\n case 2 satisfies FormFieldType.NUMBER:\n return value.numberValue;\n case 3 satisfies FormFieldType.BOOLEAN:\n return value.boolValue;\n case 5 satisfies FormFieldType.SELECTION:\n return value.selectionValue?.values ?? [];\n default:\n return undefined;\n }\n}\n\n// This is a carbon copy of the transformFormFields function in the public-api package\n// We copy it here so that @devvit/client does not need to depend on public-api\n// Any changes to this function should be reflected in the public-api version\nexport function getFormValues(results: { [key: string]: FormFieldValue }): FormValues {\n return Object.keys(results).reduce((acc, key) => {\n const val = flattenFormFieldValue(results[key]);\n if (val !== undefined) acc[key] = val;\n return acc;\n }, {} as FormValues);\n}\n", "import type { FormField as FormFieldProto, FormFieldType } from '@devvit/protos';\n\nimport type {\n BooleanField,\n FormField,\n FormFieldGroup,\n ImageField,\n NumberField,\n ParagraphField,\n SelectField,\n StringField,\n} from './form-types.js';\n\n// This is a carbon copy of the transformFormFields function in the public-api package\n// We copy it here so that @devvit/client does not need to depend on public-api\n// Any changes to this function should be reflected in the public-api version\nexport function transformFormFields(fields: readonly FormField[]): FormFieldProto[] {\n return fields.map((field) => {\n switch (field.type) {\n case 'string':\n return transformStringField(field);\n case 'image':\n return transformImageField(field);\n case 'paragraph':\n return transformParagraphField(field);\n case 'number':\n return transformNumberField(field);\n case 'select':\n return transformSelectField(field);\n case 'boolean':\n return transformBooleanField(field);\n case 'group':\n return transformGroupField(field);\n default:\n throw new Error('Unknown field type.');\n }\n });\n}\n\nfunction transformStringField(field: StringField): FormFieldProto {\n return {\n defaultValue: {\n fieldType: 0 satisfies FormFieldType.STRING,\n stringValue: field.defaultValue,\n },\n disabled: field.disabled,\n fieldConfig: {\n stringConfig: {\n placeholder: field.placeholder,\n },\n },\n fieldId: field.name,\n fieldType: 0 satisfies FormFieldType.STRING,\n helpText: field.helpText,\n label: field.label,\n required: field.required,\n isSecret: field.isSecret,\n };\n}\n\nfunction transformImageField(field: ImageField): FormFieldProto {\n return {\n disabled: field.disabled,\n fieldId: field.name,\n fieldType: 7 satisfies FormFieldType.IMAGE,\n helpText: field.helpText,\n label: field.label,\n required: field.required,\n };\n}\n\nfunction transformParagraphField(field: ParagraphField): FormFieldProto {\n return {\n defaultValue: {\n fieldType: 1 satisfies FormFieldType.PARAGRAPH,\n stringValue: field.defaultValue,\n },\n disabled: field.disabled,\n fieldConfig: {\n paragraphConfig: {\n lineHeight: field.lineHeight,\n placeholder: field.placeholder,\n },\n },\n fieldId: field.name,\n fieldType: 1 satisfies FormFieldType.PARAGRAPH,\n helpText: field.helpText,\n label: field.label,\n required: field.required,\n };\n}\n\nfunction transformNumberField(field: NumberField): FormFieldProto {\n return {\n defaultValue: {\n fieldType: 2 satisfies FormFieldType.NUMBER,\n numberValue: field.defaultValue,\n },\n disabled: field.disabled,\n fieldConfig: {\n numberConfig: {},\n },\n fieldId: field.name,\n fieldType: 2 satisfies FormFieldType.NUMBER,\n helpText: field.helpText,\n label: field.label,\n required: field.required,\n };\n}\n\nfunction transformSelectField(field: SelectField): FormFieldProto {\n return {\n defaultValue: {\n fieldType: 5 satisfies FormFieldType.SELECTION,\n selectionValue: {\n values: field.defaultValue ?? [],\n },\n },\n disabled: field.disabled,\n fieldConfig: {\n selectionConfig: {\n choices: field.options,\n multiSelect: field.multiSelect,\n },\n },\n fieldId: field.name,\n fieldType: 5 satisfies FormFieldType.SELECTION,\n helpText: field.helpText,\n label: field.label,\n required: field.required,\n };\n}\n\nfunction transformBooleanField(field: BooleanField): FormFieldProto {\n return {\n defaultValue: {\n fieldType: 3 satisfies FormFieldType.BOOLEAN,\n boolValue: field.defaultValue,\n },\n disabled: field.disabled,\n fieldId: field.name,\n fieldType: 3 satisfies FormFieldType.BOOLEAN,\n helpText: field.helpText,\n label: field.label,\n };\n}\n\nfunction transformGroupField(field: FormFieldGroup): FormFieldProto {\n return {\n fieldId: '',\n fieldType: 6 satisfies FormFieldType.GROUP,\n fieldConfig: {\n groupConfig: {\n fields: transformFormFields(field.fields),\n },\n },\n label: field.label,\n helpText: field.helpText,\n };\n}\n", "import type { Form as FormProto } from '@devvit/protos';\nimport type { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport { emitEffect } from '@devvit/shared-types/client/emit-effect.js';\nimport type { FormKey } from '@devvit/shared-types/useForm.js';\n\nimport { assertValidFormFields } from './helpers/assert-valid-form-fields.js';\nimport type { Form, FormToFormValues } from './helpers/form-types.js';\nimport type { FormEffectResponse } from './helpers/form-types.js';\nimport { getFormValues } from './helpers/get-form-values.js';\nimport { transformFormFields } from './helpers/transform-form.js';\n\nlet _formKey = 1;\n\nconst getNextFormKey = (): FormKey => {\n _formKey++;\n return `form.${_formKey}`;\n};\n\n/**\n * Opens a form in a modal.\n * Returns a promise that resolves with the form submission results.\n * The form can be submitted or canceled by the user.\n *\n * @param formDefinition - The form configuration\n * @returns A promise that resolves to either:\n * - An object with `action: FormAction.SUBMITTED` and the submitted form values\n * - An object with `action: FormAction.CANCELED` if the user canceled the form\n * @throws Will throw if the form fields are invalid\n */\nexport const showForm = async <const T extends Form>(\n formDefinition: T\n): Promise<FormEffectResponse<FormToFormValues<T>>> => {\n const form: FormProto = {\n fields: [],\n id: getNextFormKey(),\n title: formDefinition.title,\n acceptLabel: formDefinition.acceptLabel,\n cancelLabel: formDefinition.cancelLabel,\n shortDescription: formDefinition.description,\n };\n\n assertValidFormFields(formDefinition.fields);\n form.fields = transformFormFields(formDefinition.fields);\n\n const response = await emitEffect({\n showForm: {\n form,\n },\n type: 3 satisfies EffectType.EFFECT_SHOW_FORM,\n });\n\n if (!response || !response.formSubmitted) {\n return {\n action: 'CANCELED',\n };\n }\n\n const formResults = getFormValues(response.formSubmitted.results);\n\n return {\n action: 'SUBMITTED',\n values: formResults as FormToFormValues<T>,\n };\n};\n", "import type { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport type {\n Toast as ToastProto,\n ToastAppearance,\n} from '@devvit/protos/json/devvit/ui/toast/toast.js';\nimport type { Toast } from '@devvit/shared';\nimport { emitEffect } from '@devvit/shared-types/client/emit-effect.js';\n\n/**\n * Shows a toast message.\n *\n * @param textOrToast - The text or toast object to display\n */\nexport function showToast(text: string): void;\nexport function showToast(toast: Toast): void;\nexport function showToast(textOrToast: string | Toast): void {\n let toast: ToastProto;\n\n if (textOrToast instanceof Object) {\n toast = {\n text: textOrToast.text,\n appearance:\n textOrToast.appearance === 'success'\n ? (1 satisfies ToastAppearance.SUCCESS)\n : (0 satisfies ToastAppearance.NEUTRAL),\n };\n } else {\n toast = {\n text: textOrToast,\n };\n }\n\n void emitEffect({\n showToast: {\n toast,\n },\n type: 4 satisfies EffectType.EFFECT_SHOW_TOAST,\n });\n}\n", "/**\n * #immersive_mode.ts\n *\n * Code generated by ts-proto. DO NOT EDIT.\n * @packageDocumentation\n */\n/* eslint-disable */\n/** How a Devvit app is presented. */\nexport var WebViewImmersiveMode;\n(function (WebViewImmersiveMode) {\n WebViewImmersiveMode[WebViewImmersiveMode[\"UNSPECIFIED\"] = 0] = \"UNSPECIFIED\";\n /**\n * INLINE_MODE - Conventional post presentation. May appear adjacent other posts in a feed\n * or with comments.\n */\n WebViewImmersiveMode[WebViewImmersiveMode[\"INLINE_MODE\"] = 1] = \"INLINE_MODE\";\n /**\n * IMMERSIVE_MODE - Large modal expanded presentation. Appears popped out from the rest of the\n * Reddit experience which is disabled.\n */\n WebViewImmersiveMode[WebViewImmersiveMode[\"IMMERSIVE_MODE\"] = 2] = \"IMMERSIVE_MODE\";\n WebViewImmersiveMode[WebViewImmersiveMode[\"UNRECOGNIZED\"] = -1] = \"UNRECOGNIZED\";\n})(WebViewImmersiveMode || (WebViewImmersiveMode = {}));\n", "export const noWebbitToken = `0.0.0`;\n/**\n * `RequestContext` token query parameter name on `DevvitPost.entrypointUrl`.\n */\nexport const tokenParam = 'token';\n", "import type { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport { WebViewImmersiveMode } from '@devvit/protos/json/devvit/ui/effects/web_view/v1alpha/immersive_mode.js';\nimport type { WebViewMessageEvent_MessageData } from '@devvit/protos/json/devvit/ui/events/v1alpha/web_view.js';\nimport { emitEffect } from '@devvit/shared-types/client/emit-effect.js';\nimport { tokenParam } from '@devvit/shared-types/webbit.js';\n\n/**\n * The presentation mode of the web view.\n * 'inline' The web view is displayed inline within a feed or post detail page\n * 'expanded' The web view is displayed in a larger modal presentation\n * @experimental\n */\nexport type WebViewMode = 'inline' | 'expanded';\n\n/**\n * A listener that is called when the web view mode changes.\n * @param mode The new mode, either 'inline' or 'expanded'.\n * @experimental\n */\nexport type WebViewModeListener = (mode: WebViewMode) => void;\n\nconst modeListeners = new Set<WebViewModeListener>();\n\n/**\n * Represents the current web view mode state for the application.\n *\n * @experimental\n */\nexport const getWebViewMode = (): WebViewMode => webViewMode(devvit.webViewMode);\n\n/**\n * Requests expanded mode for the web view.\n * This will display the web view in a larger modal presentation.\n *\n * @param event The gesture that triggered the request, must be a trusted event.\n * @param entry The destination URI name. Eg, `'splash'` or `'default'`. Entry\n * names are the `devvit.json` `post.entrypoints` keys. Passing the\n * same entrypoint as currently loaded causes a reload.\n * @returns A promise that resolves request has been received.\n * @throws When already expanded.\n *\n * @experimental\n * @example\n * ```ts\n * button.addEventListener('click', async (event) => {\n * await requestExpandedMode(event);\n * });\n * ```\n */\nexport function requestExpandedMode(event: MouseEvent, entry: string): Promise<void> {\n if (devvit.webViewMode === WebViewImmersiveMode.IMMERSIVE_MODE)\n throw Error('web view is already expanded');\n return emitModeEffect(WebViewImmersiveMode.IMMERSIVE_MODE, event, entry);\n}\n\n/**\n * Exits expanded mode for the web view.\n * This will display the web view in an inline presentation.\n *\n * @param event The event that triggered the request, must be a trusted event.\n * @returns A promise that resolves request has been received.\n * @throws When already inlined.\n *\n * @experimental\n * @example\n * ```ts\n * button.addEventListener('click', async (event) => {\n * await exitExpandedMode(event);\n * });\n * ```\n */\nexport function exitExpandedMode(event: MouseEvent): Promise<void> {\n if (devvit.webViewMode === WebViewImmersiveMode.INLINE_MODE)\n throw Error('web view is already inlined');\n return emitModeEffect(WebViewImmersiveMode.INLINE_MODE, event, undefined);\n}\n\n/**\n * Adds a listener that is called when the web view mode changes. Initial mode\n * is not reported. Web views in the process of destruction may not receive a\n * mode change event.\n *\n * @param callback The callback to be called when the mode changes.\n * @experimental\n */\nexport function addWebViewModeListener(callback: WebViewModeListener): void {\n modeListeners.add(callback);\n}\n\n/**\n * Removes a listener that was previously added with `addWebViewModeListener`.\n *\n * @param callback The callback to be removed.\n * @experimental\n */\nexport function removeWebViewModeListener(callback: WebViewModeListener): void {\n modeListeners.delete(callback);\n}\n\nasync function emitModeEffect(\n mode: WebViewImmersiveMode,\n event: Event,\n entry: string | undefined\n): Promise<void> {\n if (!event.isTrusted || event.type !== 'click') {\n console.error('Expanded mode effect ignored due to untrusted event');\n throw new Error('Untrusted event');\n }\n\n if (entry != null && !devvit.entrypoints[entry])\n throw Error(\n `no entrypoint named \"${entry}\"; all entrypoints must appear in \\`devvit.json\\` \\`post.entrypoints\\``\n );\n\n let entryUrl;\n if (entry) {\n // Only `DevvitPost.entrypointUrl` has a token.\n const url = new URL(devvit.entrypoints[entry]);\n url.searchParams.set(tokenParam, devvit.token);\n entryUrl = `${url}`;\n }\n\n const type = 9 satisfies EffectType.EFFECT_WEB_VIEW;\n await emitEffect({\n type,\n immersiveMode: { entryUrl, immersiveMode: mode },\n });\n}\n\n/**\n * @internal\n * Handles incoming messages from the client, like when the user closes the immersive modal\n */\nexport function registerListener(): void {\n addEventListener('message', (event: MessageEvent<WebViewMessageEvent_MessageData>) => {\n const { type, data } = event.data;\n\n if (type !== 'devvit-message') {\n return;\n }\n if (!data?.immersiveModeEvent) {\n return;\n }\n\n devvit.webViewMode = data.immersiveModeEvent.immersiveMode;\n\n const webViewModeString = webViewMode(data.immersiveModeEvent.immersiveMode);\n modeListeners.forEach((listener) => listener(webViewModeString));\n });\n}\n\nfunction webViewMode(mode: WebViewImmersiveMode | undefined): WebViewMode {\n switch (mode) {\n case WebViewImmersiveMode.IMMERSIVE_MODE:\n return 'expanded';\n case WebViewImmersiveMode.INLINE_MODE:\n case WebViewImmersiveMode.UNRECOGNIZED:\n case WebViewImmersiveMode.UNSPECIFIED:\n case undefined:\n return 'inline';\n default:\n mode satisfies never;\n throw Error(`${mode} not a WebViewImmersiveMode`);\n }\n}\n", "export * from './clientContext.js';\nexport * from './effects/navigate-to.js';\nexport * from './effects/run-as-user.js';\nexport * from './effects/share.js';\nexport * from './effects/show-form.js';\nexport * from './effects/show-toast.js';\nexport * from './effects/web-view-mode.js';\nexport type { Context } from '@devvit/shared-types/client/client-context.js';\nexport * from '@devvit/shared-types/PostData.js';\n\nimport { registerListener } from './effects/web-view-mode.js';\nregisterListener();\n"],
|
|
5
|
-
"mappings": "AAGO,IAAMA,EAAmB,WAAW,QAAQ,QCI5C,IAAIC,GACV,SAAUA,EAAY,CAEnBA,EAAWA,EAAW,oBAAyB,CAAC,EAAI,sBAEpDA,EAAWA,EAAW,mBAAwB,CAAC,EAAI,qBAMnDA,EAAWA,EAAW,mBAAwB,CAAC,EAAI,qBAEnDA,EAAWA,EAAW,iBAAsB,CAAC,EAAI,mBAEjDA,EAAWA,EAAW,kBAAuB,CAAC,EAAI,oBAElDA,EAAWA,EAAW,uBAA4B,CAAC,EAAI,yBAEvDA,EAAWA,EAAW,qBAA0B,CAAC,EAAI,uBAErDA,EAAWA,EAAW,oBAAyB,CAAC,EAAI,sBAEpDA,EAAWA,EAAW,gBAAqB,CAAC,EAAI,kBAEhDA,EAAWA,EAAW,uBAA4B,EAAE,EAAI,yBAExDA,EAAWA,EAAW,iBAAsB,EAAE,EAAI,mBAClDA,EAAWA,EAAW,aAAkB,EAAE,EAAI,cAClD,GAAGA,IAAeA,EAAa,CAAC,EAAE,ECxB3B,IAAIC,GACV,SAAUA,EAA6B,CAEpCA,EAA4BA,EAA4B,OAAY,CAAC,EAAI,SACzEA,EAA4BA,EAA4B,aAAkB,EAAE,EAAI,cACpF,GAAGA,IAAgCA,EAA8B,CAAC,EAAE,ECd7D,IAAMC,EAA6B,kBACpCC,EAAwB,CAC1B,CAACC,EAAW,gBAAgB,EAAG,GAC/B,CAACA,EAAW,sBAAsB,EAAG,GACrC,CAACA,EAAW,mBAAmB,EAAG,EACtC,EAcaC,EAAcC,GAChB,IAAI,QAASC,GAAY,CAC5B,IAAMC,EAAU,CACZ,GAAGF,EACH,eAAgBA,EAAO,SACvB,MAAOG,EAA4B,OACnC,KAAMP,CACV,EAWA,IAPII,EAAO,WACPA,EAAO,eACPA,EAAO,UACPA,EAAO,OAASF,EAAW,0BAC3BI,EAAQ,OAASF,GAGjBH,EAAsBG,EAAO,IAAI,EAAG,CACpC,IAAMI,EAAK,OAAO,WAAW,EAC7BF,EAAQ,GAAKE,EACb,IAAMC,EAAgBC,GAAU,CACxBA,EAAM,MAAM,OAAS,kBAAoBA,EAAM,MAAM,MAAM,KAAOF,IAClEH,EAAQK,EAAM,KAAK,IAAI,EACvB,oBAAoB,UAAWD,CAAY,EAEnD,EACA,iBAAiB,UAAWA,CAAY,EAExC,OAAO,YAAYH,EAAS,GAAG,CACnC,MAEI,OAAO,YAAYA,EAAS,GAAG,EAE/BD,EAAQ,MAAS,CAEzB,CAAC,EClDE,SAASM,EAAWC,EAAqD,CAC9E,IAAMC,EAAW,OAAOD,GAAe,SAAWA,EAAaA,EAAW,IACtEE,EACJ,GAAI,CACFA,EAAgB,IAAI,IAAID,CAAQ,EAAE,SAAS,CAC7C,MAAQ,CACN,MAAM,IAAI,UAAU,gBAAgBA,CAAQ,EAAE,CAChD,CACKE,EAAW,CACd,cAAe,CACb,IAAKD,CACP,EACA,KAAM,CACR,CAAC,CACH,CCdO,IAAIE,GACV,SAAUA,EAAe,CAEtBA,EAAcA,EAAc,uBAA4B,CAAC,EAAI,yBAE7DA,EAAcA,EAAc,QAAa,CAAC,EAAI,UAE9CA,EAAcA,EAAc,QAAa,CAAC,EAAI,UAC9CA,EAAcA,EAAc,aAAkB,EAAE,EAAI,cACxD,GAAGA,IAAkBA,EAAgB,CAAC,EAAE,EAEjC,IAAIC,GACV,SAAUA,EAAO,CAEdA,EAAMA,EAAM,cAAmB,CAAC,EAAI,gBAEpCA,EAAMA,EAAM,YAAiB,CAAC,EAAI,cAElCA,EAAMA,EAAM,eAAoB,CAAC,EAAI,iBAErCA,EAAMA,EAAM,uBAA4B,CAAC,EAAI,yBAC7CA,EAAMA,EAAM,aAAkB,EAAE,EAAI,cACxC,GAAGA,IAAUA,EAAQ,CAAC,EAAE,ECpBjB,IAAMC,EAAe,SAA8B,CACxD,IAAMC,EAAqB,OAAO,mBAElC,GAAI,CAACA,EAGH,MAAO,GAGT,GAAIA,EAAmB,gBAAgB,SAAW,EAEhD,MAAO,GAGT,OAAQA,EAAmB,cAAe,CACxC,KAAKC,EAAc,QACjB,MAAO,GACT,KAAKA,EAAc,QAEjB,GACED,EAAmB,gBAAgB,MAAOE,GACxCF,EAAmB,cAAc,SAASE,CAAK,CACjD,EAEA,MAAO,GAGT,MACF,KAAKD,EAAc,uBACnB,KAAKA,EAAc,aAEjB,MACF,QAAS,CACPD,EAAmB,cACnB,KACF,CACF,CAWA,OATiB,MAAMG,EAAW,CAChC,aAAc,CACZ,OAAQ,OAAO,QAAQ,OACvB,QAAS,OAAO,QAAQ,QACxB,YAAa,OAAO,QAAQ,WAC9B,EACA,KAAM,EACR,CAAC,IAEgB,eAAe,gBAAkBF,EAAc,OAClE,EC5BO,IAAMG,EAAiB,mBCb9B,eAAsBC,
|
|
6
|
-
"names": ["context", "EffectType", "WebViewInternalMessageScope", "webViewInternalMessageType", "EFFECTS_WITH_RESPONSE", "EffectType", "emitEffect", "effect", "resolve", "message", "WebViewInternalMessageScope", "id", "handleEffect", "event", "navigateTo", "thingOrUrl", "inputUrl", "normalizedUrl", "emitEffect", "ConsentStatus", "Scope", "canRunAsUser", "appPermissionState", "ConsentStatus", "scope", "emitEffect", "ICON_FILE_PATH", "showShareSheet", "opts", "iconURL", "ICON_FILE_PATH", "iconRsp", "emitEffect", "getShareData", "assertValidFormFields", "fields", "seenNames", "field", "fieldName", "assertAppSecretsOnly", "flattenFormFieldValue", "value", "getFormValues", "results", "acc", "key", "val", "transformFormFields", "fields", "field", "transformStringField", "transformImageField", "transformParagraphField", "transformNumberField", "transformSelectField", "transformBooleanField", "transformGroupField", "_formKey", "getNextFormKey", "showForm", "formDefinition", "form", "assertValidFormFields", "transformFormFields", "response", "emitEffect", "getFormValues", "showToast", "textOrToast", "toast", "emitEffect", "WebViewImmersiveMode", "tokenParam", "
|
|
3
|
+
"sources": ["../src/clientContext.ts", "../../protos/dist/json/devvit/ui/effects/v1alpha/effect.js", "../../protos/dist/json/devvit/ui/effects/web_view/v1alpha/post_message.js", "../../shared-types/dist/client/emit-effect.js", "../src/effects/navigate-to.ts", "../../protos/dist/json/reddit/devvit/app_permission/v1/app_permission.js", "../src/effects/run-as-user.ts", "../../shared-types/dist/constants.js", "../src/effects/share.ts", "../src/effects/helpers/assert-valid-form-fields.ts", "../src/effects/helpers/get-form-values.ts", "../src/effects/helpers/transform-form.ts", "../src/effects/show-form.ts", "../src/effects/show-toast.ts", "../../protos/dist/json/devvit/ui/effects/web_view/v1alpha/immersive_mode.js", "../../shared-types/dist/webbit.js", "../src/effects/web-view-mode.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@devvit/shared-types/client/client-context.js';\n\n// Use nullish coalescing for local development.\nexport const context: Context = globalThis.devvit?.context!;\n", "/**\n * #effect.ts\n *\n * Code generated by ts-proto. DO NOT EDIT.\n * @packageDocumentation\n */\n/* eslint-disable */\nexport var EffectType;\n(function (EffectType) {\n /** EFFECT_REALTIME_SUB - Notify client to synchronize its realtime subscriptions */\n EffectType[EffectType[\"EFFECT_REALTIME_SUB\"] = 0] = \"EFFECT_REALTIME_SUB\";\n /** EFFECT_RERENDER_UI - Trigger a re-render for apps that have visible UI */\n EffectType[EffectType[\"EFFECT_RERENDER_UI\"] = 1] = \"EFFECT_RERENDER_UI\";\n /**\n * EFFECT_RELOAD_PART - Notify the client that parts of a subreddit, post, or comment should be reloaded\n *\n * @deprecated\n */\n EffectType[EffectType[\"EFFECT_RELOAD_PART\"] = 2] = \"EFFECT_RELOAD_PART\";\n /** EFFECT_SHOW_FORM - Display a user input form */\n EffectType[EffectType[\"EFFECT_SHOW_FORM\"] = 3] = \"EFFECT_SHOW_FORM\";\n /** EFFECT_SHOW_TOAST - Display a transient toast message */\n EffectType[EffectType[\"EFFECT_SHOW_TOAST\"] = 4] = \"EFFECT_SHOW_TOAST\";\n /** EFFECT_NAVIGATE_TO_URL - Notify the client to navigate to a URL */\n EffectType[EffectType[\"EFFECT_NAVIGATE_TO_URL\"] = 5] = \"EFFECT_NAVIGATE_TO_URL\";\n /** EFFECT_SET_INTERVALS - This updates the list of active timers. */\n EffectType[EffectType[\"EFFECT_SET_INTERVALS\"] = 7] = \"EFFECT_SET_INTERVALS\";\n /** EFFECT_CREATE_ORDER - This starts a purchase flow */\n EffectType[EffectType[\"EFFECT_CREATE_ORDER\"] = 8] = \"EFFECT_CREATE_ORDER\";\n /** EFFECT_WEB_VIEW - Control and communicate with WebViews */\n EffectType[EffectType[\"EFFECT_WEB_VIEW\"] = 9] = \"EFFECT_WEB_VIEW\";\n /** EFFECT_CAN_RUN_AS_USER - Check if the app can execute some actions as the user */\n EffectType[EffectType[\"EFFECT_CAN_RUN_AS_USER\"] = 11] = \"EFFECT_CAN_RUN_AS_USER\";\n /** EFFECT_TELEMETRY - Statistical data event for analytics. */\n EffectType[EffectType[\"EFFECT_TELEMETRY\"] = 12] = \"EFFECT_TELEMETRY\";\n EffectType[EffectType[\"UNRECOGNIZED\"] = -1] = \"UNRECOGNIZED\";\n})(EffectType || (EffectType = {}));\n", "/**\n * #post_message.ts\n *\n * Code generated by ts-proto. DO NOT EDIT.\n * @packageDocumentation\n */\n/* eslint-disable */\n/**\n * Blocks only.\n *\n * @deprecated\n */\nexport var WebViewInternalMessageScope;\n(function (WebViewInternalMessageScope) {\n /** CLIENT - Message is intended for the client only */\n WebViewInternalMessageScope[WebViewInternalMessageScope[\"CLIENT\"] = 0] = \"CLIENT\";\n WebViewInternalMessageScope[WebViewInternalMessageScope[\"UNRECOGNIZED\"] = -1] = \"UNRECOGNIZED\";\n})(WebViewInternalMessageScope || (WebViewInternalMessageScope = {}));\n", "import { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport { WebViewInternalMessageScope, } from '@devvit/protos/json/devvit/ui/effects/web_view/v1alpha/post_message.js';\n/** `WebViewInternalMessage.type`. */\nexport const webViewInternalMessageType = 'devvit-internal';\nconst EFFECTS_WITH_RESPONSE = {\n [EffectType.EFFECT_SHOW_FORM]: true,\n [EffectType.EFFECT_CAN_RUN_AS_USER]: true,\n [EffectType.EFFECT_CREATE_ORDER]: true,\n};\n/**\n * Emits an effect to the parent window and handles the response if required.\n *\n * @param effect - The effect to be emitted to the parent window\n * @returns A promise that resolves with the response message for effects that require\n * a response, or resolves immediately with undefined for effects that don't\n *\n * @description\n * This function handles two types of effects:\n * 1. Effects that require a response: Creates a unique ID, sets up a message listener,\n * and resolves the promise when a matching response is received\n * 2. Effects that don't require a response: Posts the message and resolves immediately\n */\nexport const emitEffect = (effect) => {\n return new Promise((resolve) => {\n const message = {\n ...effect,\n realtimeEffect: effect.realtime, // to-do: remove deprecated field.\n scope: WebViewInternalMessageScope.CLIENT,\n type: webViewInternalMessageType,\n };\n // For temporary backward compatibility, we set both `message.effect_type` above, and `effect` below\n // Once mobile clients are updated to consume the strongly typed properties above, we can remove this block\n // *Do not* add new effects here, use the strongly typed properties above\n if (effect.showToast ||\n effect.navigateToUrl ||\n effect.showForm ||\n effect.type === EffectType.EFFECT_CAN_RUN_AS_USER) {\n message.effect = effect;\n }\n // Only set message id and add a listener for effects which require a response\n if (EFFECTS_WITH_RESPONSE[effect.type]) {\n const id = crypto.randomUUID();\n message.id = id;\n const handleEffect = (event) => {\n if (event.data?.type === 'devvit-message' && event.data?.data?.id === id) {\n resolve(event.data.data);\n removeEventListener('message', handleEffect);\n }\n };\n addEventListener('message', handleEffect);\n // Post message to the parent window, handled by client web view component\n parent.postMessage(message, '*');\n }\n else {\n parent.postMessage(message, '*');\n // Resolve immediately for effects that don't expect a response.\n resolve(undefined);\n }\n });\n};\n", "import type { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport { emitEffect } from '@devvit/shared-types/client/emit-effect.js';\n\n/**\n * Navigates to a URL, subreddit, post, comment, or user.\n *\n * @param thingOrUrl - The URL, subreddit, post, comment, or user to navigate to\n */\nexport function navigateTo(thingOrUrl: string | { readonly url: string }): void {\n const inputUrl = typeof thingOrUrl === 'string' ? thingOrUrl : thingOrUrl.url;\n let normalizedUrl: string;\n try {\n normalizedUrl = new URL(inputUrl).toString();\n } catch {\n throw new TypeError(`Invalid URL: ${inputUrl}`);\n }\n void emitEffect({\n navigateToUrl: {\n url: normalizedUrl,\n },\n type: 5 satisfies EffectType.EFFECT_NAVIGATE_TO_URL,\n });\n}\n", "/**\n * #app_permission.ts\n *\n * Code generated by ts-proto. DO NOT EDIT.\n * @packageDocumentation\n */\n/* eslint-disable */\n/** This enum is used to represent the consent status of an app permission. */\nexport var ConsentStatus;\n(function (ConsentStatus) {\n /** CONSENT_STATUS_UNKNOWN - The consent status is unknown, which means it has not been set or is not applicable. */\n ConsentStatus[ConsentStatus[\"CONSENT_STATUS_UNKNOWN\"] = 0] = \"CONSENT_STATUS_UNKNOWN\";\n /** REVOKED - The user has explicitly denied consent for the app permissions. */\n ConsentStatus[ConsentStatus[\"REVOKED\"] = 1] = \"REVOKED\";\n /** GRANTED - The user has granted consent for the app permissions. */\n ConsentStatus[ConsentStatus[\"GRANTED\"] = 2] = \"GRANTED\";\n ConsentStatus[ConsentStatus[\"UNRECOGNIZED\"] = -1] = \"UNRECOGNIZED\";\n})(ConsentStatus || (ConsentStatus = {}));\n/** This enum is used to represent the scopes of permissions that can be granted to an app. */\nexport var Scope;\n(function (Scope) {\n /** SCOPE_UNKNOWN - The scope is unknown, which means it has not been set or is not applicable. */\n Scope[Scope[\"SCOPE_UNKNOWN\"] = 0] = \"SCOPE_UNKNOWN\";\n /** SUBMIT_POST - Allows the app to submit posts on behalf of the user. */\n Scope[Scope[\"SUBMIT_POST\"] = 1] = \"SUBMIT_POST\";\n /** SUBMIT_COMMENT - Allows the app to submit comments on behalf of the user. */\n Scope[Scope[\"SUBMIT_COMMENT\"] = 2] = \"SUBMIT_COMMENT\";\n /** SUBSCRIBE_TO_SUBREDDIT - Allows the app to subscribe the user to a subreddit. */\n Scope[Scope[\"SUBSCRIBE_TO_SUBREDDIT\"] = 3] = \"SUBSCRIBE_TO_SUBREDDIT\";\n Scope[Scope[\"UNRECOGNIZED\"] = -1] = \"UNRECOGNIZED\";\n})(Scope || (Scope = {}));\n", "import type { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport { ConsentStatus } from '@devvit/protos/json/reddit/devvit/app_permission/v1/app_permission.js';\nimport { emitEffect } from '@devvit/shared-types/client/emit-effect.js';\n\n/**\n * This method is used to check if the app has been granted all the requested scopes.\n *\n * @experimental\n * @returns true if the app has been granted all the requested scopes, false otherwise.\n */\nexport const canRunAsUser = async (): Promise<boolean> => {\n const appPermissionState = devvit.appPermissionState;\n\n if (!appPermissionState) {\n // Note: The app permission state will be missing when the app runs on clients that haven't implemented this feature yet.\n // \"true\" is the correct value for the time being, until we have all clients supporting this feature.\n return true;\n }\n\n if (appPermissionState.requestedScopes.length === 0) {\n // the app does not have permission to do anything as the user\n return false;\n }\n\n switch (appPermissionState.consentStatus) {\n case ConsentStatus.REVOKED:\n return false;\n case ConsentStatus.GRANTED:\n // If the app has been granted all the requested scopes, return true\n if (\n appPermissionState.requestedScopes.every((scope) =>\n appPermissionState.grantedScopes.includes(scope)\n )\n ) {\n return true;\n }\n // Otherwise, the sets of scopes do not overlap we emit the effect to request the scopes; continue\n break;\n case ConsentStatus.CONSENT_STATUS_UNKNOWN:\n case ConsentStatus.UNRECOGNIZED:\n // We don't know the status, so we'll have to ask.\n break;\n default: {\n appPermissionState.consentStatus satisfies never;\n break;\n }\n }\n\n const response = await emitEffect({\n canRunAsUser: {\n postId: devvit.context.postId,\n appSlug: devvit.context.appName,\n subredditId: devvit.context.subredditId,\n },\n type: 11 satisfies EffectType.EFFECT_CAN_RUN_AS_USER,\n });\n\n return response?.consentStatus?.consentStatus === ConsentStatus.GRANTED;\n};\n", "// to-do: move to service + CLI package.\n// Do not use in @devvit/public-api. Protos not in externalized @devvit/protos\n// barrel.\nexport const ACTOR_SRC_DIR = 'src';\nexport const ACTOR_SRC_PRIMARY_NAME = 'main';\n/** payments stuff. */\nexport const PRODUCTS_JSON_FILE = 'products.json';\n/**\n * The hashing algorithm used - pass this to `createHash`\n */\nexport const ASSET_HASHING_ALGO = 'sha256';\n/**\n * The max number of subscribers allowed in a test subreddit (ie where a user can install an uploaded, private app\n * Determines if a PRIVATE (unpublished) version of an app can be installed or playtested\n *\n * TODO: turn this off before we go live - see DX-1336\n * */\nexport const MAX_ALLOWED_SUBSCRIBER_COUNT = 200;\n/**\n * How many assets to upload at once. Sending too many at once can cause the\n * server to become overwhelmed and start erroring clients out.\n * @type {number}\n */\nexport const ASSET_UPLOAD_BATCH_SIZE = 10;\nexport const REDDIT_OAUTH_COPY_PASTE_CLIENT_ID = 'TWTsqXa53CexlrYGBWaesQ';\n/** All server endpoints must start with this prefix. */\nexport const apiPathPrefix = '/api/';\n/** All unexposed APIs must start with this prefix. */\nexport const internalPathPrefix = '/internal/';\n/** When an app's icon is present in the media assets list, it'll be with this special name. */\nexport const ICON_FILE_PATH = '$devvit_icon.png'; // Uses special characters intentionally to avoid conflicts with real asset paths\nexport const REDDIT_DISCORD_INVITE_URL = 'https://discord.gg/Cd43ExtEFS';\nexport const REDDIT_SUBREDDIT_URL = 'https://www.reddit.com/r/devvit';\n", "import type { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport { emitEffect } from '@devvit/shared-types/client/emit-effect.js';\nimport { maxShareParamUserDataChars } from '@devvit/shared-types/client/share.js';\nimport { ICON_FILE_PATH } from '@devvit/shared-types/constants.js';\n\nexport type ShareSheetOpts = {\n /** Data to share. Must be 1024 characters or less. */\n data?: string | undefined;\n /** Title of share sheet. */\n title?: string | undefined;\n /** Body text of share sheet. */\n text?: string | undefined;\n};\n\n// to-do: unit test.\n// to-do: move to web view scripts.\n/** Show the native share sheet or copy to clipboard depending on device. */\nexport async function showShareSheet(opts: Readonly<ShareSheetOpts>): Promise<void> {\n if (opts.data && opts.data.length > maxShareParamUserDataChars)\n throw Error(\n `data must be <= ${maxShareParamUserDataChars} characters but was ${opts.data.length} characters`\n );\n\n const iconURL = `${new URL(ICON_FILE_PATH, location.origin)}`;\n let iconRsp;\n try {\n iconRsp = await fetch(iconURL, { method: 'HEAD' });\n } catch {\n //\n }\n\n await emitEffect({\n type: 9 satisfies EffectType.EFFECT_WEB_VIEW,\n share: {\n appIconUri: iconRsp?.ok ? iconURL : undefined,\n userData: opts.data,\n text: opts.text,\n title: opts.title,\n },\n });\n}\n\nexport function getShareData(): string | undefined {\n return devvit.share?.userData;\n}\n", "import type { FormField, SettingScope } from './form-types.js';\n\n/**\n * Make sure that the form fields have unique names.\n *\n * This is a carbon copy of the assertValidFormFields function in the public-api package\n * We copy it here so that @devvit/client does not need to depend on public-api\n * Any changes to this function should be reflected in the public-api version\n */\nexport function assertValidFormFields(\n fields: readonly FormField[],\n seenNames: Set<string> = new Set()\n): void {\n for (const field of fields) {\n if (field.type === 'group') {\n assertValidFormFields(field.fields, seenNames);\n continue;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fieldName = (field as any).name as string;\n\n if (seenNames.has(fieldName)) {\n throw new Error(`Duplicate field name: ${fieldName}`);\n }\n\n seenNames.add(fieldName);\n }\n assertAppSecretsOnly(fields);\n}\n\nexport function assertAppSecretsOnly(fields: readonly FormField[]): void {\n for (const field of fields) {\n if (field.type === 'string' && field.isSecret && field.scope !== ('app' as SettingScope.App)) {\n throw `Invalid setting: only app settings can be secrets. Add \"scope: SettingScope.App\" to field \"${field.name}\"`;\n }\n }\n}\n", "import type { FormFieldType, FormFieldValue } from '@devvit/protos';\n\nimport type { FormValues } from './form-types.js';\n\nfunction flattenFormFieldValue(\n value: FormFieldValue\n): undefined | string | string[] | number | boolean {\n switch (value.fieldType) {\n case 0 satisfies FormFieldType.STRING:\n return value.stringValue;\n case 7 satisfies FormFieldType.IMAGE:\n // the string value is the URL\n return value.stringValue;\n case 1 satisfies FormFieldType.PARAGRAPH:\n return value.stringValue;\n case 2 satisfies FormFieldType.NUMBER:\n return value.numberValue;\n case 3 satisfies FormFieldType.BOOLEAN:\n return value.boolValue;\n case 5 satisfies FormFieldType.SELECTION:\n return value.selectionValue?.values ?? [];\n default:\n return undefined;\n }\n}\n\n// This is a carbon copy of the transformFormFields function in the public-api package\n// We copy it here so that @devvit/client does not need to depend on public-api\n// Any changes to this function should be reflected in the public-api version\nexport function getFormValues(results: { [key: string]: FormFieldValue }): FormValues {\n return Object.keys(results).reduce((acc, key) => {\n const val = flattenFormFieldValue(results[key]);\n if (val !== undefined) acc[key] = val;\n return acc;\n }, {} as FormValues);\n}\n", "import type { FormField as FormFieldProto, FormFieldType } from '@devvit/protos';\n\nimport type {\n BooleanField,\n FormField,\n FormFieldGroup,\n ImageField,\n NumberField,\n ParagraphField,\n SelectField,\n StringField,\n} from './form-types.js';\n\n// This is a carbon copy of the transformFormFields function in the public-api package\n// We copy it here so that @devvit/client does not need to depend on public-api\n// Any changes to this function should be reflected in the public-api version\nexport function transformFormFields(fields: readonly FormField[]): FormFieldProto[] {\n return fields.map((field) => {\n switch (field.type) {\n case 'string':\n return transformStringField(field);\n case 'image':\n return transformImageField(field);\n case 'paragraph':\n return transformParagraphField(field);\n case 'number':\n return transformNumberField(field);\n case 'select':\n return transformSelectField(field);\n case 'boolean':\n return transformBooleanField(field);\n case 'group':\n return transformGroupField(field);\n default:\n throw new Error('Unknown field type.');\n }\n });\n}\n\nfunction transformStringField(field: StringField): FormFieldProto {\n return {\n defaultValue: {\n fieldType: 0 satisfies FormFieldType.STRING,\n stringValue: field.defaultValue,\n },\n disabled: field.disabled,\n fieldConfig: {\n stringConfig: {\n placeholder: field.placeholder,\n },\n },\n fieldId: field.name,\n fieldType: 0 satisfies FormFieldType.STRING,\n helpText: field.helpText,\n label: field.label,\n required: field.required,\n isSecret: field.isSecret,\n };\n}\n\nfunction transformImageField(field: ImageField): FormFieldProto {\n return {\n disabled: field.disabled,\n fieldId: field.name,\n fieldType: 7 satisfies FormFieldType.IMAGE,\n helpText: field.helpText,\n label: field.label,\n required: field.required,\n };\n}\n\nfunction transformParagraphField(field: ParagraphField): FormFieldProto {\n return {\n defaultValue: {\n fieldType: 1 satisfies FormFieldType.PARAGRAPH,\n stringValue: field.defaultValue,\n },\n disabled: field.disabled,\n fieldConfig: {\n paragraphConfig: {\n lineHeight: field.lineHeight,\n placeholder: field.placeholder,\n },\n },\n fieldId: field.name,\n fieldType: 1 satisfies FormFieldType.PARAGRAPH,\n helpText: field.helpText,\n label: field.label,\n required: field.required,\n };\n}\n\nfunction transformNumberField(field: NumberField): FormFieldProto {\n return {\n defaultValue: {\n fieldType: 2 satisfies FormFieldType.NUMBER,\n numberValue: field.defaultValue,\n },\n disabled: field.disabled,\n fieldConfig: {\n numberConfig: {},\n },\n fieldId: field.name,\n fieldType: 2 satisfies FormFieldType.NUMBER,\n helpText: field.helpText,\n label: field.label,\n required: field.required,\n };\n}\n\nfunction transformSelectField(field: SelectField): FormFieldProto {\n return {\n defaultValue: {\n fieldType: 5 satisfies FormFieldType.SELECTION,\n selectionValue: {\n values: field.defaultValue ?? [],\n },\n },\n disabled: field.disabled,\n fieldConfig: {\n selectionConfig: {\n choices: field.options,\n multiSelect: field.multiSelect,\n },\n },\n fieldId: field.name,\n fieldType: 5 satisfies FormFieldType.SELECTION,\n helpText: field.helpText,\n label: field.label,\n required: field.required,\n };\n}\n\nfunction transformBooleanField(field: BooleanField): FormFieldProto {\n return {\n defaultValue: {\n fieldType: 3 satisfies FormFieldType.BOOLEAN,\n boolValue: field.defaultValue,\n },\n disabled: field.disabled,\n fieldId: field.name,\n fieldType: 3 satisfies FormFieldType.BOOLEAN,\n helpText: field.helpText,\n label: field.label,\n };\n}\n\nfunction transformGroupField(field: FormFieldGroup): FormFieldProto {\n return {\n fieldId: '',\n fieldType: 6 satisfies FormFieldType.GROUP,\n fieldConfig: {\n groupConfig: {\n fields: transformFormFields(field.fields),\n },\n },\n label: field.label,\n helpText: field.helpText,\n };\n}\n", "import type { Form as FormProto } from '@devvit/protos';\nimport type { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport { emitEffect } from '@devvit/shared-types/client/emit-effect.js';\nimport type { FormKey } from '@devvit/shared-types/useForm.js';\n\nimport { assertValidFormFields } from './helpers/assert-valid-form-fields.js';\nimport type { Form, FormToFormValues } from './helpers/form-types.js';\nimport type { FormEffectResponse } from './helpers/form-types.js';\nimport { getFormValues } from './helpers/get-form-values.js';\nimport { transformFormFields } from './helpers/transform-form.js';\n\nlet _formKey = 1;\n\nconst getNextFormKey = (): FormKey => {\n _formKey++;\n return `form.${_formKey}`;\n};\n\n/**\n * Opens a form in a modal.\n * Returns a promise that resolves with the form submission results.\n * The form can be submitted or canceled by the user.\n *\n * @param formDefinition - The form configuration\n * @returns A promise that resolves to either:\n * - An object with `action: FormAction.SUBMITTED` and the submitted form values\n * - An object with `action: FormAction.CANCELED` if the user canceled the form\n * @throws Will throw if the form fields are invalid\n */\nexport const showForm = async <const T extends Form>(\n formDefinition: T\n): Promise<FormEffectResponse<FormToFormValues<T>>> => {\n const form: FormProto = {\n fields: [],\n id: getNextFormKey(),\n title: formDefinition.title,\n acceptLabel: formDefinition.acceptLabel,\n cancelLabel: formDefinition.cancelLabel,\n shortDescription: formDefinition.description,\n };\n\n assertValidFormFields(formDefinition.fields);\n form.fields = transformFormFields(formDefinition.fields);\n\n const response = await emitEffect({\n showForm: {\n form,\n },\n type: 3 satisfies EffectType.EFFECT_SHOW_FORM,\n });\n\n if (!response || !response.formSubmitted) {\n return {\n action: 'CANCELED',\n };\n }\n\n const formResults = getFormValues(response.formSubmitted.results);\n\n return {\n action: 'SUBMITTED',\n values: formResults as FormToFormValues<T>,\n };\n};\n", "import type { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport type {\n Toast as ToastProto,\n ToastAppearance,\n} from '@devvit/protos/json/devvit/ui/toast/toast.js';\nimport type { Toast } from '@devvit/shared';\nimport { emitEffect } from '@devvit/shared-types/client/emit-effect.js';\n\n/**\n * Shows a toast message.\n *\n * @param textOrToast - The text or toast object to display\n */\nexport function showToast(text: string): void;\nexport function showToast(toast: Toast): void;\nexport function showToast(textOrToast: string | Toast): void {\n let toast: ToastProto;\n\n if (textOrToast instanceof Object) {\n toast = {\n text: textOrToast.text,\n appearance:\n textOrToast.appearance === 'success'\n ? (1 satisfies ToastAppearance.SUCCESS)\n : (0 satisfies ToastAppearance.NEUTRAL),\n };\n } else {\n toast = {\n text: textOrToast,\n };\n }\n\n void emitEffect({\n showToast: {\n toast,\n },\n type: 4 satisfies EffectType.EFFECT_SHOW_TOAST,\n });\n}\n", "/**\n * #immersive_mode.ts\n *\n * Code generated by ts-proto. DO NOT EDIT.\n * @packageDocumentation\n */\n/* eslint-disable */\n/** How a Devvit app is presented. */\nexport var WebViewImmersiveMode;\n(function (WebViewImmersiveMode) {\n WebViewImmersiveMode[WebViewImmersiveMode[\"UNSPECIFIED\"] = 0] = \"UNSPECIFIED\";\n /**\n * INLINE_MODE - Conventional post presentation. May appear adjacent other posts in a feed\n * or with comments.\n */\n WebViewImmersiveMode[WebViewImmersiveMode[\"INLINE_MODE\"] = 1] = \"INLINE_MODE\";\n /**\n * IMMERSIVE_MODE - Large modal expanded presentation. Appears popped out from the rest of the\n * Reddit experience which is disabled.\n */\n WebViewImmersiveMode[WebViewImmersiveMode[\"IMMERSIVE_MODE\"] = 2] = \"IMMERSIVE_MODE\";\n WebViewImmersiveMode[WebViewImmersiveMode[\"UNRECOGNIZED\"] = -1] = \"UNRECOGNIZED\";\n})(WebViewImmersiveMode || (WebViewImmersiveMode = {}));\n", "export const noWebbitToken = `0.0.0`;\n/**\n * `RequestContext` token query parameter name on `DevvitPost.entrypointUrl`.\n */\nexport const tokenParam = 'token';\n", "import type { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect.js';\nimport { WebViewImmersiveMode } from '@devvit/protos/json/devvit/ui/effects/web_view/v1alpha/immersive_mode.js';\nimport { emitEffect } from '@devvit/shared-types/client/emit-effect.js';\nimport { tokenParam } from '@devvit/shared-types/webbit.js';\n\n/**\n * The presentation mode of the web view.\n * 'inline' The web view is displayed inline within a feed or post detail page\n * 'expanded' The web view is displayed in a larger modal presentation\n * @experimental\n */\nexport type WebViewMode = 'inline' | 'expanded';\n\n/**\n * A listener that is called when the web view mode changes.\n * @param mode The new mode, either 'inline' or 'expanded'.\n * @experimental\n */\nexport type WebViewModeListener = (mode: WebViewMode) => void;\n\n/**\n * Represents the current web view mode state for the application.\n *\n * @experimental\n */\nexport const getWebViewMode = (): WebViewMode => webViewMode(devvit.webViewMode);\n\n/**\n * Requests expanded mode for the web view.\n * This will display the web view in a larger modal presentation.\n *\n * @param event The gesture that triggered the request, must be a trusted event.\n * @param entry The destination URI name. Eg, `'splash'` or `'default'`. Entry\n * names are the `devvit.json` `post.entrypoints` keys. Passing the\n * same entrypoint as currently loaded causes a reload.\n * @returns A promise that resolves request has been received.\n * @throws When already expanded.\n *\n * @experimental\n * @example\n * ```ts\n * button.addEventListener('click', async (event) => {\n * await requestExpandedMode(event);\n * });\n * ```\n */\nexport function requestExpandedMode(event: MouseEvent, entry: string): Promise<void> {\n if (devvit.webViewMode === WebViewImmersiveMode.IMMERSIVE_MODE)\n throw Error('web view is already expanded');\n return emitModeEffect(WebViewImmersiveMode.IMMERSIVE_MODE, event, entry);\n}\n\n/**\n * Exits expanded mode for the web view.\n * This will display the web view in an inline presentation.\n *\n * @param event The event that triggered the request, must be a trusted event.\n * @returns A promise that resolves request has been received.\n * @throws When already inlined.\n *\n * @experimental\n * @example\n * ```ts\n * button.addEventListener('click', async (event) => {\n * await exitExpandedMode(event);\n * });\n * ```\n */\nexport function exitExpandedMode(event: MouseEvent): Promise<void> {\n if (devvit.webViewMode === WebViewImmersiveMode.INLINE_MODE)\n throw Error('web view is already inlined');\n return emitModeEffect(WebViewImmersiveMode.INLINE_MODE, event, undefined);\n}\n\n/**\n * @deprecated This API is deprecated and will be removed in a future release.\n * use window.addEventListener(\"focus\", () => { }) to receive notifications\n * when the web view mode changes back to inline.\n * Adds a listener that is called when the web view mode changes. Initial mode\n * is not reported. Web views in the process of destruction may not receive a\n * mode change event.\n *\n * @param callback The callback to be called when the mode changes.\n * @experimental\n */\nexport function addWebViewModeListener(_: WebViewModeListener): void {}\n\n/**\n * @deprecated This API is deprecated and will be removed in a future release.\n * Removes a listener that was previously added with `addWebViewModeListener`.\n *\n * @param callback The callback to be removed.\n * @experimental\n */\nexport function removeWebViewModeListener(_: WebViewModeListener): void {}\n\nasync function emitModeEffect(\n mode: WebViewImmersiveMode,\n event: Event,\n entry: string | undefined\n): Promise<void> {\n if (!event.isTrusted || event.type !== 'click') {\n console.error('Expanded mode effect ignored due to untrusted event');\n throw new Error('Untrusted event');\n }\n\n if (entry != null && !devvit.entrypoints[entry])\n throw Error(\n `no entrypoint named \"${entry}\"; all entrypoints must appear in \\`devvit.json\\` \\`post.entrypoints\\``\n );\n\n let entryUrl;\n if (entry) {\n // Only `DevvitPost.entrypointUrl` has a token.\n const url = new URL(devvit.entrypoints[entry]);\n url.searchParams.set(tokenParam, devvit.token);\n entryUrl = `${url}`;\n }\n\n const type = 9 satisfies EffectType.EFFECT_WEB_VIEW;\n await emitEffect({\n type,\n immersiveMode: { entryUrl, immersiveMode: mode },\n });\n}\n\nfunction webViewMode(mode: WebViewImmersiveMode | undefined): WebViewMode {\n switch (mode) {\n case WebViewImmersiveMode.IMMERSIVE_MODE:\n return 'expanded';\n case WebViewImmersiveMode.INLINE_MODE:\n case WebViewImmersiveMode.UNRECOGNIZED:\n case WebViewImmersiveMode.UNSPECIFIED:\n case undefined:\n return 'inline';\n default:\n mode satisfies never;\n throw Error(`${mode} not a WebViewImmersiveMode`);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["context", "EffectType", "WebViewInternalMessageScope", "webViewInternalMessageType", "EFFECTS_WITH_RESPONSE", "EffectType", "emitEffect", "effect", "resolve", "message", "WebViewInternalMessageScope", "id", "handleEffect", "event", "navigateTo", "thingOrUrl", "inputUrl", "normalizedUrl", "emitEffect", "ConsentStatus", "Scope", "canRunAsUser", "appPermissionState", "ConsentStatus", "scope", "emitEffect", "ICON_FILE_PATH", "showShareSheet", "opts", "iconURL", "ICON_FILE_PATH", "iconRsp", "emitEffect", "getShareData", "assertValidFormFields", "fields", "seenNames", "field", "fieldName", "assertAppSecretsOnly", "flattenFormFieldValue", "value", "getFormValues", "results", "acc", "key", "val", "transformFormFields", "fields", "field", "transformStringField", "transformImageField", "transformParagraphField", "transformNumberField", "transformSelectField", "transformBooleanField", "transformGroupField", "_formKey", "getNextFormKey", "showForm", "formDefinition", "form", "assertValidFormFields", "transformFormFields", "response", "emitEffect", "getFormValues", "showToast", "textOrToast", "toast", "emitEffect", "WebViewImmersiveMode", "tokenParam", "getWebViewMode", "webViewMode", "requestExpandedMode", "event", "entry", "WebViewImmersiveMode", "emitModeEffect", "exitExpandedMode", "addWebViewModeListener", "_", "removeWebViewModeListener", "mode", "entryUrl", "url", "tokenParam", "emitEffect"]
|
|
7
7
|
}
|
|
@@ -55,6 +55,9 @@ export declare function requestExpandedMode(event: MouseEvent, entry: string): P
|
|
|
55
55
|
*/
|
|
56
56
|
export declare function exitExpandedMode(event: MouseEvent): Promise<void>;
|
|
57
57
|
/**
|
|
58
|
+
* @deprecated This API is deprecated and will be removed in a future release.
|
|
59
|
+
* use window.addEventListener("focus", () => { }) to receive notifications
|
|
60
|
+
* when the web view mode changes back to inline.
|
|
58
61
|
* Adds a listener that is called when the web view mode changes. Initial mode
|
|
59
62
|
* is not reported. Web views in the process of destruction may not receive a
|
|
60
63
|
* mode change event.
|
|
@@ -62,12 +65,13 @@ export declare function exitExpandedMode(event: MouseEvent): Promise<void>;
|
|
|
62
65
|
* @param callback The callback to be called when the mode changes.
|
|
63
66
|
* @experimental
|
|
64
67
|
*/
|
|
65
|
-
export declare function addWebViewModeListener(
|
|
68
|
+
export declare function addWebViewModeListener(_: WebViewModeListener): void;
|
|
66
69
|
/**
|
|
70
|
+
* @deprecated This API is deprecated and will be removed in a future release.
|
|
67
71
|
* Removes a listener that was previously added with `addWebViewModeListener`.
|
|
68
72
|
*
|
|
69
73
|
* @param callback The callback to be removed.
|
|
70
74
|
* @experimental
|
|
71
75
|
*/
|
|
72
|
-
export declare function removeWebViewModeListener(
|
|
76
|
+
export declare function removeWebViewModeListener(_: WebViewModeListener): void;
|
|
73
77
|
//# sourceMappingURL=web-view-mode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-view-mode.d.ts","sourceRoot":"","sources":["../../src/effects/web-view-mode.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"web-view-mode.d.ts","sourceRoot":"","sources":["../../src/effects/web-view-mode.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEhD;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAO,WAA8C,CAAC;AAEjF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAInF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAIjE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAG;AAEvE;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAG"}
|
package/effects/web-view-mode.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { WebViewImmersiveMode } from '@devvit/protos/json/devvit/ui/effects/web_view/v1alpha/immersive_mode.js';
|
|
2
2
|
import { emitEffect } from '@devvit/shared-types/client/emit-effect.js';
|
|
3
3
|
import { tokenParam } from '@devvit/shared-types/webbit.js';
|
|
4
|
-
const modeListeners = new Set();
|
|
5
4
|
/**
|
|
6
5
|
* Represents the current web view mode state for the application.
|
|
7
6
|
*
|
|
@@ -54,6 +53,9 @@ export function exitExpandedMode(event) {
|
|
|
54
53
|
return emitModeEffect(WebViewImmersiveMode.INLINE_MODE, event, undefined);
|
|
55
54
|
}
|
|
56
55
|
/**
|
|
56
|
+
* @deprecated This API is deprecated and will be removed in a future release.
|
|
57
|
+
* use window.addEventListener("focus", () => { }) to receive notifications
|
|
58
|
+
* when the web view mode changes back to inline.
|
|
57
59
|
* Adds a listener that is called when the web view mode changes. Initial mode
|
|
58
60
|
* is not reported. Web views in the process of destruction may not receive a
|
|
59
61
|
* mode change event.
|
|
@@ -61,18 +63,15 @@ export function exitExpandedMode(event) {
|
|
|
61
63
|
* @param callback The callback to be called when the mode changes.
|
|
62
64
|
* @experimental
|
|
63
65
|
*/
|
|
64
|
-
export function addWebViewModeListener(
|
|
65
|
-
modeListeners.add(callback);
|
|
66
|
-
}
|
|
66
|
+
export function addWebViewModeListener(_) { }
|
|
67
67
|
/**
|
|
68
|
+
* @deprecated This API is deprecated and will be removed in a future release.
|
|
68
69
|
* Removes a listener that was previously added with `addWebViewModeListener`.
|
|
69
70
|
*
|
|
70
71
|
* @param callback The callback to be removed.
|
|
71
72
|
* @experimental
|
|
72
73
|
*/
|
|
73
|
-
export function removeWebViewModeListener(
|
|
74
|
-
modeListeners.delete(callback);
|
|
75
|
-
}
|
|
74
|
+
export function removeWebViewModeListener(_) { }
|
|
76
75
|
async function emitModeEffect(mode, event, entry) {
|
|
77
76
|
if (!event.isTrusted || event.type !== 'click') {
|
|
78
77
|
console.error('Expanded mode effect ignored due to untrusted event');
|
|
@@ -93,24 +92,6 @@ async function emitModeEffect(mode, event, entry) {
|
|
|
93
92
|
immersiveMode: { entryUrl, immersiveMode: mode },
|
|
94
93
|
});
|
|
95
94
|
}
|
|
96
|
-
/**
|
|
97
|
-
* @internal
|
|
98
|
-
* Handles incoming messages from the client, like when the user closes the immersive modal
|
|
99
|
-
*/
|
|
100
|
-
export function registerListener() {
|
|
101
|
-
addEventListener('message', (event) => {
|
|
102
|
-
const { type, data } = event.data;
|
|
103
|
-
if (type !== 'devvit-message') {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
if (!data?.immersiveModeEvent) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
devvit.webViewMode = data.immersiveModeEvent.immersiveMode;
|
|
110
|
-
const webViewModeString = webViewMode(data.immersiveModeEvent.immersiveMode);
|
|
111
|
-
modeListeners.forEach((listener) => listener(webViewModeString));
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
95
|
function webViewMode(mode) {
|
|
115
96
|
switch (mode) {
|
|
116
97
|
case WebViewImmersiveMode.IMMERSIVE_MODE:
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devvit/client",
|
|
3
|
-
"version": "0.12.6-next-2025-12-08-21-
|
|
3
|
+
"version": "0.12.6-next-2025-12-08-21-42-15-9f008f8f0.0",
|
|
4
4
|
"license": "BSD-3-Clause",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
"test:unit-with-coverage": "vitest run --coverage"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@devvit/protos": "0.12.6-next-2025-12-08-21-
|
|
37
|
-
"@devvit/shared": "0.12.6-next-2025-12-08-21-
|
|
38
|
-
"@devvit/shared-types": "0.12.6-next-2025-12-08-21-
|
|
36
|
+
"@devvit/protos": "0.12.6-next-2025-12-08-21-42-15-9f008f8f0.0",
|
|
37
|
+
"@devvit/shared": "0.12.6-next-2025-12-08-21-42-15-9f008f8f0.0",
|
|
38
|
+
"@devvit/shared-types": "0.12.6-next-2025-12-08-21-42-15-9f008f8f0.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@ampproject/filesize": "4.3.0",
|
|
42
|
-
"@devvit/repo-tools": "0.12.6-next-2025-12-08-21-
|
|
43
|
-
"@devvit/tsconfig": "0.12.6-next-2025-12-08-21-
|
|
42
|
+
"@devvit/repo-tools": "0.12.6-next-2025-12-08-21-42-15-9f008f8f0.0",
|
|
43
|
+
"@devvit/tsconfig": "0.12.6-next-2025-12-08-21-42-15-9f008f8f0.0",
|
|
44
44
|
"esbuild": "0.25.9",
|
|
45
45
|
"eslint": "9.11.1",
|
|
46
46
|
"typescript": "5.8.3",
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
"none": "8 KB"
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "08c324a099f152eff57673696385bc9c00940172"
|
|
56
56
|
}
|