Skip to content

Commit 204e931

Browse files
author
dev
committed
webapp: Add NProgress
1 parent d56f563 commit 204e931

30 files changed

+170
-63
lines changed

package-lock.json

+13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

phoenix-blog/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"version": "0.1.0",
55
"private": true,
66
"scripts": {
7-
"templates": "handlebars src/templates/*.handlebars -f src/templates/precompiled.js -c handlebars/runtime -m && cat src/pages/*.tsx | shasum -a 256 | cut -d' ' -f1 > src/jsx_templates/templates_sha256.txt",
7+
"templates": "handlebars src/templates_handlebars/*.handlebars -f src/templates_handlebars/precompiled.js -c handlebars/runtime -m && cat src/templates_tsx/*.tsx | shasum -a 256 | cut -d' ' -f1 > src/templates_tsx/templates_sha256.txt",
88
"deploy": "wrangler deploy",
99
"dev": "wrangler dev",
1010
"css": "tailwindcss -i src/index.css -o src/public/theme/index-`git rev-parse HEAD`.css --minify",

phoenix-blog/src/cache.ts

-5
This file was deleted.

phoenix-blog/src/caching.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { sha256Sum } from "@phoenix/core/crypto";
2+
import { Context } from "./context";
3+
4+
let etagsCache = new Map<string, string>();
5+
6+
export async function getEtag(path: string, content: string): Promise<string> {
7+
let etag = etagsCache.get(path);
8+
if (!etag) {
9+
etag = await sha256Sum(content);
10+
etagsCache.set(path, etag);
11+
}
12+
return etag;
13+
}
14+
15+
export function handleCaching(ctx: Context, cacheControl: string, etag: string): Response | null {
16+
ctx.res.headers.set('Cache-Control', cacheControl);
17+
ctx.res.headers.set('ETag', `"${etag}"`);
18+
19+
const ifNoneMatch = ctx.req.header('If-None-Match')?.trim().replace('W/', '').replaceAll('"', '');
20+
if (ifNoneMatch && ifNoneMatch === etag) {
21+
return new Response(null, {
22+
status: 304,
23+
headers: ctx.res.headers,
24+
});
25+
}
26+
27+
return null;
28+
}

phoenix-blog/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Hono } from 'hono';
22
import { NotFoundError } from '@phoenix/core/errors';
3-
import { ErrorTemplate } from './jsx_templates/error';
3+
import { ErrorTemplate } from './templates_tsx/error';
44
import { Bindings, Variables } from './context';
55
import { robotsTxt } from './routes/robotstxt';
66
import { favicon } from './routes/favicon';

phoenix-blog/src/routes/favicon.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { useEtagsCache } from "../cache";
21
import { Context } from "../context";
3-
import { getEtag, handleCaching } from "../utils";
2+
import { getEtag, handleCaching } from "../caching";
43
import faviconFileContent from '../public/favicon.ico';
54

65
export async function favicon(ctx: Context): Promise<Response> {
7-
let etag = await getEtag(useEtagsCache(), '/favicon.ico', '/favicon.ico');
6+
let etag = await getEtag('/favicon.ico', '/favicon.ico');
87
const cacheHit = handleCaching(ctx, 'public, max-age=3600, must-revalidate', etag);
98
if (cacheHit) {
109
return cacheHit;

phoenix-blog/src/routes/handlebars.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { sha256Sum } from "@phoenix/core/crypto";
22
import { Context } from "../context";
3-
import { handleCaching } from "../utils";
4-
3+
import { handleCaching } from "../caching";
54

65
export async function handlebars(ctx: Context): Promise<Response> {
76
const name = ctx.req.query('name') ?? 'Handlebars'

phoenix-blog/src/routes/index.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { sha256Sum } from "@phoenix/core/crypto";
22
import { Context } from "../context";
3-
import { PostsTemplate } from "../jsx_templates/posts";
4-
import { getBlog, getPosts, handleCaching } from "../utils";
3+
import { PostsTemplate } from "../templates_tsx/posts";
4+
import { getBlog, getPosts } from "../utils";
5+
import { handleCaching } from "../caching";
56

67
export async function index(ctx: Context): Promise<Response> {
78
// const reqUrl = new URL(ctx.req.url);

phoenix-blog/src/routes/indexcss.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import { useEtagsCache } from "../cache";
1+
import { getEtag, handleCaching } from "../caching";
22
import { Context } from "../context";
3-
import { getEtag, handleCaching } from "../utils";
43
import indexCssFielContent from '../public/theme/index-5eca4c37898bca4ff1a357cf7c481dfe0375b737.css';
54

6-
75
export async function indexCss(ctx: Context): Promise<Response> {
8-
let etag = await getEtag(useEtagsCache(), '/theme/index-5eca4c37898bca4ff1a357cf7c481dfe0375b737.css', indexCssFielContent);
6+
let etag = await getEtag('/theme/index-5eca4c37898bca4ff1a357cf7c481dfe0375b737.css', indexCssFielContent);
97
const cacheHit = handleCaching(ctx, 'public, max-age=31536000, immutable', etag);
108
if (cacheHit) {
119
return cacheHit;

phoenix-blog/src/routes/page.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { sha256Sum } from "@phoenix/core/crypto";
22
import { Context } from "../context";
3-
import { getBlog, getPage, handleCaching, maxTime } from "../utils";
4-
import templatesHash from '../jsx_templates/templates_sha256.txt';
5-
import { PageTemplate } from "../jsx_templates/page";
3+
import { getBlog, getPage, maxTime } from "../utils";
4+
import templatesHash from '../templates_tsx/templates_sha256.txt';
5+
import { PageTemplate } from "../templates_tsx/page";
6+
import { handleCaching } from "../caching";
67

78
export async function page(ctx: Context): Promise<Response> {
89
const reqUrl = new URL(ctx.req.url);

phoenix-blog/src/routes/robotstxt.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { useEtagsCache } from "../cache";
1+
import { getEtag, handleCaching } from "../caching";
22
import { Context } from "../context";
3-
import { getEtag, handleCaching } from "../utils";
43
import robotsTxtFileContent from '../public/robots.txt';
54

65
export async function robotsTxt(ctx: Context): Promise<Response> {
7-
let etag = await getEtag(useEtagsCache(), '/robots.txt', robotsTxtFileContent);
6+
let etag = await getEtag('/robots.txt', robotsTxtFileContent);
87
const cacheHit = handleCaching(ctx, 'public, max-age=1800, must-revalidate', etag);
98
if (cacheHit) {
109
return cacheHit;

phoenix-blog/src/utils.ts

-24
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,6 @@ import { InternalServerError, NotFoundError } from "@phoenix/core/errors";
44
import { sha256Sum } from "@phoenix/core/crypto";
55
import { Context } from "./context";
66

7-
export function handleCaching(ctx: Context, cacheControl: string, etag: string): Response | null {
8-
ctx.res.headers.set('Cache-Control', cacheControl);
9-
ctx.res.headers.set('ETag', `"${etag}"`);
10-
11-
const ifNoneMatch = ctx.req.header('If-None-Match')?.trim().replace('W/', '').replaceAll('"', '');
12-
if (ifNoneMatch && ifNoneMatch === etag) {
13-
return new Response(null, {
14-
status: 304,
15-
headers: ctx.res.headers,
16-
});
17-
}
18-
19-
return null;
20-
}
21-
227
export async function getBlog(ctx: Context, domain: string): Promise<Blog> {
238
// service bindings expects a request with a full URL, se we set an invalid host
249
const apiReq = new Request(`https://localhost/headless/blog?domain=${domain}`, ctx.req.raw);
@@ -95,12 +80,3 @@ export function date(val: Date | undefined) {
9580
const date = new Date(val);
9681
return `${date.getFullYear()}-${date.getMonth()}-${date.getDay()}`;
9782
}
98-
99-
export async function getEtag(cache: Map<string, string>, path: string, content: string): Promise<string> {
100-
let etag = cache.get(path);
101-
if (!etag) {
102-
etag = await sha256Sum(content);
103-
cache.set(path, etag);
104-
}
105-
return etag;
106-
}

phoenix-webapp/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@tiptap/pm": "^2.1.12",
2222
"@tiptap/starter-kit": "^2.1.12",
2323
"@tiptap/vue-3": "^2.1.12",
24+
"nprogress": "^0.2.0",
2425
"pinia": "^2.1.7",
2526
"vue": "^3.3.4",
2627
"vue-router": "^4.2.5"
@@ -29,6 +30,7 @@
2930
"@tailwindcss/typography": "^0.5.10",
3031
"@tsconfig/node18": "^18.2.2",
3132
"@types/node": "^20.8.9",
33+
"@types/nprogress": "^0.2.2",
3234
"@vitejs/plugin-vue": "^4.4.0",
3335
"@vue/tsconfig": "^0.4.0",
3436
"autoprefixer": "^10.4.16",

phoenix-webapp/src/app.vue

+9-1
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
</template>
1515

1616
<script setup lang="ts">
17-
import { ref } from 'vue';
17+
import { ref, watch } from 'vue';
1818
import Navbar from './components/navbar.vue';
1919
import Sidebar from './components/sidebar.vue';
2020
import { useStore } from './app/store';
21+
import NProgress from 'nprogress';
2122
2223
// props
2324
@@ -33,6 +34,13 @@ const $store = useStore();
3334
// computed
3435
3536
// watch
37+
watch(() => $store.loading, (newValue, oldValue) => {
38+
if (!oldValue && newValue) {
39+
NProgress.start();
40+
} else if (!newValue && oldValue) {
41+
NProgress.done();
42+
}
43+
})
3644
3745
// functions
3846
</script>

phoenix-webapp/src/app/store.ts

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { defineStore } from 'pinia'
22

33
export interface AppState {
44
isAuthenticated: boolean;
5+
loading: boolean,
56
}
67

78
export const useStore = defineStore('store', {
@@ -12,12 +13,16 @@ export const useStore = defineStore('store', {
1213
},
1314
setIsAutenticated(isAuthenticated: boolean) {
1415
this.isAuthenticated = isAuthenticated;
16+
},
17+
setLoading(loading: boolean) {
18+
this.loading = loading;
1519
}
1620
},
1721
})
1822

1923
function defaultAppState(): AppState {
2024
return {
2125
isAuthenticated: false,
26+
loading: false,
2227
};
2328
}

phoenix-webapp/src/components/blogs.vue

+6-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</div>
1010
</div>
1111

12-
<div class="text-center" v-if="!loading && blogs.length === 0">
12+
<div class="text-center" v-if="!$store.loading && blogs.length === 0">
1313
<FireIcon class="inline-flex items-center -ml-0.5 mr-1.5 h-16 w-16 color-grey" aria-hidden="true" />
1414
<h3 class="mt-2 text-sm font-semibold text-gray-900">No blogs</h3>
1515
<p class="mt-1 text-sm text-gray-500">Get started by creating a new blog.</p>
@@ -23,7 +23,7 @@
2323
</div>
2424
</div>
2525

26-
<div v-else-if="!loading && blogs.length > 0" class="flex flex-col justify-center max-w-2xl mx-auto">
26+
<div v-else-if="!$store.loading && blogs.length > 0" class="flex flex-col justify-center max-w-2xl mx-auto">
2727
<div class="flex">
2828
<RouterLink :to="newBlogUrl">
2929
<CfButton>
@@ -66,19 +66,20 @@ import { onBeforeMount, ref, type Ref } from 'vue';
6666
import * as api from '@phoenix/core/api';
6767
import { PlusIcon, FireIcon, ChevronRightIcon } from '@heroicons/vue/24/outline';
6868
import CfButton from '@/components/cf_button.vue';
69+
import { useStore } from '@/app/store';
6970
7071
// props
7172
7273
// events
7374
7475
// composables
7576
const $apiClient = useApiClient();
77+
const $store = useStore();
7678
7779
// lifecycle
7880
onBeforeMount(() => fetchData());
7981
8082
// variables
81-
let loading = ref(false);
8283
let error = ref('');
8384
const newBlogUrl = '/blogs/new'
8485
@@ -90,15 +91,15 @@ const blogs: Ref<Blog[]> = ref([]);
9091
9192
// functions
9293
async function fetchData() {
93-
loading.value = true;
94+
$store.setLoading(true);
9495
error.value = '';
9596
9697
try {
9798
blogs.value = await api.getBlogs($apiClient);
9899
} catch (err: any) {
99100
error.value = err.message;
100101
} finally {
101-
loading.value = false;
102+
$store.setLoading(false);
102103
}
103104
}
104105
</script>

0 commit comments

Comments
 (0)