diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..9485214
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+## 1.9.0
+
+### New Features
+
+- **New Feature: App Icon Badge Count** - Show MRR, user counts, stock prices, and more on your ActivitySmith app icon.
diff --git a/README.md b/README.md
index 48aa3ec..d4f8de5 100644
--- a/README.md
+++ b/README.md
@@ -20,8 +20,9 @@ See [API reference](https://activitysmith.com/docs/api-reference/introduction).
- [Live Activity Action](#live-activity-action)
- [Icons and Badges](#icons-and-badges)
- [Live Activity Colors](#live-activity-colors)
-- [Channels](#channels)
- [Widgets](#widgets)
+- [App Icon Badge Count](#app-icon-badge-count)
+- [Channels](#channels)
## Installation
@@ -514,18 +515,6 @@ Choose from these colors for the Live Activity accent, including progress bars a
`lime`, `green`, `cyan`, `blue`, `purple`, `magenta`, `red`, `orange`, `yellow`, `gray`
-## Channels
-
-Channels are used to target specific team members or devices. Can be used for both push notifications and live activities.
-
-```php
-$activitysmith->notifications->send(
- title: 'New subscription 💸',
- message: 'Customer upgraded to Pro plan',
- channels: ['sales', 'customer-success'], // Optional
-);
-```
-
## Widgets
@@ -548,6 +537,58 @@ String metric values work too.
$activitysmith->metrics->update('prod.status', 'healthy');
```
+## App Icon Badge Count
+
+
+
+
+
+Show the number you care about on your ActivitySmith app icon. Track MRR, a customer count, a stock price, or any other value you want to keep in view.
+
+Set or update the badge value.
+
+```php
+$activitysmith->badgeCount(8333);
+```
+
+To clear the badge, set its value to 0.
+
+```php
+$activitysmith->badgeCount(0);
+```
+
+## Channels
+
+Use `channels` to target specific team members or devices
+
+### Push Notifications
+
+```php
+$activitysmith->notifications->send(
+ title: 'New subscription 💸',
+ message: 'Customer upgraded to Pro plan',
+ channels: ['sales', 'customer-success'],
+);
+```
+
+### Live Activities
+
+```php
+$activitysmith->liveActivities->start(
+ title: 'Nightly Database Backup',
+ subtitle: 'verify restore',
+ type: 'progress',
+ percentage: 62,
+ channels: ['sales', 'customer-success'],
+);
+```
+
+### App Icon Badge Count
+
+```php
+$activitysmith->badgeCount(3, channels: ['sales', 'customer-success']);
+```
+
## Error Handling
```php
diff --git a/generated/Api/AppIconBadgesApi.php b/generated/Api/AppIconBadgesApi.php
new file mode 100644
index 0000000..1738c0d
--- /dev/null
+++ b/generated/Api/AppIconBadgesApi.php
@@ -0,0 +1,593 @@
+ [
+ 'application/json',
+ ],
+ ];
+
+ /**
+ * @param ClientInterface $client
+ * @param Configuration $config
+ * @param HeaderSelector $selector
+ * @param int $hostIndex (Optional) host index to select the list of hosts if defined in the OpenAPI spec
+ */
+ public function __construct(
+ ClientInterface $client = null,
+ Configuration $config = null,
+ HeaderSelector $selector = null,
+ $hostIndex = 0
+ ) {
+ $this->client = $client ?: new Client();
+ $this->config = $config ?: new Configuration();
+ $this->headerSelector = $selector ?: new HeaderSelector();
+ $this->hostIndex = $hostIndex;
+ }
+
+ /**
+ * Set the host index
+ *
+ * @param int $hostIndex Host index (required)
+ */
+ public function setHostIndex($hostIndex): void
+ {
+ $this->hostIndex = $hostIndex;
+ }
+
+ /**
+ * Get the host index
+ *
+ * @return int Host index
+ */
+ public function getHostIndex()
+ {
+ return $this->hostIndex;
+ }
+
+ /**
+ * @return Configuration
+ */
+ public function getConfig()
+ {
+ return $this->config;
+ }
+
+ /**
+ * Operation updateAppIconBadgeCount
+ *
+ * Update App Icon Badge Count
+ *
+ * @param \ActivitySmith\Generated\Model\AppIconBadgeCountUpdateRequest $appIconBadgeCountUpdateRequest appIconBadgeCountUpdateRequest (required)
+ * @param string $contentType The value for the Content-Type header. Check self::contentTypes['updateAppIconBadgeCount'] to see the possible values for this operation
+ *
+ * @throws \ActivitySmith\Generated\ApiException on non-2xx response or if the response body is not in the expected format
+ * @throws \InvalidArgumentException
+ * @return \ActivitySmith\Generated\Model\AppIconBadgeCountUpdateResponse|\ActivitySmith\Generated\Model\BadRequestError|\ActivitySmith\Generated\Model\ForbiddenError|\ActivitySmith\Generated\Model\NoRecipientsError|\ActivitySmith\Generated\Model\RateLimitError
+ */
+ public function updateAppIconBadgeCount($appIconBadgeCountUpdateRequest, string $contentType = self::contentTypes['updateAppIconBadgeCount'][0])
+ {
+ list($response) = $this->updateAppIconBadgeCountWithHttpInfo($appIconBadgeCountUpdateRequest, $contentType);
+ return $response;
+ }
+
+ /**
+ * Operation updateAppIconBadgeCountWithHttpInfo
+ *
+ * Update App Icon Badge Count
+ *
+ * @param \ActivitySmith\Generated\Model\AppIconBadgeCountUpdateRequest $appIconBadgeCountUpdateRequest (required)
+ * @param string $contentType The value for the Content-Type header. Check self::contentTypes['updateAppIconBadgeCount'] to see the possible values for this operation
+ *
+ * @throws \ActivitySmith\Generated\ApiException on non-2xx response or if the response body is not in the expected format
+ * @throws \InvalidArgumentException
+ * @return array of \ActivitySmith\Generated\Model\AppIconBadgeCountUpdateResponse|\ActivitySmith\Generated\Model\BadRequestError|\ActivitySmith\Generated\Model\ForbiddenError|\ActivitySmith\Generated\Model\NoRecipientsError|\ActivitySmith\Generated\Model\RateLimitError, HTTP status code, HTTP response headers (array of strings)
+ */
+ public function updateAppIconBadgeCountWithHttpInfo($appIconBadgeCountUpdateRequest, string $contentType = self::contentTypes['updateAppIconBadgeCount'][0])
+ {
+ $request = $this->updateAppIconBadgeCountRequest($appIconBadgeCountUpdateRequest, $contentType);
+
+ try {
+ $options = $this->createHttpClientOption();
+ try {
+ $response = $this->client->send($request, $options);
+ } catch (RequestException $e) {
+ throw new ApiException(
+ "[{$e->getCode()}] {$e->getMessage()}",
+ (int) $e->getCode(),
+ $e->getResponse() ? $e->getResponse()->getHeaders() : null,
+ $e->getResponse() ? (string) $e->getResponse()->getBody() : null
+ );
+ } catch (ConnectException $e) {
+ throw new ApiException(
+ "[{$e->getCode()}] {$e->getMessage()}",
+ (int) $e->getCode(),
+ null,
+ null
+ );
+ }
+
+ $statusCode = $response->getStatusCode();
+
+ if ($statusCode < 200 || $statusCode > 299) {
+ throw new ApiException(
+ sprintf(
+ '[%d] Error connecting to the API (%s)',
+ $statusCode,
+ (string) $request->getUri()
+ ),
+ $statusCode,
+ $response->getHeaders(),
+ (string) $response->getBody()
+ );
+ }
+
+ switch($statusCode) {
+ case 200:
+ if ('\ActivitySmith\Generated\Model\AppIconBadgeCountUpdateResponse' === '\SplFileObject') {
+ $content = $response->getBody(); //stream goes to serializer
+ } else {
+ $content = (string) $response->getBody();
+ if ('\ActivitySmith\Generated\Model\AppIconBadgeCountUpdateResponse' !== 'string') {
+ try {
+ $content = json_decode($content, false, 512, JSON_THROW_ON_ERROR);
+ } catch (\JsonException $exception) {
+ throw new ApiException(
+ sprintf(
+ 'Error JSON decoding server response (%s)',
+ $request->getUri()
+ ),
+ $statusCode,
+ $response->getHeaders(),
+ $content
+ );
+ }
+ }
+ }
+
+ return [
+ ObjectSerializer::deserialize($content, '\ActivitySmith\Generated\Model\AppIconBadgeCountUpdateResponse', []),
+ $response->getStatusCode(),
+ $response->getHeaders()
+ ];
+ case 400:
+ if ('\ActivitySmith\Generated\Model\BadRequestError' === '\SplFileObject') {
+ $content = $response->getBody(); //stream goes to serializer
+ } else {
+ $content = (string) $response->getBody();
+ if ('\ActivitySmith\Generated\Model\BadRequestError' !== 'string') {
+ try {
+ $content = json_decode($content, false, 512, JSON_THROW_ON_ERROR);
+ } catch (\JsonException $exception) {
+ throw new ApiException(
+ sprintf(
+ 'Error JSON decoding server response (%s)',
+ $request->getUri()
+ ),
+ $statusCode,
+ $response->getHeaders(),
+ $content
+ );
+ }
+ }
+ }
+
+ return [
+ ObjectSerializer::deserialize($content, '\ActivitySmith\Generated\Model\BadRequestError', []),
+ $response->getStatusCode(),
+ $response->getHeaders()
+ ];
+ case 403:
+ if ('\ActivitySmith\Generated\Model\ForbiddenError' === '\SplFileObject') {
+ $content = $response->getBody(); //stream goes to serializer
+ } else {
+ $content = (string) $response->getBody();
+ if ('\ActivitySmith\Generated\Model\ForbiddenError' !== 'string') {
+ try {
+ $content = json_decode($content, false, 512, JSON_THROW_ON_ERROR);
+ } catch (\JsonException $exception) {
+ throw new ApiException(
+ sprintf(
+ 'Error JSON decoding server response (%s)',
+ $request->getUri()
+ ),
+ $statusCode,
+ $response->getHeaders(),
+ $content
+ );
+ }
+ }
+ }
+
+ return [
+ ObjectSerializer::deserialize($content, '\ActivitySmith\Generated\Model\ForbiddenError', []),
+ $response->getStatusCode(),
+ $response->getHeaders()
+ ];
+ case 404:
+ if ('\ActivitySmith\Generated\Model\NoRecipientsError' === '\SplFileObject') {
+ $content = $response->getBody(); //stream goes to serializer
+ } else {
+ $content = (string) $response->getBody();
+ if ('\ActivitySmith\Generated\Model\NoRecipientsError' !== 'string') {
+ try {
+ $content = json_decode($content, false, 512, JSON_THROW_ON_ERROR);
+ } catch (\JsonException $exception) {
+ throw new ApiException(
+ sprintf(
+ 'Error JSON decoding server response (%s)',
+ $request->getUri()
+ ),
+ $statusCode,
+ $response->getHeaders(),
+ $content
+ );
+ }
+ }
+ }
+
+ return [
+ ObjectSerializer::deserialize($content, '\ActivitySmith\Generated\Model\NoRecipientsError', []),
+ $response->getStatusCode(),
+ $response->getHeaders()
+ ];
+ case 429:
+ if ('\ActivitySmith\Generated\Model\RateLimitError' === '\SplFileObject') {
+ $content = $response->getBody(); //stream goes to serializer
+ } else {
+ $content = (string) $response->getBody();
+ if ('\ActivitySmith\Generated\Model\RateLimitError' !== 'string') {
+ try {
+ $content = json_decode($content, false, 512, JSON_THROW_ON_ERROR);
+ } catch (\JsonException $exception) {
+ throw new ApiException(
+ sprintf(
+ 'Error JSON decoding server response (%s)',
+ $request->getUri()
+ ),
+ $statusCode,
+ $response->getHeaders(),
+ $content
+ );
+ }
+ }
+ }
+
+ return [
+ ObjectSerializer::deserialize($content, '\ActivitySmith\Generated\Model\RateLimitError', []),
+ $response->getStatusCode(),
+ $response->getHeaders()
+ ];
+ }
+
+ $returnType = '\ActivitySmith\Generated\Model\AppIconBadgeCountUpdateResponse';
+ if ($returnType === '\SplFileObject') {
+ $content = $response->getBody(); //stream goes to serializer
+ } else {
+ $content = (string) $response->getBody();
+ if ($returnType !== 'string') {
+ try {
+ $content = json_decode($content, false, 512, JSON_THROW_ON_ERROR);
+ } catch (\JsonException $exception) {
+ throw new ApiException(
+ sprintf(
+ 'Error JSON decoding server response (%s)',
+ $request->getUri()
+ ),
+ $statusCode,
+ $response->getHeaders(),
+ $content
+ );
+ }
+ }
+ }
+
+ return [
+ ObjectSerializer::deserialize($content, $returnType, []),
+ $response->getStatusCode(),
+ $response->getHeaders()
+ ];
+
+ } catch (ApiException $e) {
+ switch ($e->getCode()) {
+ case 200:
+ $data = ObjectSerializer::deserialize(
+ $e->getResponseBody(),
+ '\ActivitySmith\Generated\Model\AppIconBadgeCountUpdateResponse',
+ $e->getResponseHeaders()
+ );
+ $e->setResponseObject($data);
+ break;
+ case 400:
+ $data = ObjectSerializer::deserialize(
+ $e->getResponseBody(),
+ '\ActivitySmith\Generated\Model\BadRequestError',
+ $e->getResponseHeaders()
+ );
+ $e->setResponseObject($data);
+ break;
+ case 403:
+ $data = ObjectSerializer::deserialize(
+ $e->getResponseBody(),
+ '\ActivitySmith\Generated\Model\ForbiddenError',
+ $e->getResponseHeaders()
+ );
+ $e->setResponseObject($data);
+ break;
+ case 404:
+ $data = ObjectSerializer::deserialize(
+ $e->getResponseBody(),
+ '\ActivitySmith\Generated\Model\NoRecipientsError',
+ $e->getResponseHeaders()
+ );
+ $e->setResponseObject($data);
+ break;
+ case 429:
+ $data = ObjectSerializer::deserialize(
+ $e->getResponseBody(),
+ '\ActivitySmith\Generated\Model\RateLimitError',
+ $e->getResponseHeaders()
+ );
+ $e->setResponseObject($data);
+ break;
+ }
+ throw $e;
+ }
+ }
+
+ /**
+ * Operation updateAppIconBadgeCountAsync
+ *
+ * Update App Icon Badge Count
+ *
+ * @param \ActivitySmith\Generated\Model\AppIconBadgeCountUpdateRequest $appIconBadgeCountUpdateRequest (required)
+ * @param string $contentType The value for the Content-Type header. Check self::contentTypes['updateAppIconBadgeCount'] to see the possible values for this operation
+ *
+ * @throws \InvalidArgumentException
+ * @return \GuzzleHttp\Promise\PromiseInterface
+ */
+ public function updateAppIconBadgeCountAsync($appIconBadgeCountUpdateRequest, string $contentType = self::contentTypes['updateAppIconBadgeCount'][0])
+ {
+ return $this->updateAppIconBadgeCountAsyncWithHttpInfo($appIconBadgeCountUpdateRequest, $contentType)
+ ->then(
+ function ($response) {
+ return $response[0];
+ }
+ );
+ }
+
+ /**
+ * Operation updateAppIconBadgeCountAsyncWithHttpInfo
+ *
+ * Update App Icon Badge Count
+ *
+ * @param \ActivitySmith\Generated\Model\AppIconBadgeCountUpdateRequest $appIconBadgeCountUpdateRequest (required)
+ * @param string $contentType The value for the Content-Type header. Check self::contentTypes['updateAppIconBadgeCount'] to see the possible values for this operation
+ *
+ * @throws \InvalidArgumentException
+ * @return \GuzzleHttp\Promise\PromiseInterface
+ */
+ public function updateAppIconBadgeCountAsyncWithHttpInfo($appIconBadgeCountUpdateRequest, string $contentType = self::contentTypes['updateAppIconBadgeCount'][0])
+ {
+ $returnType = '\ActivitySmith\Generated\Model\AppIconBadgeCountUpdateResponse';
+ $request = $this->updateAppIconBadgeCountRequest($appIconBadgeCountUpdateRequest, $contentType);
+
+ return $this->client
+ ->sendAsync($request, $this->createHttpClientOption())
+ ->then(
+ function ($response) use ($returnType) {
+ if ($returnType === '\SplFileObject') {
+ $content = $response->getBody(); //stream goes to serializer
+ } else {
+ $content = (string) $response->getBody();
+ if ($returnType !== 'string') {
+ $content = json_decode($content);
+ }
+ }
+
+ return [
+ ObjectSerializer::deserialize($content, $returnType, []),
+ $response->getStatusCode(),
+ $response->getHeaders()
+ ];
+ },
+ function ($exception) {
+ $response = $exception->getResponse();
+ $statusCode = $response->getStatusCode();
+ throw new ApiException(
+ sprintf(
+ '[%d] Error connecting to the API (%s)',
+ $statusCode,
+ $exception->getRequest()->getUri()
+ ),
+ $statusCode,
+ $response->getHeaders(),
+ (string) $response->getBody()
+ );
+ }
+ );
+ }
+
+ /**
+ * Create request for operation 'updateAppIconBadgeCount'
+ *
+ * @param \ActivitySmith\Generated\Model\AppIconBadgeCountUpdateRequest $appIconBadgeCountUpdateRequest (required)
+ * @param string $contentType The value for the Content-Type header. Check self::contentTypes['updateAppIconBadgeCount'] to see the possible values for this operation
+ *
+ * @throws \InvalidArgumentException
+ * @return \GuzzleHttp\Psr7\Request
+ */
+ public function updateAppIconBadgeCountRequest($appIconBadgeCountUpdateRequest, string $contentType = self::contentTypes['updateAppIconBadgeCount'][0])
+ {
+
+ // verify the required parameter 'appIconBadgeCountUpdateRequest' is set
+ if ($appIconBadgeCountUpdateRequest === null || (is_array($appIconBadgeCountUpdateRequest) && count($appIconBadgeCountUpdateRequest) === 0)) {
+ throw new \InvalidArgumentException(
+ 'Missing the required parameter $appIconBadgeCountUpdateRequest when calling updateAppIconBadgeCount'
+ );
+ }
+
+
+ $resourcePath = '/badge';
+ $formParams = [];
+ $queryParams = [];
+ $headerParams = [];
+ $httpBody = '';
+ $multipart = false;
+
+
+
+
+
+ $headers = $this->headerSelector->selectHeaders(
+ ['application/json', ],
+ $contentType,
+ $multipart
+ );
+
+ // for model (json/xml)
+ if (isset($appIconBadgeCountUpdateRequest)) {
+ if (stripos($headers['Content-Type'], 'application/json') !== false) {
+ # if Content-Type contains "application/json", json_encode the body
+ $httpBody = \GuzzleHttp\Utils::jsonEncode(ObjectSerializer::sanitizeForSerialization($appIconBadgeCountUpdateRequest));
+ } else {
+ $httpBody = $appIconBadgeCountUpdateRequest;
+ }
+ } elseif (count($formParams) > 0) {
+ if ($multipart) {
+ $multipartContents = [];
+ foreach ($formParams as $formParamName => $formParamValue) {
+ $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue];
+ foreach ($formParamValueItems as $formParamValueItem) {
+ $multipartContents[] = [
+ 'name' => $formParamName,
+ 'contents' => $formParamValueItem
+ ];
+ }
+ }
+ // for HTTP post (form)
+ $httpBody = new MultipartStream($multipartContents);
+
+ } elseif (stripos($headers['Content-Type'], 'application/json') !== false) {
+ # if Content-Type contains "application/json", json_encode the form parameters
+ $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams);
+ } else {
+ // for HTTP post (form)
+ $httpBody = ObjectSerializer::buildQuery($formParams);
+ }
+ }
+
+ // this endpoint requires Bearer (API Key) authentication (access token)
+ if (!empty($this->config->getAccessToken())) {
+ $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken();
+ }
+
+ $defaultHeaders = [];
+ if ($this->config->getUserAgent()) {
+ $defaultHeaders['User-Agent'] = $this->config->getUserAgent();
+ }
+
+ $headers = array_merge(
+ $defaultHeaders,
+ $headerParams,
+ $headers
+ );
+
+ $operationHost = $this->config->getHost();
+ $query = ObjectSerializer::buildQuery($queryParams);
+ return new Request(
+ 'POST',
+ $operationHost . $resourcePath . ($query ? "?{$query}" : ''),
+ $headers,
+ $httpBody
+ );
+ }
+
+ /**
+ * Create http client option
+ *
+ * @throws \RuntimeException on file opening failure
+ * @return array of http client options
+ */
+ protected function createHttpClientOption()
+ {
+ $options = [];
+ if ($this->config->getDebug()) {
+ $options[RequestOptions::DEBUG] = fopen($this->config->getDebugFile(), 'a');
+ if (!$options[RequestOptions::DEBUG]) {
+ throw new \RuntimeException('Failed to open the debug file: ' . $this->config->getDebugFile());
+ }
+ }
+
+ return $options;
+ }
+}
diff --git a/generated/Model/AppIconBadgeCountUpdateRequest.php b/generated/Model/AppIconBadgeCountUpdateRequest.php
new file mode 100644
index 0000000..0d3d835
--- /dev/null
+++ b/generated/Model/AppIconBadgeCountUpdateRequest.php
@@ -0,0 +1,463 @@
+
+ */
+class AppIconBadgeCountUpdateRequest implements ModelInterface, ArrayAccess, \JsonSerializable
+{
+ public const DISCRIMINATOR = null;
+
+ /**
+ * The original name of the model.
+ *
+ * @var string
+ */
+ protected static $openAPIModelName = 'AppIconBadgeCountUpdateRequest';
+
+ /**
+ * Array of property to type mappings. Used for (de)serialization
+ *
+ * @var string[]
+ */
+ protected static $openAPITypes = [
+ 'badge' => 'int',
+ 'target' => '\ActivitySmith\Generated\Model\ChannelTarget'
+ ];
+
+ /**
+ * Array of property to format mappings. Used for (de)serialization
+ *
+ * @var string[]
+ * @phpstan-var array
+ * @psalm-var array
+ */
+ protected static $openAPIFormats = [
+ 'badge' => null,
+ 'target' => null
+ ];
+
+ /**
+ * Array of nullable properties. Used for (de)serialization
+ *
+ * @var boolean[]
+ */
+ protected static array $openAPINullables = [
+ 'badge' => false,
+ 'target' => false
+ ];
+
+ /**
+ * If a nullable field gets set to null, insert it here
+ *
+ * @var boolean[]
+ */
+ protected array $openAPINullablesSetToNull = [];
+
+ /**
+ * Array of property to type mappings. Used for (de)serialization
+ *
+ * @return array
+ */
+ public static function openAPITypes()
+ {
+ return self::$openAPITypes;
+ }
+
+ /**
+ * Array of property to format mappings. Used for (de)serialization
+ *
+ * @return array
+ */
+ public static function openAPIFormats()
+ {
+ return self::$openAPIFormats;
+ }
+
+ /**
+ * Array of nullable properties
+ *
+ * @return array
+ */
+ protected static function openAPINullables(): array
+ {
+ return self::$openAPINullables;
+ }
+
+ /**
+ * Array of nullable field names deliberately set to null
+ *
+ * @return boolean[]
+ */
+ private function getOpenAPINullablesSetToNull(): array
+ {
+ return $this->openAPINullablesSetToNull;
+ }
+
+ /**
+ * Setter - Array of nullable field names deliberately set to null
+ *
+ * @param boolean[] $openAPINullablesSetToNull
+ */
+ private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void
+ {
+ $this->openAPINullablesSetToNull = $openAPINullablesSetToNull;
+ }
+
+ /**
+ * Checks if a property is nullable
+ *
+ * @param string $property
+ * @return bool
+ */
+ public static function isNullable(string $property): bool
+ {
+ return self::openAPINullables()[$property] ?? false;
+ }
+
+ /**
+ * Checks if a nullable property is set to null.
+ *
+ * @param string $property
+ * @return bool
+ */
+ public function isNullableSetToNull(string $property): bool
+ {
+ return in_array($property, $this->getOpenAPINullablesSetToNull(), true);
+ }
+
+ /**
+ * Array of attributes where the key is the local name,
+ * and the value is the original name
+ *
+ * @var string[]
+ */
+ protected static $attributeMap = [
+ 'badge' => 'badge',
+ 'target' => 'target'
+ ];
+
+ /**
+ * Array of attributes to setter functions (for deserialization of responses)
+ *
+ * @var string[]
+ */
+ protected static $setters = [
+ 'badge' => 'setBadge',
+ 'target' => 'setTarget'
+ ];
+
+ /**
+ * Array of attributes to getter functions (for serialization of requests)
+ *
+ * @var string[]
+ */
+ protected static $getters = [
+ 'badge' => 'getBadge',
+ 'target' => 'getTarget'
+ ];
+
+ /**
+ * Array of attributes where the key is the local name,
+ * and the value is the original name
+ *
+ * @return array
+ */
+ public static function attributeMap()
+ {
+ return self::$attributeMap;
+ }
+
+ /**
+ * Array of attributes to setter functions (for deserialization of responses)
+ *
+ * @return array
+ */
+ public static function setters()
+ {
+ return self::$setters;
+ }
+
+ /**
+ * Array of attributes to getter functions (for serialization of requests)
+ *
+ * @return array
+ */
+ public static function getters()
+ {
+ return self::$getters;
+ }
+
+ /**
+ * The original name of the model.
+ *
+ * @return string
+ */
+ public function getModelName()
+ {
+ return self::$openAPIModelName;
+ }
+
+
+ /**
+ * Associative array for storing property values
+ *
+ * @var mixed[]
+ */
+ protected $container = [];
+
+ /**
+ * Constructor
+ *
+ * @param mixed[] $data Associated array of property values
+ * initializing the model
+ */
+ public function __construct(array $data = null)
+ {
+ $this->setIfExists('badge', $data ?? [], null);
+ $this->setIfExists('target', $data ?? [], null);
+ }
+
+ /**
+ * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName
+ * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the
+ * $this->openAPINullablesSetToNull array
+ *
+ * @param string $variableName
+ * @param array $fields
+ * @param mixed $defaultValue
+ */
+ private function setIfExists(string $variableName, array $fields, $defaultValue): void
+ {
+ if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) {
+ $this->openAPINullablesSetToNull[] = $variableName;
+ }
+
+ $this->container[$variableName] = $fields[$variableName] ?? $defaultValue;
+ }
+
+ /**
+ * Show all the invalid properties with reasons.
+ *
+ * @return array invalid properties with reasons
+ */
+ public function listInvalidProperties()
+ {
+ $invalidProperties = [];
+
+ if ($this->container['badge'] === null) {
+ $invalidProperties[] = "'badge' can't be null";
+ }
+ if (($this->container['badge'] > 2147483647)) {
+ $invalidProperties[] = "invalid value for 'badge', must be smaller than or equal to 2147483647.";
+ }
+
+ if (($this->container['badge'] < 0)) {
+ $invalidProperties[] = "invalid value for 'badge', must be bigger than or equal to 0.";
+ }
+
+ return $invalidProperties;
+ }
+
+ /**
+ * Validate all the properties in the model
+ * return true if all passed
+ *
+ * @return bool True if all properties are valid
+ */
+ public function valid()
+ {
+ return count($this->listInvalidProperties()) === 0;
+ }
+
+
+ /**
+ * Gets badge
+ *
+ * @return int
+ */
+ public function getBadge()
+ {
+ return $this->container['badge'];
+ }
+
+ /**
+ * Sets badge
+ *
+ * @param int $badge The count to show on the ActivitySmith app icon. Send 0 to clear it.
+ *
+ * @return self
+ */
+ public function setBadge($badge)
+ {
+ if (is_null($badge)) {
+ throw new \InvalidArgumentException('non-nullable badge cannot be null');
+ }
+
+ if (($badge > 2147483647)) {
+ throw new \InvalidArgumentException('invalid value for $badge when calling AppIconBadgeCountUpdateRequest., must be smaller than or equal to 2147483647.');
+ }
+ if (($badge < 0)) {
+ throw new \InvalidArgumentException('invalid value for $badge when calling AppIconBadgeCountUpdateRequest., must be bigger than or equal to 0.');
+ }
+
+ $this->container['badge'] = $badge;
+
+ return $this;
+ }
+
+ /**
+ * Gets target
+ *
+ * @return \ActivitySmith\Generated\Model\ChannelTarget|null
+ */
+ public function getTarget()
+ {
+ return $this->container['target'];
+ }
+
+ /**
+ * Sets target
+ *
+ * @param \ActivitySmith\Generated\Model\ChannelTarget|null $target target
+ *
+ * @return self
+ */
+ public function setTarget($target)
+ {
+ if (is_null($target)) {
+ throw new \InvalidArgumentException('non-nullable target cannot be null');
+ }
+ $this->container['target'] = $target;
+
+ return $this;
+ }
+ /**
+ * Returns true if offset exists. False otherwise.
+ *
+ * @param integer $offset Offset
+ *
+ * @return boolean
+ */
+ public function offsetExists($offset): bool
+ {
+ return isset($this->container[$offset]);
+ }
+
+ /**
+ * Gets offset.
+ *
+ * @param integer $offset Offset
+ *
+ * @return mixed|null
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($offset)
+ {
+ return $this->container[$offset] ?? null;
+ }
+
+ /**
+ * Sets value based on offset.
+ *
+ * @param int|null $offset Offset
+ * @param mixed $value Value to be set
+ *
+ * @return void
+ */
+ public function offsetSet($offset, $value): void
+ {
+ if (is_null($offset)) {
+ $this->container[] = $value;
+ } else {
+ $this->container[$offset] = $value;
+ }
+ }
+
+ /**
+ * Unsets offset.
+ *
+ * @param integer $offset Offset
+ *
+ * @return void
+ */
+ public function offsetUnset($offset): void
+ {
+ unset($this->container[$offset]);
+ }
+
+ /**
+ * Serializes the object to a value that can be serialized natively by json_encode().
+ * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php
+ *
+ * @return mixed Returns data which can be serialized by json_encode(), which is a value
+ * of any type other than a resource.
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return ObjectSerializer::sanitizeForSerialization($this);
+ }
+
+ /**
+ * Gets the string presentation of the object
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return json_encode(
+ ObjectSerializer::sanitizeForSerialization($this),
+ JSON_PRETTY_PRINT
+ );
+ }
+
+ /**
+ * Gets a header-safe presentation of the object
+ *
+ * @return string
+ */
+ public function toHeaderValue()
+ {
+ return json_encode(ObjectSerializer::sanitizeForSerialization($this));
+ }
+}
+
+
diff --git a/generated/Model/AppIconBadgeCountUpdateResponse.php b/generated/Model/AppIconBadgeCountUpdateResponse.php
new file mode 100644
index 0000000..96a06e4
--- /dev/null
+++ b/generated/Model/AppIconBadgeCountUpdateResponse.php
@@ -0,0 +1,613 @@
+
+ */
+class AppIconBadgeCountUpdateResponse implements ModelInterface, ArrayAccess, \JsonSerializable
+{
+ public const DISCRIMINATOR = null;
+
+ /**
+ * The original name of the model.
+ *
+ * @var string
+ */
+ protected static $openAPIModelName = 'AppIconBadgeCountUpdateResponse';
+
+ /**
+ * Array of property to type mappings. Used for (de)serialization
+ *
+ * @var string[]
+ */
+ protected static $openAPITypes = [
+ 'success' => 'bool',
+ 'badge' => 'int',
+ 'devicesNotified' => 'int',
+ 'usersNotified' => 'int',
+ 'effectiveChannelSlugs' => 'string[]',
+ 'timestamp' => '\DateTime'
+ ];
+
+ /**
+ * Array of property to format mappings. Used for (de)serialization
+ *
+ * @var string[]
+ * @phpstan-var array
+ * @psalm-var array
+ */
+ protected static $openAPIFormats = [
+ 'success' => null,
+ 'badge' => null,
+ 'devicesNotified' => null,
+ 'usersNotified' => null,
+ 'effectiveChannelSlugs' => null,
+ 'timestamp' => 'date-time'
+ ];
+
+ /**
+ * Array of nullable properties. Used for (de)serialization
+ *
+ * @var boolean[]
+ */
+ protected static array $openAPINullables = [
+ 'success' => false,
+ 'badge' => false,
+ 'devicesNotified' => false,
+ 'usersNotified' => false,
+ 'effectiveChannelSlugs' => false,
+ 'timestamp' => false
+ ];
+
+ /**
+ * If a nullable field gets set to null, insert it here
+ *
+ * @var boolean[]
+ */
+ protected array $openAPINullablesSetToNull = [];
+
+ /**
+ * Array of property to type mappings. Used for (de)serialization
+ *
+ * @return array
+ */
+ public static function openAPITypes()
+ {
+ return self::$openAPITypes;
+ }
+
+ /**
+ * Array of property to format mappings. Used for (de)serialization
+ *
+ * @return array
+ */
+ public static function openAPIFormats()
+ {
+ return self::$openAPIFormats;
+ }
+
+ /**
+ * Array of nullable properties
+ *
+ * @return array
+ */
+ protected static function openAPINullables(): array
+ {
+ return self::$openAPINullables;
+ }
+
+ /**
+ * Array of nullable field names deliberately set to null
+ *
+ * @return boolean[]
+ */
+ private function getOpenAPINullablesSetToNull(): array
+ {
+ return $this->openAPINullablesSetToNull;
+ }
+
+ /**
+ * Setter - Array of nullable field names deliberately set to null
+ *
+ * @param boolean[] $openAPINullablesSetToNull
+ */
+ private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void
+ {
+ $this->openAPINullablesSetToNull = $openAPINullablesSetToNull;
+ }
+
+ /**
+ * Checks if a property is nullable
+ *
+ * @param string $property
+ * @return bool
+ */
+ public static function isNullable(string $property): bool
+ {
+ return self::openAPINullables()[$property] ?? false;
+ }
+
+ /**
+ * Checks if a nullable property is set to null.
+ *
+ * @param string $property
+ * @return bool
+ */
+ public function isNullableSetToNull(string $property): bool
+ {
+ return in_array($property, $this->getOpenAPINullablesSetToNull(), true);
+ }
+
+ /**
+ * Array of attributes where the key is the local name,
+ * and the value is the original name
+ *
+ * @var string[]
+ */
+ protected static $attributeMap = [
+ 'success' => 'success',
+ 'badge' => 'badge',
+ 'devicesNotified' => 'devices_notified',
+ 'usersNotified' => 'users_notified',
+ 'effectiveChannelSlugs' => 'effective_channel_slugs',
+ 'timestamp' => 'timestamp'
+ ];
+
+ /**
+ * Array of attributes to setter functions (for deserialization of responses)
+ *
+ * @var string[]
+ */
+ protected static $setters = [
+ 'success' => 'setSuccess',
+ 'badge' => 'setBadge',
+ 'devicesNotified' => 'setDevicesNotified',
+ 'usersNotified' => 'setUsersNotified',
+ 'effectiveChannelSlugs' => 'setEffectiveChannelSlugs',
+ 'timestamp' => 'setTimestamp'
+ ];
+
+ /**
+ * Array of attributes to getter functions (for serialization of requests)
+ *
+ * @var string[]
+ */
+ protected static $getters = [
+ 'success' => 'getSuccess',
+ 'badge' => 'getBadge',
+ 'devicesNotified' => 'getDevicesNotified',
+ 'usersNotified' => 'getUsersNotified',
+ 'effectiveChannelSlugs' => 'getEffectiveChannelSlugs',
+ 'timestamp' => 'getTimestamp'
+ ];
+
+ /**
+ * Array of attributes where the key is the local name,
+ * and the value is the original name
+ *
+ * @return array
+ */
+ public static function attributeMap()
+ {
+ return self::$attributeMap;
+ }
+
+ /**
+ * Array of attributes to setter functions (for deserialization of responses)
+ *
+ * @return array
+ */
+ public static function setters()
+ {
+ return self::$setters;
+ }
+
+ /**
+ * Array of attributes to getter functions (for serialization of requests)
+ *
+ * @return array
+ */
+ public static function getters()
+ {
+ return self::$getters;
+ }
+
+ /**
+ * The original name of the model.
+ *
+ * @return string
+ */
+ public function getModelName()
+ {
+ return self::$openAPIModelName;
+ }
+
+
+ /**
+ * Associative array for storing property values
+ *
+ * @var mixed[]
+ */
+ protected $container = [];
+
+ /**
+ * Constructor
+ *
+ * @param mixed[] $data Associated array of property values
+ * initializing the model
+ */
+ public function __construct(array $data = null)
+ {
+ $this->setIfExists('success', $data ?? [], null);
+ $this->setIfExists('badge', $data ?? [], null);
+ $this->setIfExists('devicesNotified', $data ?? [], null);
+ $this->setIfExists('usersNotified', $data ?? [], null);
+ $this->setIfExists('effectiveChannelSlugs', $data ?? [], null);
+ $this->setIfExists('timestamp', $data ?? [], null);
+ }
+
+ /**
+ * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName
+ * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the
+ * $this->openAPINullablesSetToNull array
+ *
+ * @param string $variableName
+ * @param array $fields
+ * @param mixed $defaultValue
+ */
+ private function setIfExists(string $variableName, array $fields, $defaultValue): void
+ {
+ if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) {
+ $this->openAPINullablesSetToNull[] = $variableName;
+ }
+
+ $this->container[$variableName] = $fields[$variableName] ?? $defaultValue;
+ }
+
+ /**
+ * Show all the invalid properties with reasons.
+ *
+ * @return array invalid properties with reasons
+ */
+ public function listInvalidProperties()
+ {
+ $invalidProperties = [];
+
+ if ($this->container['success'] === null) {
+ $invalidProperties[] = "'success' can't be null";
+ }
+ if ($this->container['badge'] === null) {
+ $invalidProperties[] = "'badge' can't be null";
+ }
+ if (($this->container['badge'] > 2147483647)) {
+ $invalidProperties[] = "invalid value for 'badge', must be smaller than or equal to 2147483647.";
+ }
+
+ if (($this->container['badge'] < 0)) {
+ $invalidProperties[] = "invalid value for 'badge', must be bigger than or equal to 0.";
+ }
+
+ if ($this->container['devicesNotified'] === null) {
+ $invalidProperties[] = "'devicesNotified' can't be null";
+ }
+ if ($this->container['usersNotified'] === null) {
+ $invalidProperties[] = "'usersNotified' can't be null";
+ }
+ if ($this->container['effectiveChannelSlugs'] === null) {
+ $invalidProperties[] = "'effectiveChannelSlugs' can't be null";
+ }
+ if ($this->container['timestamp'] === null) {
+ $invalidProperties[] = "'timestamp' can't be null";
+ }
+ return $invalidProperties;
+ }
+
+ /**
+ * Validate all the properties in the model
+ * return true if all passed
+ *
+ * @return bool True if all properties are valid
+ */
+ public function valid()
+ {
+ return count($this->listInvalidProperties()) === 0;
+ }
+
+
+ /**
+ * Gets success
+ *
+ * @return bool
+ */
+ public function getSuccess()
+ {
+ return $this->container['success'];
+ }
+
+ /**
+ * Sets success
+ *
+ * @param bool $success success
+ *
+ * @return self
+ */
+ public function setSuccess($success)
+ {
+ if (is_null($success)) {
+ throw new \InvalidArgumentException('non-nullable success cannot be null');
+ }
+ $this->container['success'] = $success;
+
+ return $this;
+ }
+
+ /**
+ * Gets badge
+ *
+ * @return int
+ */
+ public function getBadge()
+ {
+ return $this->container['badge'];
+ }
+
+ /**
+ * Sets badge
+ *
+ * @param int $badge badge
+ *
+ * @return self
+ */
+ public function setBadge($badge)
+ {
+ if (is_null($badge)) {
+ throw new \InvalidArgumentException('non-nullable badge cannot be null');
+ }
+
+ if (($badge > 2147483647)) {
+ throw new \InvalidArgumentException('invalid value for $badge when calling AppIconBadgeCountUpdateResponse., must be smaller than or equal to 2147483647.');
+ }
+ if (($badge < 0)) {
+ throw new \InvalidArgumentException('invalid value for $badge when calling AppIconBadgeCountUpdateResponse., must be bigger than or equal to 0.');
+ }
+
+ $this->container['badge'] = $badge;
+
+ return $this;
+ }
+
+ /**
+ * Gets devicesNotified
+ *
+ * @return int
+ */
+ public function getDevicesNotified()
+ {
+ return $this->container['devicesNotified'];
+ }
+
+ /**
+ * Sets devicesNotified
+ *
+ * @param int $devicesNotified devicesNotified
+ *
+ * @return self
+ */
+ public function setDevicesNotified($devicesNotified)
+ {
+ if (is_null($devicesNotified)) {
+ throw new \InvalidArgumentException('non-nullable devicesNotified cannot be null');
+ }
+ $this->container['devicesNotified'] = $devicesNotified;
+
+ return $this;
+ }
+
+ /**
+ * Gets usersNotified
+ *
+ * @return int
+ */
+ public function getUsersNotified()
+ {
+ return $this->container['usersNotified'];
+ }
+
+ /**
+ * Sets usersNotified
+ *
+ * @param int $usersNotified usersNotified
+ *
+ * @return self
+ */
+ public function setUsersNotified($usersNotified)
+ {
+ if (is_null($usersNotified)) {
+ throw new \InvalidArgumentException('non-nullable usersNotified cannot be null');
+ }
+ $this->container['usersNotified'] = $usersNotified;
+
+ return $this;
+ }
+
+ /**
+ * Gets effectiveChannelSlugs
+ *
+ * @return string[]
+ */
+ public function getEffectiveChannelSlugs()
+ {
+ return $this->container['effectiveChannelSlugs'];
+ }
+
+ /**
+ * Sets effectiveChannelSlugs
+ *
+ * @param string[] $effectiveChannelSlugs effectiveChannelSlugs
+ *
+ * @return self
+ */
+ public function setEffectiveChannelSlugs($effectiveChannelSlugs)
+ {
+ if (is_null($effectiveChannelSlugs)) {
+ throw new \InvalidArgumentException('non-nullable effectiveChannelSlugs cannot be null');
+ }
+ $this->container['effectiveChannelSlugs'] = $effectiveChannelSlugs;
+
+ return $this;
+ }
+
+ /**
+ * Gets timestamp
+ *
+ * @return \DateTime
+ */
+ public function getTimestamp()
+ {
+ return $this->container['timestamp'];
+ }
+
+ /**
+ * Sets timestamp
+ *
+ * @param \DateTime $timestamp timestamp
+ *
+ * @return self
+ */
+ public function setTimestamp($timestamp)
+ {
+ if (is_null($timestamp)) {
+ throw new \InvalidArgumentException('non-nullable timestamp cannot be null');
+ }
+ $this->container['timestamp'] = $timestamp;
+
+ return $this;
+ }
+ /**
+ * Returns true if offset exists. False otherwise.
+ *
+ * @param integer $offset Offset
+ *
+ * @return boolean
+ */
+ public function offsetExists($offset): bool
+ {
+ return isset($this->container[$offset]);
+ }
+
+ /**
+ * Gets offset.
+ *
+ * @param integer $offset Offset
+ *
+ * @return mixed|null
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($offset)
+ {
+ return $this->container[$offset] ?? null;
+ }
+
+ /**
+ * Sets value based on offset.
+ *
+ * @param int|null $offset Offset
+ * @param mixed $value Value to be set
+ *
+ * @return void
+ */
+ public function offsetSet($offset, $value): void
+ {
+ if (is_null($offset)) {
+ $this->container[] = $value;
+ } else {
+ $this->container[$offset] = $value;
+ }
+ }
+
+ /**
+ * Unsets offset.
+ *
+ * @param integer $offset Offset
+ *
+ * @return void
+ */
+ public function offsetUnset($offset): void
+ {
+ unset($this->container[$offset]);
+ }
+
+ /**
+ * Serializes the object to a value that can be serialized natively by json_encode().
+ * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php
+ *
+ * @return mixed Returns data which can be serialized by json_encode(), which is a value
+ * of any type other than a resource.
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return ObjectSerializer::sanitizeForSerialization($this);
+ }
+
+ /**
+ * Gets the string presentation of the object
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return json_encode(
+ ObjectSerializer::sanitizeForSerialization($this),
+ JSON_PRETTY_PRINT
+ );
+ }
+
+ /**
+ * Gets a header-safe presentation of the object
+ *
+ * @return string
+ */
+ public function toHeaderValue()
+ {
+ return json_encode(ObjectSerializer::sanitizeForSerialization($this));
+ }
+}
+
+
diff --git a/generated/Model/LiveActivityAction.php b/generated/Model/LiveActivityAction.php
index ec0aac6..cc8b434 100644
--- a/generated/Model/LiveActivityAction.php
+++ b/generated/Model/LiveActivityAction.php
@@ -62,7 +62,7 @@ class LiveActivityAction implements ModelInterface, ArrayAccess, \JsonSerializab
'type' => '\ActivitySmith\Generated\Model\LiveActivityActionType',
'url' => 'string',
'method' => '\ActivitySmith\Generated\Model\LiveActivityWebhookMethod',
- 'body' => 'array'
+ 'body' => 'object'
];
/**
@@ -438,7 +438,7 @@ public function setMethod($method)
/**
* Gets body
*
- * @return array|null
+ * @return object|null
*/
public function getBody()
{
@@ -448,7 +448,7 @@ public function getBody()
/**
* Sets body
*
- * @param array|null $body Optional webhook payload body. Used only when type=webhook.
+ * @param object|null $body Optional webhook payload body. Used only when type=webhook.
*
* @return self
*/
diff --git a/generated/Model/PushNotificationAction.php b/generated/Model/PushNotificationAction.php
index 72d65a6..8996e3d 100644
--- a/generated/Model/PushNotificationAction.php
+++ b/generated/Model/PushNotificationAction.php
@@ -61,7 +61,7 @@ class PushNotificationAction implements ModelInterface, ArrayAccess, \JsonSerial
'type' => '\ActivitySmith\Generated\Model\PushNotificationActionType',
'url' => 'string',
'method' => '\ActivitySmith\Generated\Model\PushNotificationWebhookMethod',
- 'body' => 'array'
+ 'body' => 'object'
];
/**
@@ -437,7 +437,7 @@ public function setMethod($method)
/**
* Gets body
*
- * @return array|null
+ * @return object|null
*/
public function getBody()
{
@@ -447,7 +447,7 @@ public function getBody()
/**
* Sets body
*
- * @param array|null $body Optional webhook payload body. Used only when type=webhook.
+ * @param object|null $body Optional webhook payload body. Used only when type=webhook.
*
* @return self
*/
diff --git a/generated/Model/PushNotificationRequest.php b/generated/Model/PushNotificationRequest.php
index 33bd6d9..ccf6900 100644
--- a/generated/Model/PushNotificationRequest.php
+++ b/generated/Model/PushNotificationRequest.php
@@ -63,7 +63,7 @@ class PushNotificationRequest implements ModelInterface, ArrayAccess, \JsonSeria
'media' => 'string',
'redirection' => 'string',
'actions' => '\ActivitySmith\Generated\Model\PushNotificationAction[]',
- 'payload' => 'array',
+ 'payload' => 'object',
'badge' => 'int',
'sound' => 'string',
'target' => '\ActivitySmith\Generated\Model\ChannelTarget'
@@ -546,7 +546,7 @@ public function setActions($actions)
/**
* Gets payload
*
- * @return array|null
+ * @return object|null
*/
public function getPayload()
{
@@ -556,7 +556,7 @@ public function getPayload()
/**
* Sets payload
*
- * @param array|null $payload payload
+ * @param object|null $payload payload
*
* @return self
*/
diff --git a/src/ActivitySmith.php b/src/ActivitySmith.php
index ec74232..8dd4fd8 100644
--- a/src/ActivitySmith.php
+++ b/src/ActivitySmith.php
@@ -16,6 +16,7 @@ final class ActivitySmith
private const PUSH_API_CLASS = 'ActivitySmith\\Generated\\Api\\PushNotificationsApi';
private const LIVE_API_CLASS = 'ActivitySmith\\Generated\\Api\\LiveActivitiesApi';
private const METRICS_API_CLASS = 'ActivitySmith\\Generated\\Api\\MetricsApi';
+ private const APP_ICON_BADGES_API_CLASS = 'ActivitySmith\\Generated\\Api\\AppIconBadgesApi';
private const SDK_HEADER_NAME = 'X-ActivitySmith-SDK';
/** @var Notifications */
@@ -27,6 +28,8 @@ final class ActivitySmith
/** @var Metrics */
public $metrics;
+ private $appIconBadges;
+
public function __construct(string $apiKey)
{
if ($apiKey === '') {
@@ -39,6 +42,7 @@ public function __construct(string $apiKey)
$pushApiClass = self::PUSH_API_CLASS;
$liveApiClass = self::LIVE_API_CLASS;
$metricsApiClass = self::METRICS_API_CLASS;
+ $appIconBadgesApiClass = self::APP_ICON_BADGES_API_CLASS;
$configuration = $configurationClass::getDefaultConfiguration();
$configuration->setAccessToken($apiKey);
@@ -56,6 +60,21 @@ public function __construct(string $apiKey)
$this->notifications = new Notifications(new $pushApiClass($httpClient, $configuration));
$this->liveActivities = new LiveActivities(new $liveApiClass($httpClient, $configuration));
$this->metrics = new Metrics(new $metricsApiClass($httpClient, $configuration));
+ $this->appIconBadges = new $appIconBadgesApiClass($httpClient, $configuration);
+ }
+
+ /**
+ * @param array|string|null $channels
+ */
+ public function badgeCount(int $value, array|string|null $channels = null): mixed
+ {
+ $request = ['badge' => $value];
+ $channels = $this->normalizeChannels($channels);
+ if ($channels !== []) {
+ $request['target'] = ['channels' => $channels];
+ }
+
+ return $this->appIconBadges->updateAppIconBadgeCount($request);
}
private function assertGeneratedClientIsPresent(): void
@@ -65,10 +84,33 @@ private function assertGeneratedClientIsPresent(): void
|| !class_exists(self::PUSH_API_CLASS)
|| !class_exists(self::LIVE_API_CLASS)
|| !class_exists(self::METRICS_API_CLASS)
+ || !class_exists(self::APP_ICON_BADGES_API_CLASS)
) {
throw new RuntimeException(
'Generated PHP client not found. Run SDK regeneration so /generated contains OpenAPI output.'
);
}
}
+
+ /**
+ * @param array|string|null $channels
+ * @return array
+ */
+ private function normalizeChannels(array|string|null $channels): array
+ {
+ if (is_string($channels)) {
+ $channels = explode(',', $channels);
+ }
+
+ if (!is_array($channels)) {
+ return [];
+ }
+
+ return array_values(
+ array_filter(
+ array_map('trim', $channels),
+ static fn (string $channel): bool => $channel !== ''
+ )
+ );
+ }
}
diff --git a/src/Version.php b/src/Version.php
index 074439d..d0b7daf 100644
--- a/src/Version.php
+++ b/src/Version.php
@@ -6,7 +6,7 @@
final class Version
{
- public const VERSION = '1.8.0';
+ public const VERSION = '1.9.0';
private function __construct()
{
diff --git a/tests/ResourcesTest.php b/tests/ResourcesTest.php
index 9d6681e..0c29860 100644
--- a/tests/ResourcesTest.php
+++ b/tests/ResourcesTest.php
@@ -5,6 +5,7 @@
namespace ActivitySmith\Tests;
use ActivitySmith\LiveActivities;
+use ActivitySmith\ActivitySmith;
use ActivitySmith\LiveActivityAction;
use ActivitySmith\LiveActivityAlertBadge;
use ActivitySmith\LiveActivityAlertIcon;
@@ -14,6 +15,7 @@
use ActivitySmith\Notifications;
use ActivitySmith\PushAction;
use ActivitySmith\Generated\Api\LiveActivitiesApi;
+use ActivitySmith\Generated\Api\AppIconBadgesApi;
use ActivitySmith\Generated\Api\MetricsApi;
use ActivitySmith\Generated\Api\PushNotificationsApi;
use ActivitySmith\Generated\Model\LiveActivityAction as GeneratedLiveActivityAction;
@@ -25,6 +27,43 @@
final class ResourcesTest extends TestCase
{
+ public function testBadgeCountClearsAndTargetsChannels(): void
+ {
+ $client = new ActivitySmith('test');
+ $captured = [];
+ $api = $this->getMockBuilder(AppIconBadgesApi::class)
+ ->disableOriginalConstructor()
+ ->onlyMethods(['updateAppIconBadgeCount'])
+ ->getMock();
+ $api->method('updateAppIconBadgeCount')
+ ->willReturnCallback(static function (mixed $request) use (&$captured): mixed {
+ $captured[] = $request;
+ return $request;
+ });
+
+ $property = new \ReflectionProperty(ActivitySmith::class, 'appIconBadges');
+ $property->setValue($client, $api);
+
+ self::assertSame(['badge' => 0], $client->badgeCount(0));
+ self::assertSame(
+ [
+ 'badge' => 3,
+ 'target' => ['channels' => ['sales', 'customer-success']],
+ ],
+ $client->badgeCount(3, 'sales,customer-success')
+ );
+ self::assertSame(
+ [
+ ['badge' => 0],
+ [
+ 'badge' => 3,
+ 'target' => ['channels' => ['sales', 'customer-success']],
+ ],
+ ],
+ $captured
+ );
+ }
+
public function testNotificationsShortAndLegacyMethods(): void
{
$payload = ['title' => 'Build Failed'];
diff --git a/tests/SmokeTest.php b/tests/SmokeTest.php
index 95097e2..51d9026 100644
--- a/tests/SmokeTest.php
+++ b/tests/SmokeTest.php
@@ -28,6 +28,6 @@ public function testClientConstructsWhenGeneratedCodeIsPresent(): void
$this->assertTrue(method_exists($client->liveActivities, 'endStream'));
$this->assertNotNull($client->metrics);
$this->assertTrue(method_exists($client->metrics, 'update'));
- $this->assertSame('1.8.0', Version::VERSION);
+ $this->assertSame('1.9.0', Version::VERSION);
}
}