From 543ad635c81464b6a840a3767b704296f436e64f Mon Sep 17 00:00:00 2001 From: Dikran Samarjian Date: Thu, 16 Apr 2026 00:40:47 -0700 Subject: [PATCH] remove Blocks / Mod Tools tabs everywhere --- docs/capabilities/client/forms.mdx | 408 +----------------- docs/capabilities/client/menu-actions.mdx | 94 +--- docs/capabilities/client/navigation.mdx | 158 ++----- docs/capabilities/client/overview.mdx | 110 ++--- docs/capabilities/client/toasts.mdx | 141 ++---- docs/capabilities/server/cache-helper.mdx | 99 +---- docs/capabilities/server/http-fetch.mdx | 106 +---- docs/capabilities/server/media-uploads.mdx | 26 +- docs/capabilities/server/reddit-api.mdx | 118 +---- docs/capabilities/server/redis.mdx | 40 +- docs/capabilities/server/triggers.mdx | 57 +-- .../capabilities/client/forms.mdx | 408 +----------------- .../capabilities/client/menu-actions.mdx | 94 +--- .../capabilities/client/navigation.mdx | 158 ++----- .../capabilities/client/overview.mdx | 110 ++--- .../capabilities/client/toasts.mdx | 141 ++---- .../capabilities/server/cache-helper.mdx | 99 +---- .../capabilities/server/http-fetch.mdx | 106 +---- .../capabilities/server/media-uploads.mdx | 26 +- .../capabilities/server/reddit-api.mdx | 118 +---- .../capabilities/server/redis.mdx | 40 +- .../capabilities/server/triggers.mdx | 57 +-- 22 files changed, 328 insertions(+), 2386 deletions(-) diff --git a/docs/capabilities/client/forms.mdx b/docs/capabilities/client/forms.mdx index bd137e4b..b1801026 100644 --- a/docs/capabilities/client/forms.mdx +++ b/docs/capabilities/client/forms.mdx @@ -9,10 +9,7 @@ A form lets your app ask users to input and submit data. Forms can be defined wi ## Using forms - - - - **Promise-based forms:** +**Promise-based forms:** ```ts title="client/index.ts" import { showForm } from '@devvit/web/client'; @@ -64,104 +61,18 @@ A form lets your app ask users to input and submit data. Forms can be defined wi } ``` - ### Parameters - - **`showForm(options)` → Returns Promise** - - `form` (Form): The form specification object - - `data` (FormValues, optional): Initial form field values - - **Returns**: `Promise` - Resolves with form data or null if cancelled - - - - ```tsx - import { Devvit, useState, useForm } from '@devvit/public-api'; - - // Interactive post with form - // addCustomPostType() is deprecated and will be unsupported. It will not work after June 30. View the announcement below this example. - Devvit.addCustomPostType({ - name: 'FormExample', - render: (context) => { - const [name, setName] = useState('unknown'); - - const myForm = useForm( - { - fields: [ - { - type: 'string', - name: 'name', - label: 'Name', - }, - ], - }, - (values) => { - // onSubmit handler - setName(values.name); - } - ); - - return ( - - Hello {name}! - - - ); - }, - }); - - // Menu action with form - const myForm = Devvit.createForm( - { - fields: [ - { - type: 'string', - name: 'food', - label: 'What is your favorite food?', - }, - ], - }, - (event, context) => { - // onSubmit handler - context.ui.showToast({ text: event.values.food }); - } - ); - - Devvit.addMenuItem({ - label: 'Show a form', - location: 'subreddit', - onPress: async (_event, context) => { - context.ui.showForm(myForm); - }, - }); - ``` - [View `addCustomPostType` deprecation announcement.](https://www.reddit.com/r/Devvit/comments/1r3xcm2/devvit_web_and_the_future_of_devvit/) - - ### Methods +### Parameters - **`context.ui.showForm(formConfig, onSubmit)`** - For interactive posts - - `formConfig` (Form): The form specification object - - `onSubmit` (function): Callback function when form is submitted - - **`Devvit.createForm(formConfig, onSubmit)`** - For menu actions - - `formConfig` (Form): The form specification object - - `onSubmit` (function): Callback function when form is submitted - - - +**`showForm(options)` → Returns Promise** +- `form` (Form): The form specification object +- `data` (FormValues, optional): Initial form field values +- **Returns**: `Promise` - Resolves with form data or null if cancelled ## Menu response forms For forms that open from a menu item, you can use menu responses. This is useful since you do not have access to the `@devvit/web/client` library from a menu item endpoint. - - - - **Configure forms in devvit.json:** +**Configure forms in devvit.json:** ```json title="devvit.json" { "forms": { @@ -316,57 +227,6 @@ For forms that open from a menu item, you can use menu responses. This is useful - - - - For Devvit Blocks, use the standard promise-based approach even in menu actions: - - ```tsx - Devvit.addMenuItem({ - label: 'Multi-step workflow', - location: 'subreddit', - onPress: async (_event, context) => { - // Step 1: Get user data from server - const userData = await fetchUserData(); - - // Step 2: Show form with server data - const step1 = await context.ui.showForm({ - fields: [ - { - type: 'string', - name: 'name', - label: 'Name', - }, - ], - data: { name: userData.name } - }); - - if (!step1) return; - - // Step 3: Save and continue to next form - await saveUserName(step1.name); - - const step2 = await context.ui.showForm({ - fields: [ - { - type: 'paragraph', - name: 'review', - label: 'How was your experience?', - }, - ], - }); - - if (step2) { - await saveReview(step2.review); - context.ui.showToast('Thank you for your feedback!'); - } - }, - }); - ``` - - - - ## Form object @@ -643,10 +503,7 @@ Below is a collection of common use cases and patterns. ### Dynamic forms - - - - **Client-side approach:** +**Client-side approach:** ```ts title="client/index.ts" import { showForm } from '@devvit/web/client'; @@ -775,56 +632,10 @@ Below is a collection of common use cases and patterns. - - - ```tsx - import { Devvit } from '@devvit/public-api'; - - Devvit.configure({ - redditAPI: true, - }); - - const myForm = Devvit.createForm( - (data) => { - return { - fields: [ - { - type: 'string', - name: 'username', - label: 'Username', - defaultValue: data.username, - }, - ], - // Adding `as const` helps you get accurate types in the onSubmit function below - // This will only work if the function does not have any branching logic - } as const; - }, - (event, context) => { - context.ui.showToast({ - text: `Hello ${event.values.username}`, - }); - } - ); - - Devvit.addMenuItem({ - label: 'Show a dynamic form', - location: 'subreddit', - onPress: async (_event, context) => { - const user = await context.reddit.getCurrentUser(); - const username = user?.username; - context.ui.showForm(myForm, { username }); - }, - }); - ``` - - ### Multi-step forms - - - - **Client-side approach (Promise chaining):** +**Client-side approach (Promise chaining):** ```ts title="client/index.ts" import { showForm } from '@devvit/web/client'; @@ -1041,111 +852,12 @@ Below is a collection of common use cases and patterns. - - - ```tsx - import { Devvit, useState, useForm } from '@devvit/public-api'; - - Devvit.configure({ - redditAPI: true, - }); - - // addCustomPostType() is deprecated and will be unsupported. It will not work after June 30. View the announcement below this example. - Devvit.addCustomPostType({ - name: 'Multi-step Form', - render: (context) => { - const [name, setName] = useState(''); - const [food, setFood] = useState(''); - const [drink, setDrink] = useState(''); - - const form3 = useForm( - { - fields: [ - { - type: 'string', - name: 'drink', - label: "What's your favorite drink?", - required: true, - }, - ], - }, - (values) => { - setDrink(values.drink); - } - ); - - const form2 = useForm( - { - fields: [ - { - type: 'string', - name: 'food', - label: "What's your favorite food?", - required: true, - }, - ], - }, - (values) => { - setFood(values.food); - context.ui.showForm(form3); - } - ); - - const form1 = useForm( - { - fields: [ - { - type: 'string', - name: 'name', - label: "What's your name?", - required: true, - }, - ], - }, - (values) => { - setName(values.name); - context.ui.showForm(form2); - } - ); - - function restart() { - setName(''); - setFood(''); - setDrink(''); - context.ui.showForm(form1); - } - - const isAnswered = name && food && drink; - - return ( - - {isAnswered && ( - <> - Name: {name} - Favorite food: {food} - Favorite drink: {drink} - - - - )} - {!isAnswered && } - - ); - }, - }); - ``` - [View `addCustomPostType` deprecation announcement.](https://www.reddit.com/r/Devvit/comments/1r3xcm2/devvit_web_and_the_future_of_devvit/) - - ### One of everything This example includes one of each of the [supported field types](#supported-fields-types). - - - - **Client-side approach:** +**Client-side approach:** ```ts title="client/index.ts" import { showForm } from '@devvit/web/client'; @@ -1386,83 +1098,10 @@ This example includes one of each of the [supported field types](#supported-fiel - - - ```tsx - import { Devvit } from '@devvit/public-api'; - - const exampleForm = Devvit.createForm( - { - title: 'My favorites', - description: 'Tell us about your favorite food!', - fields: [ - { - type: 'string', - name: 'food', - label: 'What is your favorite food?', - helpText: 'Must be edible', - required: true, - }, - { - label: 'About that food', - type: 'group', - fields: [ - { - type: 'number', - name: 'times', - label: 'How many times a week do you eat it?', - defaultValue: 1, - }, - { - type: 'paragraph', - name: 'what', - label: 'What makes it your favorite?', - }, - { - type: 'select', - name: 'healthy', - label: 'Is it healthy?', - options: [ - { label: 'Yes', value: 'yes' }, - { label: 'No', value: 'no' }, - { label: 'Maybe', value: 'maybe' }, - ], - defaultValue: ['maybe'], - }, - ], - }, - { - type: 'boolean', - name: 'again', - label: 'Can we ask again?', - }, - ], - acceptLabel: 'Submit', - cancelLabel: 'Cancel', - }, - (event, context) => { - console.log(event.values); - context.ui.showToast('Thanks!'); - } - ); - - Devvit.addMenuItem({ - location: 'subreddit', - label: 'One of everything form', - onPress: (_event, context) => { - context.ui.showForm(exampleForm); - }, - }); - ``` - - ### Image uploads - - - - **Client-side approach:** +**Client-side approach:** ```ts title="client/index.ts" import { showForm } from '@devvit/web/client'; @@ -1549,28 +1188,3 @@ This example includes one of each of the [supported field types](#supported-fiel - - - ```tsx - import { Devvit } from '@devvit/public-api'; - - const form = Devvit.createForm( - { - title: 'Upload an image!', - fields: [ - { - name: 'myImage', - type: 'image', // This tells the form to expect an image - label: 'Image goes here', - required: true, - }, - ], - }, - (event, context) => { - // returns an i.redd.it URL - const imageUrl = event.values.myImage; - } - ); - ``` - - diff --git a/docs/capabilities/client/menu-actions.mdx b/docs/capabilities/client/menu-actions.mdx index c842d410..348944e8 100644 --- a/docs/capabilities/client/menu-actions.mdx +++ b/docs/capabilities/client/menu-actions.mdx @@ -11,10 +11,7 @@ Add an item to the three dot menu for posts, comments, or subreddits. Menu actio **For most menu actions, use direct client effects.** These provide immediate responses and are perfect for simple actions that don't require server processing. - - - - **Menu items defined in devvit.json:** +**Menu items defined in devvit.json:** ```json title="devvit.json" { @@ -74,53 +71,6 @@ app.post( - - - - ```tsx - import { Devvit } from '@devvit/public-api'; - -// Simple menu action with direct client effects -Devvit.addMenuItem({ -label: 'Show user info', -location: 'post', // 'post', 'comment', 'subreddit', or array -onPress: async (event, context) => { -// Direct client effect - no server processing needed - context.ui.showToast('Menu action clicked!'); -}, -}); - -// Menu action with form -const surveyForm = Devvit.createForm( -{ -fields: [ -{ -type: 'string', -name: 'feedback', -label: 'Your feedback', -}, -], -}, -(event, context) => { -// onSubmit handler -context.ui.showToast({ text: `Thanks for the feedback: ${event.values.feedback}` }); -} -); - -Devvit.addMenuItem({ -label: 'Quick survey', -location: 'subreddit', -forUserType: 'moderator', // Optional: restrict to moderators -onPress: async (event, context) => { -context.ui.showForm(surveyForm); -}, -}); - -```` - - - - ## Supported contexts You can decide where the menu action shows up by specifying the location property. @@ -139,9 +89,6 @@ For moderator permission security, when opening a form from a menu action with ` In Devvit Web, your menu item should respond with a client side effect to give feedback to users. This is available as a UIResponse as you do not have access to the `@devvit/web/client` library from your server endpoints. - - - **Menu items with server processing:** ```json title="devvit.json" @@ -242,45 +189,6 @@ app.post( - - - - For Devvit Blocks, use the direct context approach even for complex workflows: - -```tsx -Devvit.addMenuItem({ - label: "Process and validate data", - location: "post", // 'post', 'comment', 'subreddit', or array - forUserType: "moderator", // Optional: restrict to moderators - onPress: async (event, context) => { - try { - // Perform server-side processing - const userData = await validateAndProcessData(); - - // Show form with server-fetched data - const result = await context.ui.showForm( - { - fields: [ - { - type: "string", - name: "processedData", - label: "Processed Data", - }, - ], - }, - (values) => { - context.ui.showToast(`Processed: ${values.processedData}`); - }, - ); - } catch (error) { - context.ui.showToast("Processing failed. Please try again."); - } - }, -}); -``` - - - ### Menu response examples diff --git a/docs/capabilities/client/navigation.mdx b/docs/capabilities/client/navigation.mdx index 3b143ed2..0a2cf3cb 100644 --- a/docs/capabilities/client/navigation.mdx +++ b/docs/capabilities/client/navigation.mdx @@ -1,6 +1,3 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - # Navigation Use navigation functions to redirect users to Reddit content or external websites in response to user actions, such as button clicks. You can redirect to a `url` string or to objects such as [`Subreddit`](/api/redditapi/models/classes/Subreddit.md), [`Post`](/api/redditapi/models/classes/Post.md), or [`Comment`](/api/redditapi/models/classes/Comment.md). @@ -13,135 +10,32 @@ When linking to Reddit content, the navigation function requires the app account ## Basic navigation - - - ```ts title="client/index.ts" - import { navigateTo } from '@devvit/web/client'; - - // Navigate to external URLs - navigateTo('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); - - // Navigate to Reddit URLs - navigateTo('https://www.reddit.com/r/movies/comments/tzxev3/'); - - // Navigate to Reddit objects - async function goToPost() { - const post = await fetch('/api/getPost').then(r => r.json()); - navigateTo(post); - } - - // Use in button handlers or user interactions - function handleNavigateClick() { - navigateTo('https://www.reddit.com/r/webdev'); - } - ``` - - ### Parameters - - **`navigateTo(target)`** - - - `target`: Either a URL string or a Reddit object (Subreddit, Post, Comment) - - - - ```tsx - import { Devvit } from '@devvit/public-api'; - - Devvit.configure({ redditAPI: true }); - - // Navigate to URL - Devvit.addMenuItem({ - label: 'Navigate to url', - location: 'subreddit', - onPress: async (_event, context) => { - const url = 'https://www.reddit.com/r/movies/comments/tzxev3/'; - context.ui.navigateTo(url); - }, - }); - - // Navigate to subreddit - Devvit.addMenuItem({ - label: 'Navigate to subreddit', - location: 'subreddit', - onPress: async (_event, context) => { - const subredditId = 't5_2qh1o'; - const subreddit = await context.reddit.getSubredditById(subredditId); - context.ui.navigateTo(subreddit); - }, - }); - - // Navigate to post - Devvit.addMenuItem({ - label: 'Navigate to post', - location: 'subreddit', - onPress: async (_event, context) => { - const postId = 't3_tzxev3'; - const post = await context.reddit.getPostById(postId); - context.ui.navigateTo(post); - }, - }); - - // Navigate to comment - Devvit.addMenuItem({ - label: 'Navigate to comment', - location: 'subreddit', - onPress: async (_event, context) => { - const commentId = 't1_i426ob1'; - const comment = await context.reddit.getCommentById(commentId); - context.ui.navigateTo(comment); - }, - }); - - // Interactive post with navigation - // addCustomPostType() is deprecated and will be unsupported. It will not work after June 30. View the announcement below this example. - Devvit.addCustomPostType({ - name: 'Navigation Post', - render: (context) => { - return ( - - - - ); - }, - }); - - // Menu action to create interactive post - Devvit.addMenuItem({ - label: 'Add navigation post', - location: 'subreddit', - onPress: async (_event, context) => { - const subreddit = await context.reddit.getCurrentSubreddit(); - await context.reddit.submitPost({ - title: 'Navigate to post', - subredditName: subreddit.name, - preview: ( - - Loading ... - - ), - }); - context.ui.showToast('Created post!'); - }, - }); - ``` - [View `addCustomPostType` deprecation announcement.](https://www.reddit.com/r/Devvit/comments/1r3xcm2/devvit_web_and_the_future_of_devvit/) - - ### Parameters - - **`context.ui.navigateTo(target)`** - - - `target`: Either a URL string or a Reddit object (Subreddit, Post, Comment) - - - +```ts title="client/index.ts" +import { navigateTo } from '@devvit/web/client'; + +// Navigate to external URLs +navigateTo('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); + +// Navigate to Reddit URLs +navigateTo('https://www.reddit.com/r/movies/comments/tzxev3/'); + +// Navigate to Reddit objects +async function goToPost() { + const post = await fetch('/api/getPost').then(r => r.json()); + navigateTo(post); +} + +// Use in button handlers or user interactions +function handleNavigateClick() { + navigateTo('https://www.reddit.com/r/webdev'); +} +``` + +### Parameters + +**`navigateTo(target)`** + +- `target`: Either a URL string or a Reddit object (Subreddit, Post, Comment) :::tip Menu response navigation For navigation in menu response workflows (when you need server processing before navigation), see the [Menu Actions](./menu-actions.mdx) documentation. diff --git a/docs/capabilities/client/overview.mdx b/docs/capabilities/client/overview.mdx index 72746638..5d645d5d 100644 --- a/docs/capabilities/client/overview.mdx +++ b/docs/capabilities/client/overview.mdx @@ -1,91 +1,35 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - # Client Overview Client-side effects enable your Devvit app to provide interactive feedback and navigation to users. These effects include showing toasts, displaying forms, navigating to different pages, and more. - - - - Import client functions from `@devvit/web/client`: - - ```ts title="client/index.ts" - import { showToast, showForm, navigateTo } from '@devvit/web/client'; - - // Show a toast notification - showToast('Hello from Devvit Web!'); - - // Navigate to a URL - navigateTo('https://www.reddit.com/r/webdev'); - - // Show a form and handle response - const result = await showForm({ - form: { - fields: [ - { - type: 'string', - name: 'username', - label: 'Username' - } - ] - } - }); - - if (result) { - console.log('Form submitted:', result.username); +Import client functions from `@devvit/web/client`: + +```ts title="client/index.ts" +import { showToast, showForm, navigateTo } from '@devvit/web/client'; + +// Show a toast notification +showToast('Hello from Devvit Web!'); + +// Navigate to a URL +navigateTo('https://www.reddit.com/r/webdev'); + +// Show a form and handle response +const result = await showForm({ + form: { + fields: [ + { + type: 'string', + name: 'username', + label: 'Username' + } + ] } - ``` - - - - - Use context methods in event handlers and component render functions: - - ```tsx - import { Devvit } from '@devvit/public-api'; - - // In a custom post component - // addCustomPostType() is deprecated and will be unsupported. It will not work after June 30. View the announcement below this example. - Devvit.addCustomPostType({ - name: 'Interactive Post', - render: (context) => { - return ( - - - - - ); - }, - }); - ``` - [View `addCustomPostType` deprecation announcement.](https://www.reddit.com/r/Devvit/comments/1r3xcm2/devvit_web_and_the_future_of_devvit/) - - - +}); + +if (result) { + console.log('Form submitted:', result.username); +} +``` ## Available client effects diff --git a/docs/capabilities/client/toasts.mdx b/docs/capabilities/client/toasts.mdx index 6d94f099..a0ed6194 100644 --- a/docs/capabilities/client/toasts.mdx +++ b/docs/capabilities/client/toasts.mdx @@ -1,6 +1,3 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - # Toasts Display temporary notification messages to users at the bottom of the screen. @@ -24,108 +21,44 @@ Toasts will not work from scheduled jobs or triggers. ## Basic toast usage - - - ```ts - import { showToast } from '@devvit/web/client'; - - // Simple text toast - showToast('Operation completed successfully!'); - - // Toast with custom appearance - showToast({ - text: 'Data saved successfully!', - appearance: 'success', // 'neutral' | 'success' - }); - - // Use in button handlers or user interactions - function handleButtonClick() { - try { - // Perform some operation - processUserData(); - - showToast({ - text: 'Your data has been processed!', - appearance: 'success' - }); - } catch (error) { - showToast('Something went wrong. Please try again.'); - } +```ts +import { showToast } from '@devvit/web/client'; + +// Simple text toast +showToast('Operation completed successfully!'); + +// Toast with custom appearance +showToast({ + text: 'Data saved successfully!', + appearance: 'success', // 'neutral' | 'success' +}); + +// Use in button handlers or user interactions +function handleButtonClick() { + try { + // Perform some operation + processUserData(); + + showToast({ + text: 'Your data has been processed!', + appearance: 'success' + }); + } catch (error) { + showToast('Something went wrong. Please try again.'); } - ``` - - ### Parameters - - **`showToast(textOrToast)`** - - - `textOrToast`: Either a string message or a `Toast` object - - **Toast Object Properties:** - - - `text` (string): The message to display - - `appearance` (string, optional): The visual style (`'neutral'` | `'success'`). Defaults to `'neutral'` - - - - ```ts - import { Devvit } from '@devvit/public-api'; - - // Example with menu action - Devvit.addMenuItem({ - label: 'Save Data', - location: 'post', - forUserType: 'moderator', - onPress: async (event, context) => { - try { - // Perform some operation - await performAction(); - - // Show success toast - context.ui.showToast({ - text: 'Data saved successfully!', - appearance: 'success' - }); - } catch (error) { - // Show error toast - context.ui.showToast('Something went wrong. Please try again.'); - } - }, - }); - - // Example in blocks - // addCustomPostType() is deprecated and will be unsupported. It will not work after June 30. View the announcement below this example. - Devvit.addCustomPostType({ - name: 'My Custom Post', - render: (context) => { - return ( - - - - ); - }, - }); - ``` - [View `addCustomPostType` deprecation announcement.](https://www.reddit.com/r/Devvit/comments/1r3xcm2/devvit_web_and_the_future_of_devvit/) - - ### Parameters - - **`context.ui.showToast(textOrToast)`** - - - `textOrToast`: Either a string message or a `Toast` object - - **Toast Object Properties:** - - - `text` (string): The message to display - - `appearance` (string, optional): The visual style (`'neutral'` | `'success'`). Defaults to `'neutral'` - - - +} +``` + +### Parameters + +**`showToast(textOrToast)`** + +- `textOrToast`: Either a string message or a `Toast` object + +**Toast Object Properties:** + +- `text` (string): The message to display +- `appearance` (string, optional): The visual style (`'neutral'` | `'success'`). Defaults to `'neutral'` :::tip Menu response toasts For toasts in menu response workflows (when you need server processing before showing toasts), see the [Menu Actions](./menu-actions.mdx) documentation. diff --git a/docs/capabilities/server/cache-helper.mdx b/docs/capabilities/server/cache-helper.mdx index c7724bfe..a6e4e5ce 100644 --- a/docs/capabilities/server/cache-helper.mdx +++ b/docs/capabilities/server/cache-helper.mdx @@ -14,56 +14,26 @@ Under the covers, it's Redis plus a local in-memory write-through cache. This pr ## Usage - - - You can import cache helper from `@devvit/web/server` in your server source files. The cache helper is not available client-side, so you will see an error if you try to import it in client source files. +You can import cache helper from `@devvit/web/server` in your server source files. The cache helper is not available client-side, so you will see an error if you try to import it in client source files. - ```tsx - import { cache } from '@devvit/web/server'; - ``` - - - You need to enable [Redis](../server/redis.mdx) in order to use Cache helper. - - ```tsx - Devvit.configure({ - redis: true, - // other capabilities - }); - ``` - - +```tsx +import { cache } from '@devvit/web/server'; +``` ## Parameters -The cache takes a key and a TTL regardless of whether you are using Devvit Web or Devvit Blocks. The only difference is that **the TTL is in seconds for Devvit Web** and **milliseconds for Devvit Blocks / Mod Tools**. - - - +The cache takes a key and a TTL: | **Parameters** | **Description** | | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `key` | This is a string that identifies a cached response. Instead of making a real request, the app gets the cached response with the key you provide. Make sure to use different keys for different data. For example, if you’re saving post-specific data, add the postId to the cache key, like this: `post_data_${postId})`. | | `ttl` | Time to live is the number of **seconds** the cached response is expected to be relevant. Once the cached response expires, it will be voided and a real request is made to populate the cache again. You can treat it as a threshold, where ttl of 30 would mean that a request is done no more than once per 30 seconds. | - - - -| **Parameters** | **Description** | -| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `key` | This is a string that identifies a cached response. Instead of making a real request, the app gets the cached response with the key you provide. Make sure to use different keys for different data. For example, if you’re saving post-specific data, add the postId to the cache key, like this: `post_data_${postId})`. | -| `ttl` | Time to live is the number of **milliseconds** the cached response is expected to be relevant. Once the cached response expires, it will be voided and a real request is made to populate the cache again. You can treat it as a threshold, where ttl of 30000 would mean that a request is done no more than once per 30 seconds. | - - - - ## Example Here’s a way to set up in-app caching instead of using scheduler or interval to fetch. - - - - - - - ```tsx - import { Devvit, useState } from '@devvit/public-api'; - - Devvit.configure({ - redis: true, - http: true, // to use `fetch()` - // other capabilities - }); - - // addCustomPostType() is deprecated and will be unsupported. It will not work after June 30. View the announcement below this example. - Devvit.addCustomPostType({ - name: 'Name', - render: (context) => { - const [data, setData] = useState({}); - - async function getData() { - const result = await context.cache( - async () => { - const response = await fetch('https://example.com'); - if (!response.ok) { - throw Error(`HTTP error ${response.status}: ${response.statusText}`); - } - return await response.json(); - }, - { - key: context.userId!, - ttl: 10_000, // millis - } - ); - - setData(result); - } - - return ( - - - - {data.something} - - ); - }, - }); - - export default Devvit; - ``` - [View `addCustomPostType` deprecation announcement.](https://www.reddit.com/r/Devvit/comments/1r3xcm2/devvit_web_and_the_future_of_devvit/) - - diff --git a/docs/capabilities/server/http-fetch.mdx b/docs/capabilities/server/http-fetch.mdx index 3ea13c22..a3c024c3 100644 --- a/docs/capabilities/server/http-fetch.mdx +++ b/docs/capabilities/server/http-fetch.mdx @@ -1,6 +1,3 @@ -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - # HTTP Fetch Make requests to allow-listed external domains. @@ -9,33 +6,17 @@ Your Devvit app can make network requests to access allow-listed external domain ## Enabling HTTP fetch calls - - - ```json title="devvit.json" - { - ... - "permissions": { - "http": { - "enable": true, - "domains": ["my-site.com", "another-domain.net"] - } +```json title="devvit.json" +{ + ... + "permissions": { + "http": { + "enable": true, + "domains": ["my-site.com", "another-domain.net"] } } - ``` - - - ```ts - import { Devvit } from '@devvit/public-api'; - -Devvit.configure({ -http: { -domains: ['my-site.com', 'another-domain.net'], -}, -}); - -```` - - +} +``` ### Requesting a domain to be allow-listed @@ -64,25 +45,22 @@ Apps must request each individual domain that it intends to fetch, even if the d ## Example usage - - - - Devvit Web applications have two different contexts for using fetch: +Devvit Web applications have two different contexts for using fetch: - ### Server-side fetch +### Server-side fetch - Server-side fetch allows your app to make HTTP requests to allowlisted external domains from your server-side code (e.g., API routes, server actions): +Server-side fetch allows your app to make HTTP requests to allowlisted external domains from your server-side code (e.g., API routes, server actions): ```tsx title="server/index.ts" - const response = await fetch('https://example.com/api/data', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); +const response = await fetch('https://example.com/api/data', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, +}); - const data = await response.json(); - console.log('External API response:', data); +const data = await response.json(); +console.log('External API response:', data); ```` ### Client-side fetch @@ -98,7 +76,7 @@ Client-side fetch has different restrictions and can only make requests to your ```tsx title="client/index.ts" const handleFetchData = async () => { - // ✅ Correct: Fetching your own webview's API endpoint + // Correct: fetching your own webview's API endpoint const response = await fetch("/api/user-data", { method: "GET", headers: { @@ -110,51 +88,13 @@ const handleFetchData = async () => { console.log("API response:", data); }; -// ❌ Incorrect: Cannot fetch external domains from client-side +// Incorrect: cannot fetch external domains from client-side // const response = await fetch('https://external-api.com/data'); -// ❌ Incorrect: Endpoint must end with /api +// Incorrect: endpoint must end with /api // const response = await fetch('/user-data'); ``` - - - - For Devvit Blocks and Mod Tools, fetch is available within menu actions, triggers, and other server-side contexts: - - ```ts - import { Devvit } from '@devvit/public-api'; - - Devvit.configure({ - http: { - domains: ['example.com'], - }, - }); - - Devvit.addMenuItem({ - location: 'comment', - label: 'Sample HTTP request', - onPress: async (_event, context) => { - console.log(`Comment ID: ${context.commentId}`); - const response = await fetch('https://example.com', { - method: 'post', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ content: context.commentId }), - }); - context.ui.showToast( - `Invoked HTTP request on comment: ${context.commentId}. Completed with status: ${response.status}` - ); - }, - }); - - export default Devvit; - ``` - - - - ## Troubleshooting If you see the following error, it means HTTP Fetch requests are hitting the internal timeout limits. To resolve this: diff --git a/docs/capabilities/server/media-uploads.mdx b/docs/capabilities/server/media-uploads.mdx index 7316f1a7..b9e61a94 100644 --- a/docs/capabilities/server/media-uploads.mdx +++ b/docs/capabilities/server/media-uploads.mdx @@ -1,6 +1,3 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - # Media Uploads :::warning @@ -24,29 +21,16 @@ Enable the `media` permission in your `devvit.json` file. ## Using media uploads On the server, you can pass the URL of any remotely hosted image (even if its not hosted on Reddit) to the `media.upload` function. This function will return a Reddit URL. Both HTTP and data URLs are supported. - - - ```ts title="server/index.ts" - import { media } from '@devvit/media'; - function submitImage() { - const response = await media.upload({ - url: 'https://media2.giphy.com/media/xTiN0CNHgoRf1Ha7CM/giphy.gif', - type: 'gif', - }); - } - ``` - - - ```ts - import { Devvit } from '@devvit/public-api'; +```ts title="server/index.ts" +import { media } from '@devvit/media'; +function submitImage() { const response = await media.upload({ url: 'https://media2.giphy.com/media/xTiN0CNHgoRf1Ha7CM/giphy.gif', type: 'gif', }); - ``` - - +} +``` ## Canvas screenshots diff --git a/docs/capabilities/server/reddit-api.mdx b/docs/capabilities/server/reddit-api.mdx index cb9191ff..a2b32781 100644 --- a/docs/capabilities/server/reddit-api.mdx +++ b/docs/capabilities/server/reddit-api.mdx @@ -1,6 +1,3 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - # Reddit API Overview The Reddit API allows you to read and write Reddit content such as posts / comments / upvotes, in order to integrate your app's behavior with the content of the community it's installed in. @@ -24,32 +21,17 @@ Devvit apps cannot access certain private user data. This data is private to the Here's how to obtain a reference to the Reddit client - - - ```json title="devvit.json" - { - "permissions": { - "reddit": true - } +```json title="devvit.json" +{ + "permissions": { + "reddit": true } - ``` - ```ts title="server/index.ts" - import { reddit } from '@devvit/reddit'; - ``` - - - ```ts title="devvit.tsx" - import { Devvit } from '@devvit/public-api'; - - Devvit.configure({ - redditAPI: true, - }); +} +``` - //Then, in any function that has a reference to Devvit.Context: - const reddit = context.reddit; - ``` - - +```ts title="server/index.ts" +import { reddit } from '@devvit/reddit'; +``` ## Reddit Thing IDs @@ -79,9 +61,7 @@ const parentId = comment.parentId; // 't3_abc123' or 't1_def456' ## Example usage ### Submitting a post - - - ```ts +```ts import { Devvit } from '@devvit/public-api'; import { context, reddit } from '@devvit/web/server'; @@ -100,36 +80,7 @@ export const createPost = async () => { entry: 'default', }); }; - ``` - - - -```tsx -import { Devvit } from '@devvit/public-api'; - -Devvit.configure({ - redditAPI: true, -}); - -function createPost(context: Devvit.Context) { - const currentSubreddit = context.reddit.getCurrentSubreddit(); - if (!currentSubreddit) { - throw new Error('No subreddit found'); - } - - return context.reddit.submitPost({ - title: 'My custom post', - subredditName: currentSubreddit.name, - preview: ( - - Loading... - - ), - }); -} ``` - - ### Submitting a comment @@ -138,42 +89,19 @@ Auto-comments should be used to spark conversation in the post comments, but you ::: - - - ```ts - import { context, reddit } from '@devvit/web/server'; +```ts +import { context, reddit } from '@devvit/web/server'; - export const createComment = async () => { - const { subredditName } = context; - if (!subredditName) { - throw new Error('subredditName is required'); - } +export const createComment = async () => { + const { subredditName } = context; + if (!subredditName) { + throw new Error('subredditName is required'); + } - reddit.submitComment({ - postId: 't3_123456', // Replace with the actual post ID - text: 'This is a comment from a Devvit app', - runAs: 'USER' // Optional: specify the user to run as - }); - }; -``` - - - ```tsx - import { Devvit } from '@devvit/public-api'; - - Devvit.configure({ - redditAPI: true, - }); - - function createComment(context: Devvit.Context) { - const { reddit } = context; - - reddit.submitComment({ - postId: 't3_123456', // Replace with the actual post ID - text: 'This is a comment from a Devvit app', - runAs: RunAs.USER, // Optional: specify the user to run as - }); - }; + reddit.submitComment({ + postId: 't3_123456', // Replace with the actual post ID + text: 'This is a comment from a Devvit app', + runAs: 'USER' // Optional: specify the user to run as + }); +}; ``` - - diff --git a/docs/capabilities/server/redis.mdx b/docs/capabilities/server/redis.mdx index bcca403a..61a62107 100644 --- a/docs/capabilities/server/redis.mdx +++ b/docs/capabilities/server/redis.mdx @@ -30,9 +30,7 @@ All limits are applied at a per-installation granularity. ### Menu actions - - - ```js title="devvit.json" +```js title="devvit.json" { "menuActions": [ { @@ -89,22 +87,6 @@ All limits are applied at a per-installation granularity. - - - ```ts - Devvit.addMenuItem({ - location: 'subreddit', - label: 'Test Redis', - onPress: async (event, { redis }) => { - const key = 'hello'; - await redis.set(key, 'world'); - const value = await redis.get(key); - console.log(`${key}: ${value}`); - }, - }); - ``` - - ### Games @@ -117,11 +99,9 @@ You can take a look at this [Game Template](https://github.com/reddit/devvit-tem Not all Redis features are supported. If you would like to request a specific Redis feature, please reach out to our team [via modmail](https://www.reddit.com/message/compose/?to=%2Fr%2FDevvit) or Discord. ::: -For all examples below, we assume that you already have obtained a Redis Client. Here's how to obtain a Redis Client for Devvit Web, Devvit Blocks and Mod Tools: +For all examples below, we assume that you already have obtained a Redis Client. Here's how to obtain a Redis Client: - - - ```json title="devvit.json" +```json title="devvit.json" { "permissions": { "redis": true @@ -131,20 +111,6 @@ For all examples below, we assume that you already have obtained a Redis Client. ```ts title="server/index.ts" import { redis } from '@devvit/redis'; ``` - - - ```ts title="devvit.tsx" - import { Devvit } from '@devvit/public-api'; - - Devvit.configure({ - redis: true, - }); - - //Then, in any function that has a reference to Devvit.Context: - const redis = context.redis; - ``` - - ### Simple read/write diff --git a/docs/capabilities/server/triggers.mdx b/docs/capabilities/server/triggers.mdx index f9be593c..409a6d18 100644 --- a/docs/capabilities/server/triggers.mdx +++ b/docs/capabilities/server/triggers.mdx @@ -39,9 +39,7 @@ A full list of events and their payloads can be found in the [EventTypes documen ### 1. Add triggers and endpoints to `devvit.json` - - - Declare the triggers and their corresponding endpoints in your `devvit.json`: +Declare the triggers and their corresponding endpoints in your `devvit.json`: ```json "triggers": { @@ -51,28 +49,10 @@ A full list of events and their payloads can be found in the [EventTypes documen } ``` - - - Declare the triggers in your `devvit.json`: - -```json -{ - "name": "your-app-name", - "blocks": { - "entry": "src/main.tsx", - "triggers": ["onPostCreate"] - } -} -``` - - - ### 2. Handle trigger events in your server logic - - - Listen for the events in your server and access the data passed into the request: +Listen for the events in your server and access the data passed into the request: ( - - - Handle trigger events in your main file. Example (`src/main.tsx`): - -```tsx -import { Devvit } from "@devvit/public-api"; - -// Handling a PostSubmit event -Devvit.addTrigger({ - event: "PostSubmit", // Event name from above - onEvent: async (event) => { - console.log(`Received OnPostSubmit event:\n${JSON.stringify(event)}`); - }, -}); - -// Handling multiple events: PostUpdate and PostReport -Devvit.addTrigger({ - events: ["PostUpdate", "PostReport"], // An array of events - onEvent: async (event) => { - if (event.type == "PostUpdate") { - console.log(`Received OnPostUpdate event:\n${JSON.stringify(request)}`); - } else if (event.type === "PostReport") { - console.log(`Received OnPostReport event:\n${JSON.stringify(request)}`); - } - }, -}); - -export default Devvit; -``` - - - - ## Best practices - Avoid creating recursive triggers that could cause infinite loops or crashes (for example, a comment trigger that creates a comment). diff --git a/versioned_docs/version-0.12/capabilities/client/forms.mdx b/versioned_docs/version-0.12/capabilities/client/forms.mdx index bd137e4b..b1801026 100644 --- a/versioned_docs/version-0.12/capabilities/client/forms.mdx +++ b/versioned_docs/version-0.12/capabilities/client/forms.mdx @@ -9,10 +9,7 @@ A form lets your app ask users to input and submit data. Forms can be defined wi ## Using forms - - - - **Promise-based forms:** +**Promise-based forms:** ```ts title="client/index.ts" import { showForm } from '@devvit/web/client'; @@ -64,104 +61,18 @@ A form lets your app ask users to input and submit data. Forms can be defined wi } ``` - ### Parameters - - **`showForm(options)` → Returns Promise** - - `form` (Form): The form specification object - - `data` (FormValues, optional): Initial form field values - - **Returns**: `Promise` - Resolves with form data or null if cancelled - - - - ```tsx - import { Devvit, useState, useForm } from '@devvit/public-api'; - - // Interactive post with form - // addCustomPostType() is deprecated and will be unsupported. It will not work after June 30. View the announcement below this example. - Devvit.addCustomPostType({ - name: 'FormExample', - render: (context) => { - const [name, setName] = useState('unknown'); - - const myForm = useForm( - { - fields: [ - { - type: 'string', - name: 'name', - label: 'Name', - }, - ], - }, - (values) => { - // onSubmit handler - setName(values.name); - } - ); - - return ( - - Hello {name}! - - - ); - }, - }); - - // Menu action with form - const myForm = Devvit.createForm( - { - fields: [ - { - type: 'string', - name: 'food', - label: 'What is your favorite food?', - }, - ], - }, - (event, context) => { - // onSubmit handler - context.ui.showToast({ text: event.values.food }); - } - ); - - Devvit.addMenuItem({ - label: 'Show a form', - location: 'subreddit', - onPress: async (_event, context) => { - context.ui.showForm(myForm); - }, - }); - ``` - [View `addCustomPostType` deprecation announcement.](https://www.reddit.com/r/Devvit/comments/1r3xcm2/devvit_web_and_the_future_of_devvit/) - - ### Methods +### Parameters - **`context.ui.showForm(formConfig, onSubmit)`** - For interactive posts - - `formConfig` (Form): The form specification object - - `onSubmit` (function): Callback function when form is submitted - - **`Devvit.createForm(formConfig, onSubmit)`** - For menu actions - - `formConfig` (Form): The form specification object - - `onSubmit` (function): Callback function when form is submitted - - - +**`showForm(options)` → Returns Promise** +- `form` (Form): The form specification object +- `data` (FormValues, optional): Initial form field values +- **Returns**: `Promise` - Resolves with form data or null if cancelled ## Menu response forms For forms that open from a menu item, you can use menu responses. This is useful since you do not have access to the `@devvit/web/client` library from a menu item endpoint. - - - - **Configure forms in devvit.json:** +**Configure forms in devvit.json:** ```json title="devvit.json" { "forms": { @@ -316,57 +227,6 @@ For forms that open from a menu item, you can use menu responses. This is useful - - - - For Devvit Blocks, use the standard promise-based approach even in menu actions: - - ```tsx - Devvit.addMenuItem({ - label: 'Multi-step workflow', - location: 'subreddit', - onPress: async (_event, context) => { - // Step 1: Get user data from server - const userData = await fetchUserData(); - - // Step 2: Show form with server data - const step1 = await context.ui.showForm({ - fields: [ - { - type: 'string', - name: 'name', - label: 'Name', - }, - ], - data: { name: userData.name } - }); - - if (!step1) return; - - // Step 3: Save and continue to next form - await saveUserName(step1.name); - - const step2 = await context.ui.showForm({ - fields: [ - { - type: 'paragraph', - name: 'review', - label: 'How was your experience?', - }, - ], - }); - - if (step2) { - await saveReview(step2.review); - context.ui.showToast('Thank you for your feedback!'); - } - }, - }); - ``` - - - - ## Form object @@ -643,10 +503,7 @@ Below is a collection of common use cases and patterns. ### Dynamic forms - - - - **Client-side approach:** +**Client-side approach:** ```ts title="client/index.ts" import { showForm } from '@devvit/web/client'; @@ -775,56 +632,10 @@ Below is a collection of common use cases and patterns. - - - ```tsx - import { Devvit } from '@devvit/public-api'; - - Devvit.configure({ - redditAPI: true, - }); - - const myForm = Devvit.createForm( - (data) => { - return { - fields: [ - { - type: 'string', - name: 'username', - label: 'Username', - defaultValue: data.username, - }, - ], - // Adding `as const` helps you get accurate types in the onSubmit function below - // This will only work if the function does not have any branching logic - } as const; - }, - (event, context) => { - context.ui.showToast({ - text: `Hello ${event.values.username}`, - }); - } - ); - - Devvit.addMenuItem({ - label: 'Show a dynamic form', - location: 'subreddit', - onPress: async (_event, context) => { - const user = await context.reddit.getCurrentUser(); - const username = user?.username; - context.ui.showForm(myForm, { username }); - }, - }); - ``` - - ### Multi-step forms - - - - **Client-side approach (Promise chaining):** +**Client-side approach (Promise chaining):** ```ts title="client/index.ts" import { showForm } from '@devvit/web/client'; @@ -1041,111 +852,12 @@ Below is a collection of common use cases and patterns. - - - ```tsx - import { Devvit, useState, useForm } from '@devvit/public-api'; - - Devvit.configure({ - redditAPI: true, - }); - - // addCustomPostType() is deprecated and will be unsupported. It will not work after June 30. View the announcement below this example. - Devvit.addCustomPostType({ - name: 'Multi-step Form', - render: (context) => { - const [name, setName] = useState(''); - const [food, setFood] = useState(''); - const [drink, setDrink] = useState(''); - - const form3 = useForm( - { - fields: [ - { - type: 'string', - name: 'drink', - label: "What's your favorite drink?", - required: true, - }, - ], - }, - (values) => { - setDrink(values.drink); - } - ); - - const form2 = useForm( - { - fields: [ - { - type: 'string', - name: 'food', - label: "What's your favorite food?", - required: true, - }, - ], - }, - (values) => { - setFood(values.food); - context.ui.showForm(form3); - } - ); - - const form1 = useForm( - { - fields: [ - { - type: 'string', - name: 'name', - label: "What's your name?", - required: true, - }, - ], - }, - (values) => { - setName(values.name); - context.ui.showForm(form2); - } - ); - - function restart() { - setName(''); - setFood(''); - setDrink(''); - context.ui.showForm(form1); - } - - const isAnswered = name && food && drink; - - return ( - - {isAnswered && ( - <> - Name: {name} - Favorite food: {food} - Favorite drink: {drink} - - - - )} - {!isAnswered && } - - ); - }, - }); - ``` - [View `addCustomPostType` deprecation announcement.](https://www.reddit.com/r/Devvit/comments/1r3xcm2/devvit_web_and_the_future_of_devvit/) - - ### One of everything This example includes one of each of the [supported field types](#supported-fields-types). - - - - **Client-side approach:** +**Client-side approach:** ```ts title="client/index.ts" import { showForm } from '@devvit/web/client'; @@ -1386,83 +1098,10 @@ This example includes one of each of the [supported field types](#supported-fiel - - - ```tsx - import { Devvit } from '@devvit/public-api'; - - const exampleForm = Devvit.createForm( - { - title: 'My favorites', - description: 'Tell us about your favorite food!', - fields: [ - { - type: 'string', - name: 'food', - label: 'What is your favorite food?', - helpText: 'Must be edible', - required: true, - }, - { - label: 'About that food', - type: 'group', - fields: [ - { - type: 'number', - name: 'times', - label: 'How many times a week do you eat it?', - defaultValue: 1, - }, - { - type: 'paragraph', - name: 'what', - label: 'What makes it your favorite?', - }, - { - type: 'select', - name: 'healthy', - label: 'Is it healthy?', - options: [ - { label: 'Yes', value: 'yes' }, - { label: 'No', value: 'no' }, - { label: 'Maybe', value: 'maybe' }, - ], - defaultValue: ['maybe'], - }, - ], - }, - { - type: 'boolean', - name: 'again', - label: 'Can we ask again?', - }, - ], - acceptLabel: 'Submit', - cancelLabel: 'Cancel', - }, - (event, context) => { - console.log(event.values); - context.ui.showToast('Thanks!'); - } - ); - - Devvit.addMenuItem({ - location: 'subreddit', - label: 'One of everything form', - onPress: (_event, context) => { - context.ui.showForm(exampleForm); - }, - }); - ``` - - ### Image uploads - - - - **Client-side approach:** +**Client-side approach:** ```ts title="client/index.ts" import { showForm } from '@devvit/web/client'; @@ -1549,28 +1188,3 @@ This example includes one of each of the [supported field types](#supported-fiel - - - ```tsx - import { Devvit } from '@devvit/public-api'; - - const form = Devvit.createForm( - { - title: 'Upload an image!', - fields: [ - { - name: 'myImage', - type: 'image', // This tells the form to expect an image - label: 'Image goes here', - required: true, - }, - ], - }, - (event, context) => { - // returns an i.redd.it URL - const imageUrl = event.values.myImage; - } - ); - ``` - - diff --git a/versioned_docs/version-0.12/capabilities/client/menu-actions.mdx b/versioned_docs/version-0.12/capabilities/client/menu-actions.mdx index c842d410..348944e8 100644 --- a/versioned_docs/version-0.12/capabilities/client/menu-actions.mdx +++ b/versioned_docs/version-0.12/capabilities/client/menu-actions.mdx @@ -11,10 +11,7 @@ Add an item to the three dot menu for posts, comments, or subreddits. Menu actio **For most menu actions, use direct client effects.** These provide immediate responses and are perfect for simple actions that don't require server processing. - - - - **Menu items defined in devvit.json:** +**Menu items defined in devvit.json:** ```json title="devvit.json" { @@ -74,53 +71,6 @@ app.post( - - - - ```tsx - import { Devvit } from '@devvit/public-api'; - -// Simple menu action with direct client effects -Devvit.addMenuItem({ -label: 'Show user info', -location: 'post', // 'post', 'comment', 'subreddit', or array -onPress: async (event, context) => { -// Direct client effect - no server processing needed - context.ui.showToast('Menu action clicked!'); -}, -}); - -// Menu action with form -const surveyForm = Devvit.createForm( -{ -fields: [ -{ -type: 'string', -name: 'feedback', -label: 'Your feedback', -}, -], -}, -(event, context) => { -// onSubmit handler -context.ui.showToast({ text: `Thanks for the feedback: ${event.values.feedback}` }); -} -); - -Devvit.addMenuItem({ -label: 'Quick survey', -location: 'subreddit', -forUserType: 'moderator', // Optional: restrict to moderators -onPress: async (event, context) => { -context.ui.showForm(surveyForm); -}, -}); - -```` - - - - ## Supported contexts You can decide where the menu action shows up by specifying the location property. @@ -139,9 +89,6 @@ For moderator permission security, when opening a form from a menu action with ` In Devvit Web, your menu item should respond with a client side effect to give feedback to users. This is available as a UIResponse as you do not have access to the `@devvit/web/client` library from your server endpoints. - - - **Menu items with server processing:** ```json title="devvit.json" @@ -242,45 +189,6 @@ app.post( - - - - For Devvit Blocks, use the direct context approach even for complex workflows: - -```tsx -Devvit.addMenuItem({ - label: "Process and validate data", - location: "post", // 'post', 'comment', 'subreddit', or array - forUserType: "moderator", // Optional: restrict to moderators - onPress: async (event, context) => { - try { - // Perform server-side processing - const userData = await validateAndProcessData(); - - // Show form with server-fetched data - const result = await context.ui.showForm( - { - fields: [ - { - type: "string", - name: "processedData", - label: "Processed Data", - }, - ], - }, - (values) => { - context.ui.showToast(`Processed: ${values.processedData}`); - }, - ); - } catch (error) { - context.ui.showToast("Processing failed. Please try again."); - } - }, -}); -``` - - - ### Menu response examples diff --git a/versioned_docs/version-0.12/capabilities/client/navigation.mdx b/versioned_docs/version-0.12/capabilities/client/navigation.mdx index 3b143ed2..0a2cf3cb 100644 --- a/versioned_docs/version-0.12/capabilities/client/navigation.mdx +++ b/versioned_docs/version-0.12/capabilities/client/navigation.mdx @@ -1,6 +1,3 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - # Navigation Use navigation functions to redirect users to Reddit content or external websites in response to user actions, such as button clicks. You can redirect to a `url` string or to objects such as [`Subreddit`](/api/redditapi/models/classes/Subreddit.md), [`Post`](/api/redditapi/models/classes/Post.md), or [`Comment`](/api/redditapi/models/classes/Comment.md). @@ -13,135 +10,32 @@ When linking to Reddit content, the navigation function requires the app account ## Basic navigation - - - ```ts title="client/index.ts" - import { navigateTo } from '@devvit/web/client'; - - // Navigate to external URLs - navigateTo('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); - - // Navigate to Reddit URLs - navigateTo('https://www.reddit.com/r/movies/comments/tzxev3/'); - - // Navigate to Reddit objects - async function goToPost() { - const post = await fetch('/api/getPost').then(r => r.json()); - navigateTo(post); - } - - // Use in button handlers or user interactions - function handleNavigateClick() { - navigateTo('https://www.reddit.com/r/webdev'); - } - ``` - - ### Parameters - - **`navigateTo(target)`** - - - `target`: Either a URL string or a Reddit object (Subreddit, Post, Comment) - - - - ```tsx - import { Devvit } from '@devvit/public-api'; - - Devvit.configure({ redditAPI: true }); - - // Navigate to URL - Devvit.addMenuItem({ - label: 'Navigate to url', - location: 'subreddit', - onPress: async (_event, context) => { - const url = 'https://www.reddit.com/r/movies/comments/tzxev3/'; - context.ui.navigateTo(url); - }, - }); - - // Navigate to subreddit - Devvit.addMenuItem({ - label: 'Navigate to subreddit', - location: 'subreddit', - onPress: async (_event, context) => { - const subredditId = 't5_2qh1o'; - const subreddit = await context.reddit.getSubredditById(subredditId); - context.ui.navigateTo(subreddit); - }, - }); - - // Navigate to post - Devvit.addMenuItem({ - label: 'Navigate to post', - location: 'subreddit', - onPress: async (_event, context) => { - const postId = 't3_tzxev3'; - const post = await context.reddit.getPostById(postId); - context.ui.navigateTo(post); - }, - }); - - // Navigate to comment - Devvit.addMenuItem({ - label: 'Navigate to comment', - location: 'subreddit', - onPress: async (_event, context) => { - const commentId = 't1_i426ob1'; - const comment = await context.reddit.getCommentById(commentId); - context.ui.navigateTo(comment); - }, - }); - - // Interactive post with navigation - // addCustomPostType() is deprecated and will be unsupported. It will not work after June 30. View the announcement below this example. - Devvit.addCustomPostType({ - name: 'Navigation Post', - render: (context) => { - return ( - - - - ); - }, - }); - - // Menu action to create interactive post - Devvit.addMenuItem({ - label: 'Add navigation post', - location: 'subreddit', - onPress: async (_event, context) => { - const subreddit = await context.reddit.getCurrentSubreddit(); - await context.reddit.submitPost({ - title: 'Navigate to post', - subredditName: subreddit.name, - preview: ( - - Loading ... - - ), - }); - context.ui.showToast('Created post!'); - }, - }); - ``` - [View `addCustomPostType` deprecation announcement.](https://www.reddit.com/r/Devvit/comments/1r3xcm2/devvit_web_and_the_future_of_devvit/) - - ### Parameters - - **`context.ui.navigateTo(target)`** - - - `target`: Either a URL string or a Reddit object (Subreddit, Post, Comment) - - - +```ts title="client/index.ts" +import { navigateTo } from '@devvit/web/client'; + +// Navigate to external URLs +navigateTo('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); + +// Navigate to Reddit URLs +navigateTo('https://www.reddit.com/r/movies/comments/tzxev3/'); + +// Navigate to Reddit objects +async function goToPost() { + const post = await fetch('/api/getPost').then(r => r.json()); + navigateTo(post); +} + +// Use in button handlers or user interactions +function handleNavigateClick() { + navigateTo('https://www.reddit.com/r/webdev'); +} +``` + +### Parameters + +**`navigateTo(target)`** + +- `target`: Either a URL string or a Reddit object (Subreddit, Post, Comment) :::tip Menu response navigation For navigation in menu response workflows (when you need server processing before navigation), see the [Menu Actions](./menu-actions.mdx) documentation. diff --git a/versioned_docs/version-0.12/capabilities/client/overview.mdx b/versioned_docs/version-0.12/capabilities/client/overview.mdx index 72746638..5d645d5d 100644 --- a/versioned_docs/version-0.12/capabilities/client/overview.mdx +++ b/versioned_docs/version-0.12/capabilities/client/overview.mdx @@ -1,91 +1,35 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - # Client Overview Client-side effects enable your Devvit app to provide interactive feedback and navigation to users. These effects include showing toasts, displaying forms, navigating to different pages, and more. - - - - Import client functions from `@devvit/web/client`: - - ```ts title="client/index.ts" - import { showToast, showForm, navigateTo } from '@devvit/web/client'; - - // Show a toast notification - showToast('Hello from Devvit Web!'); - - // Navigate to a URL - navigateTo('https://www.reddit.com/r/webdev'); - - // Show a form and handle response - const result = await showForm({ - form: { - fields: [ - { - type: 'string', - name: 'username', - label: 'Username' - } - ] - } - }); - - if (result) { - console.log('Form submitted:', result.username); +Import client functions from `@devvit/web/client`: + +```ts title="client/index.ts" +import { showToast, showForm, navigateTo } from '@devvit/web/client'; + +// Show a toast notification +showToast('Hello from Devvit Web!'); + +// Navigate to a URL +navigateTo('https://www.reddit.com/r/webdev'); + +// Show a form and handle response +const result = await showForm({ + form: { + fields: [ + { + type: 'string', + name: 'username', + label: 'Username' + } + ] } - ``` - - - - - Use context methods in event handlers and component render functions: - - ```tsx - import { Devvit } from '@devvit/public-api'; - - // In a custom post component - // addCustomPostType() is deprecated and will be unsupported. It will not work after June 30. View the announcement below this example. - Devvit.addCustomPostType({ - name: 'Interactive Post', - render: (context) => { - return ( - - - - - ); - }, - }); - ``` - [View `addCustomPostType` deprecation announcement.](https://www.reddit.com/r/Devvit/comments/1r3xcm2/devvit_web_and_the_future_of_devvit/) - - - +}); + +if (result) { + console.log('Form submitted:', result.username); +} +``` ## Available client effects diff --git a/versioned_docs/version-0.12/capabilities/client/toasts.mdx b/versioned_docs/version-0.12/capabilities/client/toasts.mdx index 6d94f099..a0ed6194 100644 --- a/versioned_docs/version-0.12/capabilities/client/toasts.mdx +++ b/versioned_docs/version-0.12/capabilities/client/toasts.mdx @@ -1,6 +1,3 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - # Toasts Display temporary notification messages to users at the bottom of the screen. @@ -24,108 +21,44 @@ Toasts will not work from scheduled jobs or triggers. ## Basic toast usage - - - ```ts - import { showToast } from '@devvit/web/client'; - - // Simple text toast - showToast('Operation completed successfully!'); - - // Toast with custom appearance - showToast({ - text: 'Data saved successfully!', - appearance: 'success', // 'neutral' | 'success' - }); - - // Use in button handlers or user interactions - function handleButtonClick() { - try { - // Perform some operation - processUserData(); - - showToast({ - text: 'Your data has been processed!', - appearance: 'success' - }); - } catch (error) { - showToast('Something went wrong. Please try again.'); - } +```ts +import { showToast } from '@devvit/web/client'; + +// Simple text toast +showToast('Operation completed successfully!'); + +// Toast with custom appearance +showToast({ + text: 'Data saved successfully!', + appearance: 'success', // 'neutral' | 'success' +}); + +// Use in button handlers or user interactions +function handleButtonClick() { + try { + // Perform some operation + processUserData(); + + showToast({ + text: 'Your data has been processed!', + appearance: 'success' + }); + } catch (error) { + showToast('Something went wrong. Please try again.'); } - ``` - - ### Parameters - - **`showToast(textOrToast)`** - - - `textOrToast`: Either a string message or a `Toast` object - - **Toast Object Properties:** - - - `text` (string): The message to display - - `appearance` (string, optional): The visual style (`'neutral'` | `'success'`). Defaults to `'neutral'` - - - - ```ts - import { Devvit } from '@devvit/public-api'; - - // Example with menu action - Devvit.addMenuItem({ - label: 'Save Data', - location: 'post', - forUserType: 'moderator', - onPress: async (event, context) => { - try { - // Perform some operation - await performAction(); - - // Show success toast - context.ui.showToast({ - text: 'Data saved successfully!', - appearance: 'success' - }); - } catch (error) { - // Show error toast - context.ui.showToast('Something went wrong. Please try again.'); - } - }, - }); - - // Example in blocks - // addCustomPostType() is deprecated and will be unsupported. It will not work after June 30. View the announcement below this example. - Devvit.addCustomPostType({ - name: 'My Custom Post', - render: (context) => { - return ( - - - - ); - }, - }); - ``` - [View `addCustomPostType` deprecation announcement.](https://www.reddit.com/r/Devvit/comments/1r3xcm2/devvit_web_and_the_future_of_devvit/) - - ### Parameters - - **`context.ui.showToast(textOrToast)`** - - - `textOrToast`: Either a string message or a `Toast` object - - **Toast Object Properties:** - - - `text` (string): The message to display - - `appearance` (string, optional): The visual style (`'neutral'` | `'success'`). Defaults to `'neutral'` - - - +} +``` + +### Parameters + +**`showToast(textOrToast)`** + +- `textOrToast`: Either a string message or a `Toast` object + +**Toast Object Properties:** + +- `text` (string): The message to display +- `appearance` (string, optional): The visual style (`'neutral'` | `'success'`). Defaults to `'neutral'` :::tip Menu response toasts For toasts in menu response workflows (when you need server processing before showing toasts), see the [Menu Actions](./menu-actions.mdx) documentation. diff --git a/versioned_docs/version-0.12/capabilities/server/cache-helper.mdx b/versioned_docs/version-0.12/capabilities/server/cache-helper.mdx index c7724bfe..a6e4e5ce 100644 --- a/versioned_docs/version-0.12/capabilities/server/cache-helper.mdx +++ b/versioned_docs/version-0.12/capabilities/server/cache-helper.mdx @@ -14,56 +14,26 @@ Under the covers, it's Redis plus a local in-memory write-through cache. This pr ## Usage - - - You can import cache helper from `@devvit/web/server` in your server source files. The cache helper is not available client-side, so you will see an error if you try to import it in client source files. +You can import cache helper from `@devvit/web/server` in your server source files. The cache helper is not available client-side, so you will see an error if you try to import it in client source files. - ```tsx - import { cache } from '@devvit/web/server'; - ``` - - - You need to enable [Redis](../server/redis.mdx) in order to use Cache helper. - - ```tsx - Devvit.configure({ - redis: true, - // other capabilities - }); - ``` - - +```tsx +import { cache } from '@devvit/web/server'; +``` ## Parameters -The cache takes a key and a TTL regardless of whether you are using Devvit Web or Devvit Blocks. The only difference is that **the TTL is in seconds for Devvit Web** and **milliseconds for Devvit Blocks / Mod Tools**. - - - +The cache takes a key and a TTL: | **Parameters** | **Description** | | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `key` | This is a string that identifies a cached response. Instead of making a real request, the app gets the cached response with the key you provide. Make sure to use different keys for different data. For example, if you’re saving post-specific data, add the postId to the cache key, like this: `post_data_${postId})`. | | `ttl` | Time to live is the number of **seconds** the cached response is expected to be relevant. Once the cached response expires, it will be voided and a real request is made to populate the cache again. You can treat it as a threshold, where ttl of 30 would mean that a request is done no more than once per 30 seconds. | - - - -| **Parameters** | **Description** | -| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `key` | This is a string that identifies a cached response. Instead of making a real request, the app gets the cached response with the key you provide. Make sure to use different keys for different data. For example, if you’re saving post-specific data, add the postId to the cache key, like this: `post_data_${postId})`. | -| `ttl` | Time to live is the number of **milliseconds** the cached response is expected to be relevant. Once the cached response expires, it will be voided and a real request is made to populate the cache again. You can treat it as a threshold, where ttl of 30000 would mean that a request is done no more than once per 30 seconds. | - - - - ## Example Here’s a way to set up in-app caching instead of using scheduler or interval to fetch. - - - - - - - ```tsx - import { Devvit, useState } from '@devvit/public-api'; - - Devvit.configure({ - redis: true, - http: true, // to use `fetch()` - // other capabilities - }); - - // addCustomPostType() is deprecated and will be unsupported. It will not work after June 30. View the announcement below this example. - Devvit.addCustomPostType({ - name: 'Name', - render: (context) => { - const [data, setData] = useState({}); - - async function getData() { - const result = await context.cache( - async () => { - const response = await fetch('https://example.com'); - if (!response.ok) { - throw Error(`HTTP error ${response.status}: ${response.statusText}`); - } - return await response.json(); - }, - { - key: context.userId!, - ttl: 10_000, // millis - } - ); - - setData(result); - } - - return ( - - - - {data.something} - - ); - }, - }); - - export default Devvit; - ``` - [View `addCustomPostType` deprecation announcement.](https://www.reddit.com/r/Devvit/comments/1r3xcm2/devvit_web_and_the_future_of_devvit/) - - diff --git a/versioned_docs/version-0.12/capabilities/server/http-fetch.mdx b/versioned_docs/version-0.12/capabilities/server/http-fetch.mdx index 3ea13c22..a3c024c3 100644 --- a/versioned_docs/version-0.12/capabilities/server/http-fetch.mdx +++ b/versioned_docs/version-0.12/capabilities/server/http-fetch.mdx @@ -1,6 +1,3 @@ -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - # HTTP Fetch Make requests to allow-listed external domains. @@ -9,33 +6,17 @@ Your Devvit app can make network requests to access allow-listed external domain ## Enabling HTTP fetch calls - - - ```json title="devvit.json" - { - ... - "permissions": { - "http": { - "enable": true, - "domains": ["my-site.com", "another-domain.net"] - } +```json title="devvit.json" +{ + ... + "permissions": { + "http": { + "enable": true, + "domains": ["my-site.com", "another-domain.net"] } } - ``` - - - ```ts - import { Devvit } from '@devvit/public-api'; - -Devvit.configure({ -http: { -domains: ['my-site.com', 'another-domain.net'], -}, -}); - -```` - - +} +``` ### Requesting a domain to be allow-listed @@ -64,25 +45,22 @@ Apps must request each individual domain that it intends to fetch, even if the d ## Example usage - - - - Devvit Web applications have two different contexts for using fetch: +Devvit Web applications have two different contexts for using fetch: - ### Server-side fetch +### Server-side fetch - Server-side fetch allows your app to make HTTP requests to allowlisted external domains from your server-side code (e.g., API routes, server actions): +Server-side fetch allows your app to make HTTP requests to allowlisted external domains from your server-side code (e.g., API routes, server actions): ```tsx title="server/index.ts" - const response = await fetch('https://example.com/api/data', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); +const response = await fetch('https://example.com/api/data', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, +}); - const data = await response.json(); - console.log('External API response:', data); +const data = await response.json(); +console.log('External API response:', data); ```` ### Client-side fetch @@ -98,7 +76,7 @@ Client-side fetch has different restrictions and can only make requests to your ```tsx title="client/index.ts" const handleFetchData = async () => { - // ✅ Correct: Fetching your own webview's API endpoint + // Correct: fetching your own webview's API endpoint const response = await fetch("/api/user-data", { method: "GET", headers: { @@ -110,51 +88,13 @@ const handleFetchData = async () => { console.log("API response:", data); }; -// ❌ Incorrect: Cannot fetch external domains from client-side +// Incorrect: cannot fetch external domains from client-side // const response = await fetch('https://external-api.com/data'); -// ❌ Incorrect: Endpoint must end with /api +// Incorrect: endpoint must end with /api // const response = await fetch('/user-data'); ``` - - - - For Devvit Blocks and Mod Tools, fetch is available within menu actions, triggers, and other server-side contexts: - - ```ts - import { Devvit } from '@devvit/public-api'; - - Devvit.configure({ - http: { - domains: ['example.com'], - }, - }); - - Devvit.addMenuItem({ - location: 'comment', - label: 'Sample HTTP request', - onPress: async (_event, context) => { - console.log(`Comment ID: ${context.commentId}`); - const response = await fetch('https://example.com', { - method: 'post', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ content: context.commentId }), - }); - context.ui.showToast( - `Invoked HTTP request on comment: ${context.commentId}. Completed with status: ${response.status}` - ); - }, - }); - - export default Devvit; - ``` - - - - ## Troubleshooting If you see the following error, it means HTTP Fetch requests are hitting the internal timeout limits. To resolve this: diff --git a/versioned_docs/version-0.12/capabilities/server/media-uploads.mdx b/versioned_docs/version-0.12/capabilities/server/media-uploads.mdx index 7316f1a7..b9e61a94 100644 --- a/versioned_docs/version-0.12/capabilities/server/media-uploads.mdx +++ b/versioned_docs/version-0.12/capabilities/server/media-uploads.mdx @@ -1,6 +1,3 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - # Media Uploads :::warning @@ -24,29 +21,16 @@ Enable the `media` permission in your `devvit.json` file. ## Using media uploads On the server, you can pass the URL of any remotely hosted image (even if its not hosted on Reddit) to the `media.upload` function. This function will return a Reddit URL. Both HTTP and data URLs are supported. - - - ```ts title="server/index.ts" - import { media } from '@devvit/media'; - function submitImage() { - const response = await media.upload({ - url: 'https://media2.giphy.com/media/xTiN0CNHgoRf1Ha7CM/giphy.gif', - type: 'gif', - }); - } - ``` - - - ```ts - import { Devvit } from '@devvit/public-api'; +```ts title="server/index.ts" +import { media } from '@devvit/media'; +function submitImage() { const response = await media.upload({ url: 'https://media2.giphy.com/media/xTiN0CNHgoRf1Ha7CM/giphy.gif', type: 'gif', }); - ``` - - +} +``` ## Canvas screenshots diff --git a/versioned_docs/version-0.12/capabilities/server/reddit-api.mdx b/versioned_docs/version-0.12/capabilities/server/reddit-api.mdx index cb9191ff..a2b32781 100644 --- a/versioned_docs/version-0.12/capabilities/server/reddit-api.mdx +++ b/versioned_docs/version-0.12/capabilities/server/reddit-api.mdx @@ -1,6 +1,3 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - # Reddit API Overview The Reddit API allows you to read and write Reddit content such as posts / comments / upvotes, in order to integrate your app's behavior with the content of the community it's installed in. @@ -24,32 +21,17 @@ Devvit apps cannot access certain private user data. This data is private to the Here's how to obtain a reference to the Reddit client - - - ```json title="devvit.json" - { - "permissions": { - "reddit": true - } +```json title="devvit.json" +{ + "permissions": { + "reddit": true } - ``` - ```ts title="server/index.ts" - import { reddit } from '@devvit/reddit'; - ``` - - - ```ts title="devvit.tsx" - import { Devvit } from '@devvit/public-api'; - - Devvit.configure({ - redditAPI: true, - }); +} +``` - //Then, in any function that has a reference to Devvit.Context: - const reddit = context.reddit; - ``` - - +```ts title="server/index.ts" +import { reddit } from '@devvit/reddit'; +``` ## Reddit Thing IDs @@ -79,9 +61,7 @@ const parentId = comment.parentId; // 't3_abc123' or 't1_def456' ## Example usage ### Submitting a post - - - ```ts +```ts import { Devvit } from '@devvit/public-api'; import { context, reddit } from '@devvit/web/server'; @@ -100,36 +80,7 @@ export const createPost = async () => { entry: 'default', }); }; - ``` - - - -```tsx -import { Devvit } from '@devvit/public-api'; - -Devvit.configure({ - redditAPI: true, -}); - -function createPost(context: Devvit.Context) { - const currentSubreddit = context.reddit.getCurrentSubreddit(); - if (!currentSubreddit) { - throw new Error('No subreddit found'); - } - - return context.reddit.submitPost({ - title: 'My custom post', - subredditName: currentSubreddit.name, - preview: ( - - Loading... - - ), - }); -} ``` - - ### Submitting a comment @@ -138,42 +89,19 @@ Auto-comments should be used to spark conversation in the post comments, but you ::: - - - ```ts - import { context, reddit } from '@devvit/web/server'; +```ts +import { context, reddit } from '@devvit/web/server'; - export const createComment = async () => { - const { subredditName } = context; - if (!subredditName) { - throw new Error('subredditName is required'); - } +export const createComment = async () => { + const { subredditName } = context; + if (!subredditName) { + throw new Error('subredditName is required'); + } - reddit.submitComment({ - postId: 't3_123456', // Replace with the actual post ID - text: 'This is a comment from a Devvit app', - runAs: 'USER' // Optional: specify the user to run as - }); - }; -``` - - - ```tsx - import { Devvit } from '@devvit/public-api'; - - Devvit.configure({ - redditAPI: true, - }); - - function createComment(context: Devvit.Context) { - const { reddit } = context; - - reddit.submitComment({ - postId: 't3_123456', // Replace with the actual post ID - text: 'This is a comment from a Devvit app', - runAs: RunAs.USER, // Optional: specify the user to run as - }); - }; + reddit.submitComment({ + postId: 't3_123456', // Replace with the actual post ID + text: 'This is a comment from a Devvit app', + runAs: 'USER' // Optional: specify the user to run as + }); +}; ``` - - diff --git a/versioned_docs/version-0.12/capabilities/server/redis.mdx b/versioned_docs/version-0.12/capabilities/server/redis.mdx index bcca403a..61a62107 100644 --- a/versioned_docs/version-0.12/capabilities/server/redis.mdx +++ b/versioned_docs/version-0.12/capabilities/server/redis.mdx @@ -30,9 +30,7 @@ All limits are applied at a per-installation granularity. ### Menu actions - - - ```js title="devvit.json" +```js title="devvit.json" { "menuActions": [ { @@ -89,22 +87,6 @@ All limits are applied at a per-installation granularity. - - - ```ts - Devvit.addMenuItem({ - location: 'subreddit', - label: 'Test Redis', - onPress: async (event, { redis }) => { - const key = 'hello'; - await redis.set(key, 'world'); - const value = await redis.get(key); - console.log(`${key}: ${value}`); - }, - }); - ``` - - ### Games @@ -117,11 +99,9 @@ You can take a look at this [Game Template](https://github.com/reddit/devvit-tem Not all Redis features are supported. If you would like to request a specific Redis feature, please reach out to our team [via modmail](https://www.reddit.com/message/compose/?to=%2Fr%2FDevvit) or Discord. ::: -For all examples below, we assume that you already have obtained a Redis Client. Here's how to obtain a Redis Client for Devvit Web, Devvit Blocks and Mod Tools: +For all examples below, we assume that you already have obtained a Redis Client. Here's how to obtain a Redis Client: - - - ```json title="devvit.json" +```json title="devvit.json" { "permissions": { "redis": true @@ -131,20 +111,6 @@ For all examples below, we assume that you already have obtained a Redis Client. ```ts title="server/index.ts" import { redis } from '@devvit/redis'; ``` - - - ```ts title="devvit.tsx" - import { Devvit } from '@devvit/public-api'; - - Devvit.configure({ - redis: true, - }); - - //Then, in any function that has a reference to Devvit.Context: - const redis = context.redis; - ``` - - ### Simple read/write diff --git a/versioned_docs/version-0.12/capabilities/server/triggers.mdx b/versioned_docs/version-0.12/capabilities/server/triggers.mdx index f9be593c..409a6d18 100644 --- a/versioned_docs/version-0.12/capabilities/server/triggers.mdx +++ b/versioned_docs/version-0.12/capabilities/server/triggers.mdx @@ -39,9 +39,7 @@ A full list of events and their payloads can be found in the [EventTypes documen ### 1. Add triggers and endpoints to `devvit.json` - - - Declare the triggers and their corresponding endpoints in your `devvit.json`: +Declare the triggers and their corresponding endpoints in your `devvit.json`: ```json "triggers": { @@ -51,28 +49,10 @@ A full list of events and their payloads can be found in the [EventTypes documen } ``` - - - Declare the triggers in your `devvit.json`: - -```json -{ - "name": "your-app-name", - "blocks": { - "entry": "src/main.tsx", - "triggers": ["onPostCreate"] - } -} -``` - - - ### 2. Handle trigger events in your server logic - - - Listen for the events in your server and access the data passed into the request: +Listen for the events in your server and access the data passed into the request: ( - - - Handle trigger events in your main file. Example (`src/main.tsx`): - -```tsx -import { Devvit } from "@devvit/public-api"; - -// Handling a PostSubmit event -Devvit.addTrigger({ - event: "PostSubmit", // Event name from above - onEvent: async (event) => { - console.log(`Received OnPostSubmit event:\n${JSON.stringify(event)}`); - }, -}); - -// Handling multiple events: PostUpdate and PostReport -Devvit.addTrigger({ - events: ["PostUpdate", "PostReport"], // An array of events - onEvent: async (event) => { - if (event.type == "PostUpdate") { - console.log(`Received OnPostUpdate event:\n${JSON.stringify(request)}`); - } else if (event.type === "PostReport") { - console.log(`Received OnPostReport event:\n${JSON.stringify(request)}`); - } - }, -}); - -export default Devvit; -``` - - - - ## Best practices - Avoid creating recursive triggers that could cause infinite loops or crashes (for example, a comment trigger that creates a comment).