Skip to content

Commit 934dea7

Browse files
committed
Add app gallery
1 parent 4561963 commit 934dea7

35 files changed

Lines changed: 2075 additions & 103 deletions
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
name: Suggest an app for the gallery
2+
description: Tell us about an app built with Skip that's published on both the App Store and the Play Store, and we'll consider adding it to skip.dev/gallery.
3+
title: "[Gallery] <App name>"
4+
labels: ["gallery"]
5+
assignees: []
6+
body:
7+
- type: markdown
8+
attributes:
9+
value: |
10+
Thanks for suggesting an app! The Skip gallery features apps that are
11+
**publicly available on both the Apple App Store and the Google Play
12+
Store**, and that are built — at least in part — with Skip. Apps that
13+
are only on one store, in beta, or unreleased can be suggested here as
14+
well; we'll add them once they're live on both stores.
15+
16+
Please fill in as much as you can. The fields marked required are the
17+
minimum we need to evaluate the suggestion.
18+
19+
- type: input
20+
id: app-name
21+
attributes:
22+
label: App name
23+
description: The name as it appears in the App Store / Play Store.
24+
placeholder: e.g. "exhivision" or "UHD Radio"
25+
validations:
26+
required: true
27+
28+
- type: input
29+
id: developer
30+
attributes:
31+
label: Developer / publisher
32+
description: The legal entity or person publishing the app.
33+
placeholder: e.g. "Areo Info LLC" or "Kazuya Ueoka"
34+
validations:
35+
required: true
36+
37+
- type: input
38+
id: app-store-url
39+
attributes:
40+
label: App Store URL
41+
description: Full https://apps.apple.com/... URL.
42+
placeholder: https://apps.apple.com/us/app/example/id1234567890
43+
validations:
44+
required: true
45+
46+
- type: input
47+
id: play-store-url
48+
attributes:
49+
label: Play Store URL
50+
description: Full https://play.google.com/store/apps/details?id=... URL.
51+
placeholder: https://play.google.com/store/apps/details?id=com.example.app
52+
validations:
53+
required: true
54+
55+
- type: textarea
56+
id: description
57+
attributes:
58+
label: One- or two-sentence description
59+
description: What does the app do? We'll use this as a starting point for the gallery card text.
60+
placeholder: |
61+
e.g. "A free internet radio app streaming high-fidelity xHE-AAC audio. Available on iOS and Android, with a 13 MB Android footprint."
62+
validations:
63+
required: true
64+
65+
- type: dropdown
66+
id: skip-mode
67+
attributes:
68+
label: Skip mode
69+
description: Which Skip mode does the app use? If you're not sure, choose "I'm not sure".
70+
options:
71+
- SkipFuse (Swift compiled natively for Android)
72+
- SkipLite (Swift transpiled to Kotlin)
73+
- SkipBridge (mixed transpiled UI + native model)
74+
- partial (Skip used only for shared business logic, native UI on each platform)
75+
- I'm not sure
76+
validations:
77+
required: true
78+
79+
- type: input
80+
id: source-url
81+
attributes:
82+
label: Source repository (optional)
83+
description: If the app is open source, the GitHub / GitLab / etc URL.
84+
placeholder: e.g. https://github.com/example/example-app
85+
86+
- type: textarea
87+
id: skip-evidence
88+
attributes:
89+
label: Evidence the app is built with Skip
90+
description: |
91+
A link, quote, or screenshot showing the app uses Skip. Examples:
92+
- A line in the project's README or Package.swift referencing skip.dev or skiptools
93+
- A tweet, blog post, or talk where the developer mentions Skip
94+
- Bundle ID conventions (e.g. `*.skip` suffix on the Play Store package)
95+
If the app is open source, the README is usually enough.
96+
validations:
97+
required: true
98+
99+
- type: input
100+
id: developer-website
101+
attributes:
102+
label: Developer website (optional)
103+
description: Marketing site, blog, or company page.
104+
105+
- type: checkboxes
106+
id: confirmations
107+
attributes:
108+
label: Confirmations
109+
options:
110+
- label: The app is **currently live** on both the App Store and the Play Store (or I've noted in the description that one or both listings are pending).
111+
required: true
112+
- label: I have permission to suggest this app for the gallery (developer, employee of the publishing company, or the suggestion comes from a publicly available source like a community announcement).
113+
required: true

public/icons/android.svg

Lines changed: 1 addition & 0 deletions
Loading

public/icons/arrow_back.svg

Lines changed: 1 addition & 0 deletions
Loading

public/icons/download.svg

Lines changed: 1 addition & 0 deletions
Loading

public/icons/phone_iphone.svg

Lines changed: 1 addition & 0 deletions
Loading

public/icons/star.svg

Lines changed: 1 addition & 0 deletions
Loading

src/components/CustomHeader.astro

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ const links: Link[] = [
3838
value: '/docs/',
3939
transfer: true,
4040
},
41+
{
42+
title: Astro.locals.t('header.link.gallery'),
43+
value: '/gallery/',
44+
transfer: false,
45+
},
4146
{
4247
title: Astro.locals.t('header.link.blog'),
4348
value: '/blog/',

src/components/GalleryCard.astro

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
import type { CollectionEntry } from 'astro:content';
3+
4+
interface Props {
5+
app: CollectionEntry<'gallery'>;
6+
}
7+
8+
const { app } = Astro.props;
9+
const data = app.data;
10+
const slug = app.id;
11+
const initial = data.name.replace(/[^A-Za-z]/, '').slice(0, 1).toUpperCase() || 'S';
12+
---
13+
14+
<a class="gallery-card" href={`/gallery/${slug}/`}>
15+
<div class="gallery-card-iconwrap">
16+
{data.icon ? (
17+
<img class="gallery-card-icon" src={data.icon} alt="" loading="lazy" />
18+
) : (
19+
<div class="gallery-card-icon gallery-card-icon-placeholder">{initial}</div>
20+
)}
21+
{data.featured && <span class="gallery-card-featured">Featured</span>}
22+
</div>
23+
24+
<h3 class="gallery-card-name">{data.name}</h3>
25+
{data.tagline && <p class="gallery-card-tagline">{data.tagline}</p>}
26+
27+
{data.mode && (
28+
<span class="gallery-card-mode" data-mode={data.mode}>
29+
{data.mode}
30+
</span>
31+
)}
32+
</a>

src/components/SiteTitle.astro

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ const links: Link[] = [
2121
value: '/docs/',
2222
transfer: true,
2323
},
24+
{
25+
title: Astro.locals.t('header.link.gallery'),
26+
value: '/gallery/',
27+
transfer: false,
28+
},
2429
{
2530
title: Astro.locals.t('header.link.blog'),
2631
value: '/blog/',

src/content.config.mjs

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,39 +16,69 @@ const docs = defineCollection({
1616
/* FIXME: doesn't create a talks/ index */
1717
const talks = defineCollection({
1818
loader: glob({ pattern: "**/*.md", base: "./src/content/docs/talks" }),
19-
//schema:
19+
//schema:
2020
});
2121

2222
const i18n = defineCollection({
2323
loader: i18nLoader(),
2424
schema: i18nSchema({
2525
extend: z.object({
2626
'header.link.docs': z.string().optional(),
27+
'header.link.gallery': z.string().optional(),
2728
'header.link.blog': z.string().optional(),
2829
'header.link.sponsor': z.string().optional(),
2930
'banner.message': z.string().optional(),
3031
}),
3132
}),
3233
});
3334

34-
/*
35-
const tour = defineCollection({
36-
type: 'content',
35+
const ratingSchema = z.object({
36+
score: z.number().min(0).max(5).optional(),
37+
count: z.number().int().nonnegative().optional(),
38+
downloads: z.string().optional(),
39+
}).optional();
40+
41+
const screenshotSchema = z.object({
42+
url: z.string().url(),
43+
caption: z.string().optional(),
44+
});
45+
46+
const gallery = defineCollection({
47+
loader: glob({ pattern: "*.yaml", base: "./src/content/gallery" }),
3748
schema: z.object({
38-
title: z.string(),
39-
duration: z.string(),
40-
video: z.string().url(),
41-
poster: z.string().url(),
42-
yt: z.string().url().optional(),
49+
name: z.string(),
50+
developer: z.string(),
51+
tagline: z.string().optional(),
4352
description: z.string(),
53+
category: z.string().optional(),
54+
mode: z.enum(['SkipFuse', 'SkipLite', 'SkipBridge', 'partial']).optional(),
55+
featured: z.boolean().default(false),
56+
icon: z.string().url().optional(),
57+
releaseYear: z.number().int().optional(),
58+
tags: z.array(z.string()).default([]),
59+
links: z.object({
60+
appStore: z.string().url().optional(),
61+
playStore: z.string().url().optional(),
62+
website: z.string().url().optional(),
63+
source: z.string().url().optional(),
64+
article: z.string().url().optional(),
65+
}).default({}),
66+
ratings: z.object({
67+
appStore: ratingSchema,
68+
playStore: ratingSchema,
69+
}).default({}),
70+
screenshots: z.object({
71+
ios: z.array(screenshotSchema).default([]),
72+
android: z.array(screenshotSchema).default([]),
73+
}).default({}),
74+
available: z.boolean().default(true),
75+
notes: z.string().optional(),
4476
}),
4577
});
46-
*/
47-
4878

4979
export const collections = {
5080
docs: docs,
5181
talks: talks,
5282
i18n: i18n,
53-
//tour: tour,
83+
gallery: gallery,
5484
};

0 commit comments

Comments
 (0)