diff --git a/output/playwright/blog-sidebar-desktop.webp b/output/playwright/blog-sidebar-desktop.webp new file mode 100644 index 0000000..7c780a4 Binary files /dev/null and b/output/playwright/blog-sidebar-desktop.webp differ diff --git a/output/playwright/blog-sidebar-mobile.webp b/output/playwright/blog-sidebar-mobile.webp new file mode 100644 index 0000000..d7168ef Binary files /dev/null and b/output/playwright/blog-sidebar-mobile.webp differ diff --git a/src/components/Blog.astro b/src/components/Blog.astro index e1a73fc..ef4a324 100644 --- a/src/components/Blog.astro +++ b/src/components/Blog.astro @@ -16,36 +16,35 @@ const { tag, link, date, title, image, loading } = Astro.props const cannLink = getRelativeLocaleUrl(Astro.locals.locale, `blog/${link}`) --- -
-
-
- { - image && ( - - {title} - - ) - } - { - tag && ( +
+ { + image && ( +
+ + {title} + + {tag && (
{tag}
- ) - } -
- + )} +
+ ) + } +
+ {tag && !image &&

{tag}

} + {formatTime(date)} -

+

{title} diff --git a/src/components/BlogListing.astro b/src/components/BlogListing.astro index e99ba15..bd47e1e 100644 --- a/src/components/BlogListing.astro +++ b/src/components/BlogListing.astro @@ -11,21 +11,222 @@ const content: { title?: string; description?: string; image?: string; author?: const posts = allPosts.sort((a, b) => (new Date(a.data.created_at) > new Date(b.data.created_at) ? -1 : 1)) +const categoryRules = [ + { label: 'Authentication', terms: ['auth', 'login', 'signin', 'sign-in', 'single-sign-on', 'sso', 'kakao', 'oauth', 'biometric', 'autofill', 'password'] }, + { label: 'Firebase & Messaging', terms: ['firebase', 'fcm', 'push', 'notification', 'airship', 'cleverpush', 'marketingcloud', 'azure-notification', 'emarsys'] }, + { label: 'Maps & Location', terms: ['map', 'maps', 'location', 'geolocation', 'gps'] }, + { label: 'Media & Camera', terms: ['camera', 'photo', 'video', 'audio', 'music', 'media', 'barcode', 'scanner', 'speech', 'player', 'brightness'] }, + { label: 'Files & Storage', terms: ['file', 'storage', 'sqlite', 'blob', 'couchbase', 'mediastore', 'document', 'docutain', 'opener', 'uploader'] }, + { label: 'Payments & Revenue', terms: ['admob', 'ads', 'advertising', 'purchase', 'purchases', 'subscription', 'stripe', 'monetize', 'wallet'] }, + { + label: 'Device APIs', + terms: ['bluetooth', 'contacts', 'calendar', 'sim', 'sensor', 'nfc', 'health', 'healthkit', 'device', 'printer', 'datawedge', 'shortcut', 'siri', 'call-number'], + }, + { label: 'App Shell & WebView', terms: ['webview', 'tailwind', 'edge-to-edge', 'navigation-bar', 'home-indicator', 'cookies', 'user-agent'] }, + { label: 'Background & System', terms: ['background', 'updater', 'update', 'keep-awake', 'exit-app', 'developer-options', 'system-stats', 'ssl', 'privacy-screen', 'launcher'] }, + { label: 'Analytics & Growth', terms: ['analytics', 'mixpanel', 'facebook-events', 'appmetrica', 'adjust', 'meta', 'bugfender', 'apprate'] }, +] +const fallbackCategory = 'Capacitor Plugins' + +const escapeRegex = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + +const getPostCategory = (post: CollectionEntry<'blog'>) => { + const haystack = [post.data.title, post.data.description, post.data.slug].filter(Boolean).join(' ').toLowerCase() + return categoryRules.find((category) => category.terms.some((term) => new RegExp(`\\b${escapeRegex(term)}\\b`).test(haystack)))?.label ?? fallbackCategory +} + +const postsWithCategories = posts.map((post) => ({ post, category: getPostCategory(post) })) +const categoryCounts = postsWithCategories.reduce((counts, { category }) => { + counts.set(category, (counts.get(category) ?? 0) + 1) + return counts +}, new Map()) +const categories = [ + { label: 'All', count: posts.length }, + ...[...categoryRules.map((category) => category.label), fallbackCategory] + .map((label) => ({ label, count: categoryCounts.get(label) ?? 0 })) + .filter((category) => category.count > 0), +] + if (Astro.locals.runtimeConfig.public.blog_title) content['title'] = Astro.locals.runtimeConfig.public.blog_title if (Astro.locals.runtimeConfig.public.blog_keywords) content['keywords'] = Astro.locals.runtimeConfig.public.blog_keywords if (Astro.locals.runtimeConfig.public.blog_description) content['description'] = Astro.locals.runtimeConfig.public.blog_description --- -

-
-

{m.latest_from_the_blog({}, { locale: Astro.locals.locale })}

-

- {config.public.blog_description} -

-
- {posts.map((j, i) => )} +
+
+
+

{m.latest_from_the_blog({}, { locale: Astro.locals.locale })}

+

+ {config.public.blog_description} +

+
+ +
+ + +
+
+
+ + +
+
+ + Categories + + +
+ { + categories.map((category) => ( + + )) + } +
+
+
+ +
+

Showing {posts.length} articles

+
+ +
+ { + postsWithCategories.map(({ post, category }, i) => ( +
+ +
+ )) + } +
+ + +
+ + diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index d626b97..3e20f8c 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -15,7 +15,7 @@ const isLocalhost = Astro.url.origin.includes('localhost:') -
+