Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 32 additions & 27 deletions lib/app.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// ignore_for_file: deprecated_member_use

import 'dart:html';
import 'package:jaspr/dom.dart';
import 'package:jaspr/jaspr.dart';
import 'package:writesync/pages/search_page.dart';
import 'package:jaspr_router/jaspr_router.dart';
Expand Down Expand Up @@ -58,8 +61,8 @@ class RootApp extends StatelessComponent {
const RootApp();

@override
Iterable<Component> build(BuildContext context) sync* {
yield ProviderScope(
Component build(BuildContext context) {
return ProviderScope(
child: const App(),
);
}
Expand Down Expand Up @@ -95,33 +98,35 @@ class _AppState extends State<App> {
}

@override
Iterable<Component> build(BuildContext context) sync* {
Component build(BuildContext context) {
// Add base Document.head for site-wide meta tags
yield Document.head(
title: SiteConfig.siteName,
meta: {
'viewport': 'width=device-width, initial-scale=1.0',
...SiteConfig.defaultMeta,
},
);

yield div(
classes: 'relative',
[
Router(
routes: routes,
),
// Add performance dashboard with lazy loading
if (SiteConfig.enablePerformanceMonitoring)
LazyComponent(
loader: () async {
await Future.delayed(Duration(milliseconds: 300));
return const PerformanceDashboard();
},
placeholder: div(classes: 'hidden', []),
return Component.fragment([
Document.head(
title: SiteConfig.siteName,
meta: {
'viewport': 'width=device-width, initial-scale=1.0',
...SiteConfig.defaultMeta,
},
),

div(
classes: 'relative',
[
Router(
routes: routes,
),
],
);
// Add performance dashboard with lazy loading
if (SiteConfig.enablePerformanceMonitoring)
LazyComponent(
loader: () async {
await Future.delayed(Duration(milliseconds: 300));
return const PerformanceDashboard();
},
placeholder: div(classes: 'hidden', []),
),
],
),
]);
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/builder/blog_post_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class BlogPostBuilder implements build.Builder {
// ignore_for_file: undefined_identifier, expected_identifier, invalid_reference_to_this, non_constant_identifier_names, constant_identifier_names, invalid_element_content

import 'package:jaspr/jaspr.dart';
import 'package:jaspr/dom.dart';
import 'package:writesync/models/blog_post.dart';
import 'package:writesync/posts/blog_post_registry.dart';

Expand Down
2 changes: 1 addition & 1 deletion lib/builder/main_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class MainBuilder implements build.Builder {
final output = '''
// Generated code - do not modify by hand

import 'package:jaspr/browser.dart';
import 'package:jaspr/client.dart';
import 'package:jaspr_riverpod/jaspr_riverpod.dart';
import 'package:writesync/app.dart';
import 'package:writesync/services/blog_service.dart';
Expand Down
23 changes: 12 additions & 11 deletions lib/components/blog_list.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:jaspr/dom.dart';
import 'package:jaspr/jaspr.dart';
import 'package:jaspr_riverpod/jaspr_riverpod.dart';
import '../models/blog_post.dart';
Expand All @@ -13,11 +14,11 @@ class BlogList extends StatelessComponent {
});

@override
Iterable<Component> build(BuildContext context) sync* {
Component build(BuildContext context) {
final currentLayout = context.watch(blogLayoutProvider);
final isListView = currentLayout == BlogLayout.list;

yield div(
return div(
classes: 'py-16 sm:py-24',
[
div(
Expand All @@ -29,13 +30,13 @@ class BlogList extends StatelessComponent {
h2(
classes:
'text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl',
[text('Latest Articles')],
[Component.text('Latest Articles')],
),
p(
classes:
'mt-2 text-lg leading-8 text-gray-600 dark:text-gray-300',
[
text(
Component.text(
'Stay up to date with our latest insights and developments')
],
),
Expand Down Expand Up @@ -69,8 +70,8 @@ class BlogPostListCard extends StatelessComponent {
});

@override
Iterable<Component> build(BuildContext context) sync* {
yield article(
Component build(BuildContext context) {
return article(
classes: '''
flex flex-row bg-white dark:bg-gray-800 rounded-lg overflow-hidden
border border-gray-100 dark:border-gray-800
Expand Down Expand Up @@ -118,7 +119,7 @@ class BlogPostListCard extends StatelessComponent {
bg-gray-100 dark:bg-gray-700
text-gray-600 dark:text-gray-300
''',
[text('#$tag')],
[Component.text('#$tag')],
),
],
),
Expand All @@ -130,15 +131,15 @@ class BlogPostListCard extends StatelessComponent {
hover:text-indigo-600 dark:hover:text-indigo-400
line-clamp-2 mb-2
''',
[text(post.title)],
[Component.text(post.title)],
),
// Description
p(
classes: '''
text-gray-600 dark:text-gray-300
line-clamp-2 mb-4
''',
[text(post.description)],
[Component.text(post.description)],
),
],
),
Expand All @@ -160,12 +161,12 @@ class BlogPostListCard extends StatelessComponent {
font-semibold text-gray-900 dark:text-white
hover:text-indigo-600 dark:hover:text-indigo-400
''',
[text(post.author)],
[Component.text(post.author)],
),
p(
classes: 'text-gray-600 dark:text-gray-400',
[
text(post.publishedAt
Component.text(post.publishedAt
.toLocal()
.toString()
.split(' ')[0]),
Expand Down
155 changes: 79 additions & 76 deletions lib/components/blog_post.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:jaspr/dom.dart';
import 'package:jaspr/jaspr.dart';
import 'package:intl/intl.dart';
import 'package:markdown/markdown.dart' as md;
Expand All @@ -21,84 +22,86 @@ class BlogPostComponent extends StatefulComponent {

class _BlogPostComponentState extends State<BlogPostComponent> {
@override
Iterable<Component> build(BuildContext context) sync* {
Component build(BuildContext context) {
final isDark = context.watch(isDarkProvider);

// Use Document.head() for SEO
yield Document.head(
title: '${component.post.title} - Jaspr Blog',
meta: {
'description': component.post.metaDescription,
'author': component.post.author,
'keywords': component.post.tags.join(', '),
'og:title': component.post.title,
'og:description': component.post.metaDescription,
'og:type': 'article',
if (component.post.imageUrl != null)
'og:image': component.post.imageUrl!,
'twitter:card': 'summary_large_image',
'twitter:title': component.post.title,
'twitter:description': component.post.metaDescription,
if (component.post.imageUrl != null)
'twitter:image': component.post.imageUrl!,
},
);

yield article(classes: 'bg-white py-24 sm:py-32', [
div(classes: 'mx-auto max-w-7xl px-6 lg:px-8', [
div(classes: 'mx-auto max-w-3xl', [
div(classes: 'mx-auto max-w-2xl text-center', [
div(classes: 'mt-8 flex justify-center gap-x-4 text-xs', [
DomComponent(
tag: 'time',
classes: 'text-gray-500',
attributes: {
'datetime': component.post.publishedAt.toIso8601String()
},
children: [
text(component.post.publishedAt
.toLocal()
.toString()
.split(' ')[0])
],
),
for (final tag in component.post.tags)
a(
href: '/tags/${tag.toLowerCase()}',
classes:
'relative z-10 rounded-full ${SiteConfig.getPrimaryLightClasses(isDark: isDark)} px-3 py-1.5 font-medium',
[text(tag)],
return Component.fragment([
Document.head(
title: '${component.post.title} - Jaspr Blog',
meta: {
'description': component.post.metaDescription,
'author': component.post.author,
'keywords': component.post.tags.join(', '),
'og:title': component.post.title,
'og:description': component.post.metaDescription,
'og:type': 'article',
if (component.post.imageUrl != null)
'og:image': component.post.imageUrl!,
'twitter:card': 'summary_large_image',
'twitter:title': component.post.title,
'twitter:description': component.post.metaDescription,
if (component.post.imageUrl != null)
'twitter:image': component.post.imageUrl!,
},
),

article(classes: 'bg-white py-24 sm:py-32', [
div(classes: 'mx-auto max-w-7xl px-6 lg:px-8', [
div(classes: 'mx-auto max-w-3xl', [
div(classes: 'mx-auto max-w-2xl text-center', [
div(classes: 'mt-8 flex justify-center gap-x-4 text-xs', [
Component.element(
tag: 'time',
classes: 'text-gray-500',
attributes: {
'datetime': component.post.publishedAt.toIso8601String()
},
children: [
Component.text(component.post.publishedAt
.toLocal()
.toString()
.split(' ')[0])
],
),
]),
h1(
classes:
'mt-8 text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl',
[
text(component.post.title),
for (final tag in component.post.tags)
a(
href: '/tags/${tag.toLowerCase()}',
classes:
'relative z-10 rounded-full ${SiteConfig.getPrimaryLightClasses(isDark: isDark)} px-3 py-1.5 font-medium',
[Component.text(tag)],
),
]),
h1(
classes:
'mt-8 text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl',
[
Component.text(component.post.title),
]),
div(classes: 'mt-6 text-lg leading-8 text-gray-600', [
Component.text(component.post.description),
]),
div(classes: 'mt-8 flex justify-center', [
div(classes: 'flex items-center gap-x-4', [
span(classes: 'text-gray-600 text-sm', [Component.text('By')]),
span(
classes: 'font-semibold text-gray-900',
[Component.text(component.post.author)]),
]),
div(classes: 'mt-6 text-lg leading-8 text-gray-600', [
text(component.post.description),
]),
div(classes: 'mt-8 flex justify-center', [
div(classes: 'flex items-center gap-x-4', [
span(classes: 'text-gray-600 text-sm', [text('By')]),
span(
classes: 'font-semibold text-gray-900',
[text(component.post.author)]),
]),
]),
div(
classes:
'prose prose-lg mx-auto prose-img:rounded-xl prose-headings:font-semibold ${SiteConfig.getPrimaryTextClasses(isDark: isDark)}',
attributes: {
'innerHTML': md.markdownToHtml(
component.post.markdownContent,
extensionSet: md.ExtensionSet.gitHubWeb,
),
},
[],
),
]),
div(
classes:
'prose prose-lg mx-auto prose-img:rounded-xl prose-headings:font-semibold ${SiteConfig.getPrimaryTextClasses(isDark: isDark)}',
attributes: {
'innerHTML': md.markdownToHtml(
component.post.markdownContent,
extensionSet: md.ExtensionSet.gitHubWeb,
),
},
[],
),
]),
]),
]);
Expand Down Expand Up @@ -137,11 +140,11 @@ class _BlogPostCardState extends State<BlogPostCard>
}

@override
Iterable<Component> build(BuildContext context) sync* {
Component build(BuildContext context) {
final currentLayout = context.watch(blogLayoutProvider);
final isListView = currentLayout == BlogLayout.list;

yield article(
return article(
classes: '''
${SiteConfig.blogPostCard['container']}
${isListView ? SiteConfig.blogPostCard['listView']!['container'] : ''}
Expand Down Expand Up @@ -177,12 +180,12 @@ class _BlogPostCardState extends State<BlogPostCard>
events: {
'click': (event) => _handlePostClick(),
},
[text(component.post.title)],
[Component.text(component.post.title)],
),
// Description
p(
classes: SiteConfig.blogPostCard['description'],
[text(component.post.description)],
[Component.text(component.post.description)],
),
],
),
Expand Down Expand Up @@ -236,13 +239,13 @@ class _BlogPostCardState extends State<BlogPostCard>
);
},
},
[text(component.post.author)],
[Component.text(component.post.author)],
),
if (SiteConfig.blogDisplay['showDate'] == true)
p(
classes: SiteConfig.blogPostCard['date'],
[
text(DateFormat('MMM d')
Component.text(DateFormat('MMM d')
.format(component.post.publishedAt)),
],
),
Expand Down
Loading