Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/activities/building-an-activity.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ Back in your app's settings, click on the **URL Mappings** page under **Activiti
|--------|-----------------------------------------|
| `/` | `funky-jogging-bunny.trycloudflare.com` |

Read details about URL Mapping [in the development guide](/docs/activities/development-guides#url-mapping).
Read details about URL Mapping [in the development guide](/docs/activities/development-guides/local-development#url-mapping).

### Enable Activities

Expand All @@ -330,7 +330,7 @@ Find the first checkbox, labeled `Enable Activities`. Turn it on 🎉

When you enable Activities for your app, a [default Entry Point command](/docs/interactions/application-commands#default-entry-point-command) called "Launch" is automatically created. This [Entry Point command](/docs/interactions/application-commands#entry-point-commands) is the primary way for users to launch your Activity in Discord.

By default, interactions with this command will result in Discord opening your Activity for the user and posting a message in the channel where it was launched from. However, if you prefer to handle the interactions in your app, you can update the [`handler` field](/docs/interactions/application-commands#entry-point-handlers) or create your own. Additional details are in the Entry Point command [documentation](/docs/interactions/application-commands#entry-point-commands) and [development guide](/docs/activities/development-guides#setting-up-an-entry-point-command).
By default, interactions with this command will result in Discord opening your Activity for the user and posting a message in the channel where it was launched from. However, if you prefer to handle the interactions in your app, you can update the [`handler` field](/docs/interactions/application-commands#entry-point-handlers) or create your own. Additional details are in the Entry Point command [documentation](/docs/interactions/application-commands#entry-point-commands) and [development guide](/docs/activities/development-guides/user-actions#setting-up-an-entry-point-command).


### Running your Activity in Discord
Expand All @@ -346,7 +346,7 @@ Clicking on your app will launch your locally running app from inside Discord!
:::info
**Customizing your Activity**

If you'd like to set images for your Activity, you can learn how to do that [here](/docs/activities/development-guides#setting-up-activity-metadata).
If you'd like to set images for your Activity, you can learn how to do that [here](/docs/activities/development-guides/assets-and-metadata#setting-up-activity-metadata).
:::

We're looking pretty good so far, but we haven't wired up any Discord functionality yet. Let's do that next.
Expand Down Expand Up @@ -461,7 +461,7 @@ Before we call your backend activity server, we need to be aware of the Discord
For this tutorial, we are going to prefix the API call to `/api/token/` with `/.proxy`, but you can also use the SDK's `patchUrlMappings()` method to automatically prefix calls to your external resources for the proxy.
:::

Learn more about this topic in the guides for [Constructing a Full URL](/docs/activities/development-guides#construct-a-full-url) and [Using External Resources](/docs/activities/development-guides#using-external-resources).
Learn more about this topic in the guides for [Constructing a Full URL](/docs/activities/development-guides/networking#construct-a-full-url) and [Using External Resources](/docs/activities/development-guides/networking#using-external-resources).

### Calling your backend server from your client

Expand Down Expand Up @@ -515,7 +515,7 @@ async function setupDiscordSdk() {
// Retrieve an access_token from your activity's server
// Note: We need to prefix our backend `/api/token` route with `/.proxy` to stay compliant with the CSP.
// Read more about constructing a full URL and using external resources at
// https://discord.com/developers/docs/activities/development-guides#construct-a-full-url
// https://discord.com/developers/docs/activities/development-guides/networking#construct-a-full-url
const response = await fetch("/.proxy/api/token", {
method: "POST",
headers: {
Expand Down
1,136 changes: 44 additions & 1,092 deletions docs/activities/development-guides.mdx

Large diffs are not rendered by default.

60 changes: 60 additions & 0 deletions docs/activities/development-guides/assets-and-metadata.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
sidebar_label: Assets and Metadata
---

[Home](/docs/intro) > [Activities](/docs/activities/overview) > [Development Guides](/docs/activities/development-guides) > {sidebar_label}

# Assets and Metadata

## Setting Up Activity Metadata

The Activity Shelf is where users can see what Activities can be played. It has various metadata and art assets that can be configured.

To update your app's metadata in the Discord Developer Portal, navigate to the `Settings -> General Information` tab of your app.

- **Application Name:** The publicly visible name of your app.
- **Application Icon:** The publicly visible icon for your app.
- **Application Description:** The application description is shown in the view of the Activity Shelf Item.
- **Max Participants:** The max participants indicate the maximum number of players for your application.
- Max Participants is displayed above the name in the 1-up view: `Up to X participants`.
- Leaving this field empty defaults to `Unlimited participants`.
- Max Participants is also displayed under the name in the 2-up view.

:::info
An app can have a different application name and avatar from the application's bot username and avatar. Both sets of metadata are public-facing and may be visible in various situations when a user interacts with your app. You can view your bot's username on the `Settings -> Bot` tab.
:::

---

## Setting Up Activity Art Assets

The Activity Shelf is where users can see what Activities can be played. It has various metadata and art assets that can be configured.

To update your app's embedded-specific art assets in the Discord Developer Portal, navigate to the `Activities -> Art Assets` tab of your app.

## Embedded Background

Used as a background overlay in Grid view. Artwork should be clustered around the edges of the image leaving space in the center of the image so the UI does not clash with it.

#### Specifications
- 16:9 aspect ratio
- At least 1024 pixels wide

## Cover Art

Used as the main image in the Activity Shelf. It is suggested that this image contain the title and some art in the background.

#### Specifications:
- Image can be displayed at both 16:9 and 13:11 aspect ratios
- At least 1024 pixels wide


## App Tile

There are two views of an application tile. The regular size tile (2-up tile) and the larger "featured" application tile (1-up tile).

## Video Preview

Hovering over the cover image should start playing a preview video of the Application. The preview videos should be no more than 10 seconds long. If no video is provided, nothing will happen as you hover over the application.

#### Specifications: 640 x 360, mp4 format, under 10 seconds long, under 1 MB in size
184 changes: 184 additions & 0 deletions docs/activities/development-guides/growth-and-referrals.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
---
sidebar_label: Growth and Referrals
---

[Home](/docs/intro) > [Activities](/docs/activities/overview) > [Development Guides](/docs/activities/development-guides) > {sidebar_label}

# Growth and Referrals

## Prompting Users to Share Incentivized Links

Incentivized sharing can help grow your Activity through network effects. You can use links in several different ways such as:

- **Referral links.** Users can copy referral links inside your Activity, which include their Discord user ID (`https://discord.com/activities/<your Activity ID>?referrer_id=123456789`), and they can send to their friends. If their friend accepts and starts playing your game, then you gift the referrer something inside your game.
- **Promotions.** You can run a temporary promotion on social media, where you offer a reward if they start playing now. Share a custom link on your social media (`https://discord.com/activities/<your Activity ID>?custom_id=social012025` ). Anyone who clicks that specific link receives something inside your game.
- **Social deep-links.** Currently, when users launch an Activity, they all land in the same place. Instead, you can start deep-linking to contextually relevant points in your game. For example, user A can copy a link inside your Activity for engaging other users (`https://discord.com/activities/<your Activity ID>?referrer_id=123456789&custom_id=visit-location`), and sends the link to their friends in a DM or channel. Then, user B who clicks the link gets taken directly to user A’s location.
- **Turn-based deep-links.** When you send an “it’s your turn” DM to a user, you can include a link which takes them directly to the right game instance and the turn they need to take.
- **Affiliate marketing.** You can work with affiliates (influencers, companies, etc) to advertise your game to their followings, and reward them via a custom link (`https://discord.com/activities/<your Activity ID>?custom_id=influencer1`). Then, for every user that starts playing because of said influencer, you can then pay out to the influencer.
- **Source attribution.** You can use the `custom_id` parameter to figure out how much traffic you’re getting from different marketing sources.

This guide covers implementing a referral link which will feature a reward system for users who share links and those who click them.

#### Implementation Overview

1. Create and track an incentivized link for a promotional campaign, then prompt users to share the link
2. Handle incoming referrals and grant valid rewards

#### Sharing Links

When implementing sharing, you'll need to:
1. Generate a unique ID for tracking the promotion
2. Call the [`shareLink`](/docs/developer-tools/embedded-app-sdk#sharelink) command
3. Track the share attempt

```javascript
// Generate a unique ID for this promotion
// This could be per-campaign, per-user, or per-share depending on your needs
const customId = await createPromotionalCustomId();

try {
const { success } = await discordSdk.commands.shareLink({
message: 'Click this link to redeem 5 free coins!',
custom_id: customId,
});

if (success) {
// Track successful share for analytics/limiting
await trackSuccessfulShare(customId);
}
} catch (error) {
// Handle share failures appropriately
console.error('Failed to share link:', error);
}
```

#### Handling Incoming Referrals

When a user clicks a shared link, your activity will launch with referral data available through the SDK:

```javascript
// Early in your activity's initialization
async function handleReferral() {
// Validate the referral data
if (!discordSdk.customId || !discordSdk.referrerId) {
return;
}

try {
// Verify this is a valid promotion and hasn't expired
const promotion = await validatePromotion(discordSdk.customId);
if (!promotion) {
console.log('Invalid or expired promotion');
return;
}

// Prevent self-referrals
if (discordSdk.referrerId === currentUserId) {
console.log('Self-referrals not allowed');
return;
}

// Grant rewards to both users
await grantRewards({
promotionId: discordSdk.customId,
referrerId: discordSdk.referrerId,
newUserId: currentUserId
});
} catch (error) {
console.error('Failed to process referral:', error);
}
}
```

#### Link Sharing Best Practices

- Generate unique, non-guessable `customId`s
- Track and validate referrals to prevent abuse
- Handle edge cases like expired promotions gracefully
- Consider implementing cool-down periods between shares
- Do not override the `referrer_id` query parameter directly. When present, `referrer_id` is expected to be a Discord snowflake-type user ID, otherwise it will be set to the message's author id.

---

## Creating and Managing Custom Incentivized Links

This guide covers creating a customizable [Incentivized Link](/docs/activities/development-guides/growth-and-referrals#prompting-users-to-share-incentivized-links) through the dev portal, and then retrieving the link to be able to share it off-platform. Incentivized Links are used to customize how the embed appears to users.

#### Creating a Link

1. In your Application's portal, visit the Custom Links page under the Activities heading in the navigation pane.
2. On the Custom Links page, click `Create New` to create a new link.
3. You will need to upload an image with an aspect ratio of 43:24.
4. Title, and description are also required.
5. `custom_id` is an optional field, an explicit `custom_id` query parameter on the link itself will always override the set `custom_id`.
6. Click Save.

#### Editing a Link

1. Click on a row to open up the modal with all of the data loaded in ready for your edits.
2. Change the description to something else.
3. Click Update.

#### Copying a Link

Once you're satisfied with your changes you can click on the copy icon on the row, it'll change colors to green indicating that it copied to your clipboard. You are now able to share this link anywhere. The link will look like: `https://discord.com/activities/<your Activity ID>?link_id=0-123456789`. Even if you've set a `custom_id`, it won't be explicitly included in the link but will be loaded once a user clicks on the link. You can then further shorten this URL if you'd like.

#### Deleting a Link

1. Click on the trash icon on the row of the link you're trying to delete.
2. You'll have a confirm dialog pop up.

:::warn
Deleting is irreversible and immediate. Ensure that your link isn't in active use before deleting and/or that your activity gracefully handles any click-throughs from the link.
:::

#### Best Practices

- Generate unique, non-guessable `customId`s
- Track and validate referrals to prevent abuse
- Gracefully handle expirations in your activity for any custom links that are limited time but still live off-platform.

#### User Experience

![custom-link-embed](images/activities/custom-link-embed.png)

Users will see an embed with your information displayed. Clicking "Play" opens the activity and passes through the `custom_id` you've set. A `referrer_id` will be present for links shared on Discord.

---

## Generating a Custom Link Within Your Activity

This guide covers creating a customizable [Incentivized Link](/docs/activities/development-guides/growth-and-referrals#prompting-users-to-share-incentivized-links) within your activity, and using the `shareLink` API to share the link.

* Allows you to customize the way the link is presented to users via the embed
* Can be generated on-demand within your activity
* Ephemeral, 30 day TTL
* Does not show up in the developer portal

#### Generating a Link

```javascript
// Convert an image array buffer to base64 string
const image = base64EncodedImage;

// Generate the quick activity link
const linkIdResponse = await fetch(`${env.discordAPI}/applications/${env.applicationId}/quick-links/`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
},
body: {
custom_id: 'user_123/game_456'
description: 'I just beat level 10 with a perfect score',
title: 'Check out my high score!',
image,
}
});
const {link_id} = await linkIdResponse.json();

// Open the Share modal with the generated link
const {success} = await discordSdk.commands.shareLink({
linkId: link_id
});
success ? console.log('User shared link!') : console.log('User did not share link!');
```
77 changes: 77 additions & 0 deletions docs/activities/development-guides/layout.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
sidebar_label: Layout
---

[Home](/docs/intro) > [Activities](/docs/activities/overview) > [Development Guides](/docs/activities/development-guides) > {sidebar_label}

# Layout

## Application Orientation

#### Locking Application Orientation

This SDK provides APIs for locking the application to specific orientations. The possible lock states are `UNLOCKED`, `PORTRAIT`, and `LANDSCAPE`. `lock_state` is the default lock state, and it affects the app orientation when the application is focused. `picture_in_picture_lock_state` determines the PIP aspect ratio, and `grid_lock_state` determines the grid tile aspect ratio for the application. When `picture_in_picture_lock_state` is not set, the application PIP falls back to `lock_state` to determine the aspect ratio. When `grid_lock_state` is not set, the application grid tile falls back to `picture_in_picture_lock_state` to determine its aspect ratio, and if `picture_in_picture_lock_state`is not set, it uses `lock_state`.

Calling `setOrientationLockState` with an `undefined` or omitted value for `picture_in_picture_lock_state` or `grid_lock_state` will not change the corresponding lock states for the application. Calling `setOrientationLockState` with a null value for `picture_in_picture_lock_state` or `grid_lock_state` will clear the application's corresponding lock states such that those layout modes will use the fallback lock states.

```javascript
import {DiscordSDK, Common} from '@discord/embedded-app-sdk';
const discordSdk = new DiscordSDK(clientId);
await discordSdk.ready();

// Set a default lock state
discordSdk.commands.setOrientationLockState({lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE});

// or set both a default lock state and a picture-in-picture lock state
discordSdk.commands.setOrientationLockState({
lock_state: Common.OrientationLockStateTypeObject.PORTRAIT,
picture_in_picture_lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE,
grid_lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE,
});
```

#### Configuring Default Orientation Lock State Through the Developer Portal

It's also possible to configure an application with a default orientation lock state via the Developer Portal. Using this method, the Discord app will apply the orientation lock when launching the application before the SDK has been initialized. This can create a smoother application launch flow where the application starts in the correct orientation rather than switching to the correct orientation after some delay after the application requests an orientation lock via the SDK. The Developer Portal supports setting a different default orientation lock states for phones versus tablets.

![default-orientation-lock-state](images/activities/default_orientation_lock_state.png)

#### Subscribing to Screen Orientation Updates

To listen to the screen orientation (which is sometimes different from the physical device orientation), subscribe to the `ORIENTATION_UPDATE` event. Discord will publish the current orientation upon event subscription, and it'll also publish any orientation changes that happen afterward.

```javascript
const handleOrientationUpdate = (update: {screen_orientation: number}) => {
switch (update.screen_orientation) {
case Common.OrientationTypeObject.PORTRAIT:
...
case Common.OrientationTypeObject.LANDSCAPE:
...
default:
...
}
}

discordSdk.subscribe('ORIENTATION_UPDATE', handleOrientationUpdate);
```

---

## Application Layout Mode

There are three layout modes that an application can be in: focused, picture-in-picture (PIP), or grid mode. Activities can subscribe to the layout mode to determine when to optionally change their layouts to optimize for each layout mode. Old Discord clients only support the `ACTIVITY_PIP_MODE_UPDATE` event, while new Discord clients support both `ACTIVITY_PIP_MODE_UPDATE` and `ACTIVITY_LAYOUT_MODE_UPDATE`. Use `subscribeToLayoutModeUpdatesCompat` and `unsubscribeFromLayoutModeUpdatesCompat` to subscribe to both events with backward compatibility for old Discord clients that only support `ACTIVITY_PIP_MODE_UPDATE`. Here's an example using React:

```javascript
export default function LayoutMode() {
const handleLayoutModeUpdate = React.useCallback((update: {layout_mode: number}) => {
...
}, []);

React.useEffect(() => {
discordSdk.subscribeToLayoutModeUpdatesCompat(handleLayoutModeUpdate);
return () => {
discordSdk.unsubscribeFromLayoutModeUpdatesCompat(handleLayoutModeUpdate);
};
}, [handleLayoutModeUpdate]);
}
```
Loading