diff --git a/.gitignore b/.gitignore index 0646d96..203b8bb 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ build .env.test.local .env.production.local .idea +.*.sw? npm-debug.log* yarn-debug.log* diff --git a/docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient.md b/docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient.md index a13e6df..6009bd4 100644 --- a/docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient.md +++ b/docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient.md @@ -2804,6 +2804,55 @@ const comment = await reddit.submitComment({ *** +### submitCustomPost() + +> **submitCustomPost**(`options`): `Promise`\<[`Post`](../../models/classes/Post.md)\> + +Submits a new [interactive post](../../../../capabilities/interactive-posts/interactive_posts_overview.mdx) to a subreddit. + +#### Parameters + +##### options + +[`SubmitCustomPostOptions`](../../models/type-aliases/SubmitCustomPostOptions.md) + +Either a self post or a link post. + +#### Returns + +`Promise`\<[`Post`](../../models/classes/Post.md)\> + +A Promise that resolves to a Post object. + +#### Examples + +```ts +const post = await reddit.submitCustomPost({ + subredditName: 'devvit', + title: 'Hello World', + entry: 'default', +}); +``` + +By default, `submitCustomPost()` creates a Post on behalf of the App account, but it may be called on behalf of the User making the request by setting the option `runAs: RunAs.USER`. +When using `runAs: RunAs.USER` to create an interactive post, you must specify the `userGeneratedContent` option. For example: + +```ts +import { RunAs } from '@devvit/public-api'; + +const post = await reddit.submitCustomPost({ + subredditName: await reddit.getCurrentSubredditName(), + title: 'My Devvit Post', + userGeneratedContent: { + text: "hello there", + imageUrls: ["https://styles.redditmedia.com/t5_5wa5ww/styles/communityIcon_wyopomb2xb0a1.png", "https://styles.redditmedia.com/t5_49fkib/styles/bannerBackgroundImage_5a4axis7cku61.png"] + }, + runAs: RunAs.USER, +}); +``` + +*** + ### submitPost() diff --git a/docs/capabilities/server/userActions.md b/docs/capabilities/server/userActions.md index adb44d0..481119d 100644 --- a/docs/capabilities/server/userActions.md +++ b/docs/capabilities/server/userActions.md @@ -13,6 +13,23 @@ By default, apps make posts or comments using their associated app account. With --- +## Available user actions + +These actions can be made on behalf of the user: + +| Action | Permission scope | +| :--- | :--- | +| [submitCustomPost()](../../api/redditapi/RedditAPIClient/classes/RedditAPIClient.md#submitcustompost) | `SUBMIT_POST` | +| [submitPost()](../../api/redditapi/RedditAPIClient/classes/RedditAPIClient.md#submitpost) | `SUBMIT_POST` | +| [submitComment()](../../api/redditapi/RedditAPIClient/classes/RedditAPIClient.md#submitcomment) | `SUBMIT_COMMENT` | +| [subscribeToCurrentSubreddit()](../../api/redditapi/RedditAPIClient/classes/RedditAPIClient#subscribetocurrentsubreddit) | `SUBSCRIBE_TO_SUBREDDIT` | + +Each permission scope your app uses must be declared in your `devvit.json` ([enabling user actions](#enabling-user-actions)). + +Your app must also [collect permission from the user](#collecting-permission-from-the-user). before you can perform actions on their behalf. + +--- + ## Guidelines To ensure a positive user experience and compliance with Reddit policies: @@ -54,6 +71,8 @@ To enable user actions, add the required permissions to your `devvit.json`: } ``` +You should only include the permissions your app actually uses. + After enabling, you can call certain Reddit APIs on behalf of the user by passing the option `runAs: 'USER'`. Currently, the following APIs support this option: @@ -78,6 +97,63 @@ Apps that use `submitPost()` with `runAs: 'USER'` require `userGeneratedContent` --- +## Collecting permission from the user + +The app must call `canRunAsUser()` _on the client side_ before it performs any fetch to +itself on the server side that might result in the use of any user action. This call +serves to collect permission from the user, and to inform the app of the user's decision. + +The first time a user encounters a call to this function, they will be presented with +a consent dialog. All the permission scopes declared in `devvit.json` will be listed, +and the user will be asked to approve or deny. + +If the user chooses approve, then `canRunAsUser()` will resolve its promise to `true`, +and you may proceed with using any permitted user action on the server side. + +If the user chooses deny, then the returned promise resolves to `false`. This may also +happen if the user chooses to revoke permission from their settings page anytime at a +later date. Use this check as an opportunity to continue providing non-consenting users +a quality experience, instead of exposing them to a permission check error from the +server side moments later. + +Once the user has chosen one way or the other, future calls to `canRunAsUser()` will +resolve immediately, without prompting the user any further. + +:::note +We currently do not _require_ collected permissions, but that will be changing soon! +If your app is already using `canRunAsUser()` properly, then it won't be impacted +when enforcement begins. +::: + +```ts +import { canRunAsUser } from '@devvit/web/client'; + +// Button that user interacts with to share their result as a comment +function handleButtonClick() { + if (await canRunAsUser()) { + try { + const response = await fetch('/api/submit-comment'); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + showToast({ + text: 'Comment submitted', + appearance: 'success', + }); + } catch (error) { + showToast('Something went wrong. Please try again.'); + } + } else { + showToast({ + text: 'Skipping comment submission because permission not granted', + appearance: 'success', + }); + } +} +``` + +--- + ## Example: Submit a post as the user This example uses a form to prompt the user for input and then submits a post as the user.