Appstore discover section API¶
The appstore “Discover” section is generated from data is provided using a JSON file.
The discover.json
consists of an array containing multiple elements, those elements are either shuffled or can be always shown.
Currently the following elements are supported:
- Posts
Posts consists of a headline, a text and an optional image.
- Full width media
Like images (webp is recommended due to quality, size and browser support), animations (webm or mp4) or videos 1
- App showcases
Promote a set of apps
- Carousel
Allow to shuffle through multiple elements
- 1
For videos see the example below.
Supported elements¶
Common element interface¶
All content types are sharing a common interface
{
// The type of the element
"type": "post",
// ID to indentify this element
"id": "1d41a08e-4aa6-49b3-ad1b-ff2e83bcdc88",
// (optional) if set this element will be always shown on that specified position
"order": 1,
// (optional) if set the content will only be shown starting that date
"date": "2024-04-04T20:00:00Z",
// (optional) if set the content will only be shown UNTIL that date
"expiryDate": "2024-05-04T20:00:00Z",
// (optional) the optionally localized headline, if a language is missing the "en" default is used
"headline": {
"en": "...",
"de": "..."
},
// (optional) link for the element
"link": "https://..."
}
Simple post: type: "post"
¶
Post are elements with a headline, a text and an (optional) media part. All elements of it can be localized.
Example 1: Post with image¶
{
"type": "post",
"id": "1d41a08e-4aa6-49b3-ad1b-ff2e83bcdc88",
"headline": {
"en": "Amazing wallpapers",
"de": "Bezaubernde Hintergründe"
},
"text": {
"en": "This are the most amazing wallpaers that you can possible have. Get them now!",
"de": "Die wohl beeindruckensten Hintergründe die man haben kann. Hol sie dir jetzt!"
},
"link": "https://example.com/wallpapers",
"media": {
"alignment": "end",
"content": {
"en": {
"src": {
"src": "http://example.com/a.png",
"mime": "image/png"
},
"alt": "Amazing wallpaper"
}
}
}
}
Example 2: Post with video¶
{
"type": "post",
"id": "1d41a08e-4aa6-49b3-ad1b-ff2e83bcdc88",
"media": {
"content": {
"en": {
"src": {
"src": "https://example.com/preview.webp", // the preview image (or animation)
"mime": "image/webp"
},
"alt": "Some alternative text",
"link": "http://example.com/full-video" // the link to navigate to when pressing "Play video"
}
}
}
}
Example 3: Post with localized media¶
It is possible to have localized media, e.g. screenshots in different languages.
For this use an array for media
instead:
{
"type": "post",
// ...
"media": {
"content": {
"en": {
"src": {
"src": "https://example.com/english.webp",
"mime": "image/webp"
},
"alt": "Some alternative text"
}
"de": {
"src": {
"src": "https://example.com/german.webp",
"mime": "image/webp"
},
"alt": "Ein Alternativtext"
}
}
}
}
Showcase: type: "showcase"
¶
The showcase type allow to promot multiple entries in one element. It allows to either show multiple posts or apps.
Example: Showcase with headline and apps¶
{
"type": "showcase",
// ...
"headline": {
"en": "5 top rated apps over all time"
},
"content": [
{
"type": "app",
"appId": "cospend"
},
{
"type": "app",
"appId": "cookbook"
}
]
}
Example: Showcase with headline and media entries¶
{
"type": "showcase",
"headline": {
"en": "5 amazing wallpapers"
},
"content": [
{
"type": "post",
"media": {
"content": {
"en": {
"src": {
"src": "http://example.com/first.png",
"mime": "image/png"
},
"alt": "Wallpaper..."
}
}
}
},
{
"type": "post",
"media": {
"content": {
"en": {
"src": {
"src": "http://example.com/second.png",
"mime": "image/png"
},
"alt": "Another wallpaper..."
}
}
}
}
// other posts
]
}
Carousels: type: "carousel"
¶
Carousels allow the same common properties, like headlines and text, but have a content
property for and array of posts to show.
The content
property also allows the app
type like type: "showcase"
does.
Example: Carousel with post content¶
{
"type": "carousel",
"content": [
{
"type": "post",
"headline": {
"en": "Nextcloud Office 7.0 out with full-featured editing dialogs"
},
"text": {
"en": "Today is an..."
},
"media": {
"content": {
"en": {
"src": {
"src": "http://example.com/first.png",
"mime": "image/png"
},
"alt": "..."
}
}
}
},
// other posts
]
}
Example: Carousel with headline and media content¶
{
"type": "carousel",
"headline": {
"en": "Some headline that is important"
},
"text": {
"en": "Today is an..."
},
"content": [
{
"type": "post",
"media": {
"content": {
"en": {
"src": {
"src": "http://example.com/first.png",
"mime": "image/png"
},
"alt": "...",
"link": "..."
}
}
}
},
// other posts
]
}
Example: Carousel with app content¶
{
"type": "carousel",
"headline": {
"en": "Some amazing apps"
},
"content": [
{
"type": "app",
"appId": "Cospend",
},
// other apps
]
}
JSON data schema¶
The JSON can be validated using out schema, see app-discover.schema.json
.
The generic element¶
This element all other inherit from
Property |
Type |
Is required |
Notes |
|
string |
yes |
|
|
string |
yes |
|
|
integer |
For manual ordering. Elements without |
|
|
|||
|
|||
|
URL |
allows also |
|
|
string (ISO8601 date-time) |
Can be set to only show this entry after the specified date |
|
|
string (ISO8601 date-time) |
Can be set to only show this entry until the specified date |
1 "genericElement": {
2 "type": "object",
3 "properties": {
4 "type": {
5 "type": "string"
6 }
7 },
8 "required": ["type"]
9 },
10
11 "genericContainerElement": {
12 "$ref": "#/$defs/genericElement",
13 "type": "object",
14 "properties": {
15 "id": {
16 "$comment": "A unique ID, using UUID v4 is recommended",
17 "type": "string"
18 },
19 "order": {
20 "$comment": "Optional order for fixed order of elements",
21 "type": "integer"
22 },
23 "headline": { "$ref": "#/$defs/localizedString" },
24 "text": { "$ref": "#/$defs/localizedString" },
25 "link": {
26 "type": "string",
27 "format": "uri-reference"
28 },
29 "date": {
30 "type": "string",
31 "format": "date-time"
32 },
33 "expiryDate": {
34 "type": "string",
35 "format": "date-time"
36 }
37 }
38 },
Localized strings¶
Localized strings are objects with the language code as the key and the translated string as the property, like:
{ "en": "Hello", "de": "Hallo" }
Property |
Type |
Is required |
Notes |
|
string |
yes |
The English text |
language code |
string |
The translated text for that language |
1 "localizedString": {
2 "type": "object",
3 "properties": {
4 "en": {
5 "type": "string"
6 }
7 },
8 "required": ["en"],
9 "patternProperties": {
10 "^[a-z]{2,3}(_[a-z]+)?$": { "type": "string" }
11 },
12 "additionalProperties": false
13 },
Media object¶
Media objects are used within the post element, they allow to embed video or image media.
Property |
Type |
Is required |
Notes |
|
yes |
The content to show |
|
|
string |
When combined with text this defined the media alignment. One of: |
1 "mediaObject": {
2 "type": "object",
3 "properties": {
4 "content": { "$ref": "#/$defs/localizedMediaContent" },
5 "alignment": {
6 "enum": [
7 "start",
8 "end",
9 "center"
10 ]
11 }
12 },
13 "required": ["content"]
14 },
Media content¶
The media content is dictionary similar to Localized strings but instead of strings its values are media content objects with following properties:
Property |
Type |
Is required |
Notes |
|
Media source or array of it |
yes |
The media source, use an array for fallback options (source sets) |
|
string |
yes |
The alternative text for the media |
|
URL |
In case of videos this is the link to navigate when pressing the play-video button |
1 "mediaContent": {
2 "type": "object",
3 "properties": {
4 "src": {
5 "oneOf": [
6 { "$ref": "#/$defs/mediaSource" },
7 {
8 "type": "array",
9 "items": { "$ref": "#/$defs/mediaSource" }
10 }
11 ]
12 },
13 "alt": { "type": "string" },
14 "link": {
15 "type": "string",
16 "format": "uri-reference"
17 }
18 },
19 "required": ["alt", "src"]
20 },
Media source¶
Property |
Type |
Is required |
Notes |
|
URL |
yes |
The URL of the media element |
|
string |
yes |
The MIME type of the media element |
1 "mediaSource": {
2 "type": "object",
3 "properties": {
4 "mime": { "type": "string" },
5 "src": {
6 "type": "string",
7 "format": "uri-reference"
8 }
9 },
10 "required": ["src", "mime"]
11 },
The app-element¶
The app element is only used in showcase
elements.
Property |
Type |
Is required |
Notes |
|
string |
yes |
|
|
string |
yes |
The app id, e.g. |
1 "appElement": {
2 "$ref": "#/$defs/genericElement",
3 "type": "object",
4 "properties": {
5 "type": { "const": "app" },
6 "appId": { "type": "string" }
7 },
8 "requiredProperties": ["appId"],
9 "additionalProperties": false
10 },
The post-element¶
The post element is the basic element for media or text entries, it inherits from the The generic element and extends it by allowing the media
property:
Property |
Type |
Is required |
Notes |
|
string |
yes |
|
|
Media object or array |
yes |
Either one Media object or an array of multiple for localized media |
1 "postElement": {
2 "$ref": "#/$defs/genericContainerElement",
3 "type": "object",
4 "properties": {
5 "type": { "const": "post" },
6 "media": { "$ref": "#/$defs/mediaObject" }
7 }
8 },
The showcase-element¶
The showcase elements allows to display multiple posts inside one element, it inherits from the The generic element and extends it by allowing the content
property:
Property |
Type |
Is required |
Notes |
|
string |
yes |
|
|
array |
yes |
Array of The app-element or of The post-element |
1 "showcaseElement": {
2 "$ref": "#/$defs/genericContainerElement",
3 "type": "object",
4 "properties": {
5 "type": { "const": "showcase" },
6 "content": {
7 "type": "array",
8 "items": {
9 "oneOf": [
10 { "$ref": "#/$defs/appElement" },
11 { "$ref": "#/$defs/postElement" }
12 ],
13 "minItems": 2
14 }
15 }
16 },
17 "required": ["content"]
18 },
The carousel-element¶
The carousel elements allows to display multiple posts inside carousel. It inherits from the The generic element and extends it by allowing the content
property:
Property |
Type |
Is required |
Notes |
|
string |
yes |
|
|
array |
yes |
Array of The post-element |
1 "carouselElement": {
2 "$ref": "#/$defs/genericContainerElement",
3 "type": "object",
4 "properties": {
5 "type": { "const": "carousel" },
6 "content": {
7 "type": "array",
8 "items": {
9 "$ref": "#/$defs/postElement",
10 "minItems": 2
11 }
12 }
13 },
14 "required": ["content"]
15 }