add favicon
Gitea Actions Demo / build_and_push (push) Successful in 1m14s Details

This commit is contained in:
artem 2026-09-19 01:15:22 +03:00
parent 6f223e61d2
commit 72e1d4223d
5 changed files with 57 additions and 65 deletions

View File

@ -16,7 +16,9 @@
rel="stylesheet" rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,300..600,0,0" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,300..600,0,0"
/> />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<title>Cycle Rider Ala Strava for Russian/Belorussian</title> <title>Cycle Rider Ala Strava for Russian/Belorussian</title>
</head> </head>

44
public/favicon.svg Normal file
View File

@ -0,0 +1,44 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<!-- Background -->
<circle cx="32" cy="32" r="30" fill="#228BE6"/>
<!-- Rear wheel -->
<circle cx="16" cy="40" r="11" fill="none" stroke="white" stroke-width="3"/>
<!-- Front wheel -->
<circle cx="48" cy="40" r="11" fill="none" stroke="white" stroke-width="3"/>
<!-- Frame: chain stay (BB to rear hub) -->
<line x1="32" y1="40" x2="16" y2="40" stroke="white" stroke-width="2.5"/>
<!-- Frame: seat tube (BB to seat) -->
<line x1="32" y1="40" x2="26" y2="24" stroke="white" stroke-width="2.5"/>
<!-- Frame: top tube (seat to head tube) -->
<line x1="26" y1="24" x2="44" y2="24" stroke="white" stroke-width="2.5"/>
<!-- Frame: down tube (BB to head tube) -->
<line x1="32" y1="40" x2="44" y2="24" stroke="white" stroke-width="2.5"/>
<!-- Frame: seat stay (seat to rear hub) -->
<line x1="26" y1="24" x2="16" y2="40" stroke="white" stroke-width="2.5"/>
<!-- Frame: fork (head tube to front hub) -->
<line x1="44" y1="24" x2="48" y2="40" stroke="white" stroke-width="2.5"/>
<!-- Handlebar (drop bar shape) -->
<path d="M44 24 C47 22 49 24 47 27" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round"/>
<!-- Seat -->
<line x1="23" y1="23" x2="28" y2="23" stroke="white" stroke-width="3" stroke-linecap="round"/>
<!-- Crank + pedal -->
<line x1="32" y1="40" x2="34" y2="47" stroke="white" stroke-width="2" stroke-linecap="round"/>
<line x1="31" y1="48" x2="36" y2="48" stroke="white" stroke-width="2" stroke-linecap="round"/>
<!-- Rider: rear leg (hip -> knee -> pedal) -->
<path d="M27 23 L31 33 L34 47" fill="none" stroke="white" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>
<!-- Rider: torso (hips curving forward to shoulders) -->
<path d="M27 22 Q34 19 39 19" fill="none" stroke="white" stroke-width="4.5" stroke-linecap="round"/>
<!-- Rider: arm (shoulder to handlebar) -->
<path d="M39 19 L45 24" fill="none" stroke="white" stroke-width="3" stroke-linecap="round"/>
<!-- Rider: head (helmet - slightly elongated) -->
<ellipse cx="43" cy="15" rx="4" ry="3.5" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -2,6 +2,11 @@
"name": "", "name": "",
"short_name": "", "short_name": "",
"icons": [ "icons": [
{
"src": "/favicon.svg",
"sizes": "any",
"type": "image/svg+xml"
},
{ {
"src": "/android-chrome-192x192.png", "src": "/android-chrome-192x192.png",
"sizes": "192x192", "sizes": "192x192",

View File

@ -48,7 +48,7 @@ export function renderTemplate(options: TemplateOptions): string {
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content="${meta.canonicalUrl}" /> <meta property="og:url" content="${meta.canonicalUrl}" />
${meta.ogImage ? `<meta property="og:image" content="${meta.ogImage}" />` : ""} ${meta.ogImage ? `<meta property="og:image" content="${meta.ogImage}" />` : ""}
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet" />
${assetTags} ${assetTags}
${jsonLdScript} ${jsonLdScript}

View File

@ -10,16 +10,6 @@
:hideFileList="true" :hideFileList="true"
dropzone dropzone
/> />
<div class="photo-upload" v-if="!inProgress">
<h2 class="photo-upload__title">Фото тренировки (опционально)</h2>
<VaFileUpload
v-model="photoFiles"
file-types="png,jpg,jpeg,webp,heic"
type="list"
color="#F4F6F8"
dropzone
/>
</div>
<div v-else> <div v-else>
<VaInnerLoading loading :size="60"> </VaInnerLoading> <VaInnerLoading loading :size="60"> </VaInnerLoading>
</div> </div>
@ -37,7 +27,6 @@ const { init } = useToast();
const router = useRouter(); const router = useRouter();
const inProgress = ref(false); const inProgress = ref(false);
let file: File | undefined = undefined; let file: File | undefined = undefined;
const photoFiles = ref<File[]>([]);
type ErrorItem = { type ErrorItem = {
code_string: string; code_string: string;
@ -47,31 +36,6 @@ type Error = {
detail: ErrorItem; detail: ErrorItem;
}; };
async function uploadPhotos(workoutId: string): Promise<boolean> {
const total = photoFiles.value.length;
if (total === 0) {
return true;
}
const results = await Promise.allSettled(
photoFiles.value.map((photo) => {
const formData = new FormData();
formData.append("file", photo);
return axiosAuth.post(`/api/v0/workouts/${workoutId}/photos`, formData);
}),
);
const failed = results.filter(
(r): r is PromiseRejectedResult => r.status === "rejected",
).length;
if (failed > 0) {
init({
message: `Часть фото не загружена (${failed} из ${total})`,
color: "error",
});
return false;
}
return true;
}
function onFileChanged() { function onFileChanged() {
if (file == undefined) { if (file == undefined) {
init({ init({
@ -91,25 +55,13 @@ function onFileChanged() {
attachment_id: response.data.id, attachment_id: response.data.id,
name: "Новая тренировка", name: "Новая тренировка",
}) })
.then(async (response: AxiosResponse) => { .then((response: AxiosResponse) => {
const workoutId = response.data.id; const workoutId = response.data.id;
const hasPhotos = photoFiles.value.length > 0; init({
if (hasPhotos) { message: "Тренировка успешно загружена!",
const ok = await uploadPhotos(workoutId); color: "success",
if (ok) { });
init({
message: "Тренировка успешно загружена!",
color: "success",
});
}
} else {
init({
message: "Тренировка успешно загружена!",
color: "success",
});
}
inProgress.value = false; inProgress.value = false;
photoFiles.value = [];
router.push({ router.push({
name: "workout_item", name: "workout_item",
params: { id: workoutId }, params: { id: workoutId },
@ -117,7 +69,6 @@ function onFileChanged() {
}) })
.catch((error: AxiosError) => { .catch((error: AxiosError) => {
inProgress.value = false; inProgress.value = false;
photoFiles.value = [];
if (error.status == 400) { if (error.status == 400) {
let err = <Error>error.response?.data; let err = <Error>error.response?.data;
if (err.detail.code_string == "ObjectExists") { if (err.detail.code_string == "ObjectExists") {
@ -136,7 +87,6 @@ function onFileChanged() {
}) })
.catch(function () { .catch(function () {
inProgress.value = false; inProgress.value = false;
photoFiles.value = [];
init({ init({
message: "Что-то пошло не так.", message: "Что-то пошло не так.",
color: "error", color: "error",
@ -144,12 +94,3 @@ function onFileChanged() {
}); });
} }
</script> </script>
<style scoped>
.photo-upload {
margin-top: 16px;
}
.photo-upload__title {
font-size: 18px;
margin-bottom: 8px;
}
</style>