Using `Notifications.createCategoryAsync`, yet push notifications do not have actions / buttons

In an effort to remove as much variation as possible from this, I copied sample code directly from expo and placed in the constructor method of the root App component (but have experimented with it everywhere).

Notifications.createCategoryAsync('welcome', [
   { 
      actionId: 'tada',
      buttonTitle: '🎉', 
      isDestructive: false, 
      isAuthenticationRequired: false, },
   { 
      actionId: 'heart_eyes', 
      buttonTitle: '😍', 
      isDestructive: false,  
      isAuthenticationRequired: true, },
   ])

Then, using curl:

curl -H "Content-Type: application/json" \
    -X POST https://exp.host/--/api/v2/push/send \
    -d "{ \"categoryId\":\"welcome\",\"to\": \"ExponentPushToken[***]\",\
     \"title\": \"Thing requires your approval\", \"body\": \"Please approve or deny\",\
     \"data\": \"{\\\"id\\\": 11111}\"}"

I’ve also tried category in place of categoryId and tried using the Expo Push Notification tool.

In all cases, the push notification comes through immediately—but it’s never interactive. Just a notification. I never get the “Approve” or “Deny” buttons.

Even if you don’t know the answer, but have something I might experiment with, I’m all ears! I’d really like to get this working!

I created a snack that fails to work. Interactive Notifications - Snack

The issue turns out to be in the triggering of the Push Notification. The snack is updated and works (on iOS at least). Snack source below for posterity.

import * as React from 'react';
import { TextInput, View, Text, Button } from 'react-native';
import { Notifications, Permissions, Constants } from 'expo';

Notifications.createCategoryAsync('welcome', [
  {
    actionId: 'one',
    buttonTitle: 'Button One',
    isDestructive: true,
    isAuthenticationRequired: false,
  },
  {
    actionId: 'two',
    buttonTitle: 'Button Two',
    isDestructive: false,
    isAuthenticationRequired: true,
  },
  {
    actionId: 'three',
    buttonTitle: 'Three',
    textInput: { submitButtonTitle: 'Three', placeholder: 'Type Something' },
    isAuthenticationRequired: false,
  },
])
  .then(() => {
    console.log(`Category 'welcome' created!`);
  })
  .catch(() => {
    console.log(`Category 'welcome' not created!`);
  });

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { token: null, notificationBody: null };
  }

  componentDidMount() {
    Permissions.getAsync(Permissions.NOTIFICATIONS).then(obj => {
      if (obj.status !== 'granted') {
        Permissions.askAsync(Permissions.NOTIFICATIONS).then(obj => {
          if (obj.status !== 'granted') {
            return;
          }
          Notifications.getExpoPushTokenAsync().then(token => {
            this.setState({ token: token });
          });
        });
      } else {
        Notifications.getExpoPushTokenAsync().then(token => {
          this.setState({ token: token });
        });
      }
    });
    this._notificationSubscription = Notifications.addListener(
      this.handleNotification
    );
  }

  render() {
    return (
      <View style={{ paddingTop: 50 }}>
        <View style={style.card}>
          <Text style={style.label}>Device ID</Text>
          <TextInput style={style.input}>{this.state.token}</TextInput>
        </View>
        <View style={style.card}>
          <Text style={style.label}>Notification Payload</Text>
          <TextInput multiline={true} style={[style.input, { height: 100 }]}>
            {this.state.notificationBody}
          </TextInput>
        </View>
        <View style={[style.card, { flexDirection: 'row' }]}>
          <Button onPress={this.notify} title="Notify" />
          <Button onPress={this.delayNotify} title="Delayed Notify" />
        </View>
      </View>
    );
  }

  notify = () => {
    const { token } = this.state;
    fetch('https://exp.host/--/api/v2/push/send', {
      body: JSON.stringify({
        to: token,
        title: 'Test Title',
        body: 'Test Body',
        data: { random: Math.random() },
        _category: `${Constants.manifest.id}:welcome`,
      }),
      headers: {
        'Content-Type': 'application/json',
      },
      method: 'POST',
    });
  };

  delayNotify = () => {
    setTimeout(() => {
      this.notify();
    }, 2500);
  };

  handleNotification = notification => {
    this.setState({ notificationBody: JSON.stringify(notification) });
  };
}

const style = {
  card: {
    padding: 15,
    margin: 15,
    shadowColor: 'rgb(0,0,0)',
    shadowRadius: 10,
    shadowOffset: { width: 0, height: 0 },
    shadowOpacity: 0.1,
    backgroundColor: 'white',
    borderRadius: 5,
  },
  input: {
    borderColor: '#eee',
    borderWidth: 1,
    padding: 10,
    borderRadius: 5,
  },
  label: {
    fontWeight: '600',
    fontSize: 16,
    marginBottom: 10,
  },
};
1 Like

Glad you got it resolved and thanks for the sharing the updates with the community!

Has anyone been able to get the actions working on Android? I’ve tried everything in the docs and past issues that people mentioned.

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.