Compare commits

...

10 commits

Author SHA1 Message Date
nomadics9
9ae967e8af "403 fixes" 2024-05-12 23:11:02 +03:00
nomadics9
e137a8771b dependacies 2024-05-12 22:39:49 +03:00
nomadics9
d6413fa514 Forbidden page 2024-05-12 20:46:32 +03:00
nomadics9
3f4e529a53 o 2024-05-12 16:32:58 +03:00
nomadics9
06d965ecad oopsie 2024-05-12 16:26:48 +03:00
nomadics9
9257889989 unpublished fix 2024-05-12 08:22:20 +03:00
nomadics9
e8f064fe3b unpublished warning 2024-05-12 07:45:23 +03:00
nomadics9
7447155764 toggle fix 2024-05-12 07:27:41 +03:00
nomadics9
c77a9d26d1 landing page redirect + publish toggle 2024-05-12 07:10:30 +03:00
nomadics9
86f951a375 header metadata 2024-05-12 02:48:09 +03:00
11 changed files with 5970 additions and 5855 deletions

View file

@ -1,38 +1 @@
# create-svelte https://nmd.mov
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npm create svelte@latest
# create a new project in my-app
npm create svelte@latest my-app
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.

11384
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,45 +1,43 @@
{ {
"name": "firstapp", "name": "firstapp",
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
}, },
"devDependencies": { "devDependencies": {
"@jhubbardsf/svelte-sortablejs": "^1.1.0", "@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-node": "^5.0.1",
"@sveltejs/adapter-node": "^5.0.1", "@sveltejs/kit": "^2.0.0",
"@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0", "@types/node": "^20.12.11",
"@types/node": "^20.12.11", "autoprefixer": "^10.4.19",
"autoprefixer": "^10.4.19", "daisyui": "^4.10.3",
"daisyui": "^4.10.3", "postcss": "^8.4.38",
"postcss": "^8.4.38", "svelte": "^4.2.7",
"svelte": "^4.2.7", "svelte-check": "^3.6.0",
"svelte-check": "^3.6.0", "tailwindcss": "^3.4.3",
"tailwindcss": "^3.4.3", "tslib": "^2.4.1",
"tslib": "^2.4.1", "typescript": "^5.0.0",
"typescript": "^5.0.0", "vite": "^5.0.3"
"vite": "^5.0.3" },
}, "type": "module",
"type": "module", "dependencies": {
"dependencies": { "@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-avatar": "^1.0.4", "bits-ui": "^0.21.7",
"bits-ui": "^0.21.7", "class-variance-authority": "^0.7.0",
"class-variance-authority": "^0.7.0", "clsx": "^2.1.1",
"clsx": "^2.1.1", "firebase": "^10.11.1",
"drag-drop-touch": "^1.3.1", "firebase-admin": "^12.1.0",
"firebase": "^10.11.1", "lucide-react": "^0.378.0",
"firebase-admin": "^12.1.0", "svelte-drag-drop-touch": "^0.1.9",
"lucide-react": "^0.378.0", "svelte-toasts": "^1.1.2",
"svelte-drag-drop-touch": "^0.1.9", "tailwind-merge": "^2.3.0",
"svelte-toasts": "^1.1.2", "tailwind-variants": "^0.2.1",
"tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7"
"tailwind-variants": "^0.2.1", }
"tailwindcss-animate": "^1.0.7"
}
} }

View file

@ -0,0 +1,10 @@
<script lang="ts">
export let username = 'userdata.username'
</script>
<div class="text-center">
<h1 class=" text-warning text-4xl">403</h1>
<span class="text-gray-500">The Profile of <strong class="text-xl text-warning">@{username}</strong> is not public!</span>
</div>
<div class=" my-4">
<img src="403.png" alt="forbidden"/>
</div>

99
src/lib/firebase.ts Normal file
View file

@ -0,0 +1,99 @@
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { doc, getFirestore, onSnapshot } from "firebase/firestore";
import { getAuth, onAuthStateChanged, type User } from "firebase/auth";
import { getStorage } from "firebase/storage";
import { writable, type Readable, derived } from "svelte/store";
import { env } from "$env/dynamic/public";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: env.PUBLIC_API_KEY,
authDomain: env.PUBLIC_AUTH_DOMAIN,
projectId: env.PUBLIC_PROJECT_ID,
storageBucket: env.PUBLIC_STORAGE_BUCKET,
messagingSenderId: env.PUBLIC_MESSAGING_SENDER_ID,
appId: env.PUBLIC_APP_ID,
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
export const db = getFirestore();
export const auth = getAuth();
export const storage = getStorage();
/**
* @returns a store with the current firebase user
*/
function userStore() {
let unsubscribe: () => void;
if (!auth || !globalThis.window) {
console.warn("Auth is not initialized or not in browser");
const { subscribe } = writable<User | null>(null);
return {
subscribe,
};
}
const { subscribe } = writable(auth?.currentUser ?? null, (set) => {
unsubscribe = onAuthStateChanged(auth, (user) => {
set(user);
});
return () => unsubscribe();
});
return {
subscribe,
};
}
export const user = userStore();
/**
* @param {string} path document path or reference
* @returns a store with realtime updates on document data
*/
export function docStore<T>(path: string) {
let unsubscribe: () => void;
const docRef = doc(db, path);
const { subscribe } = writable<T | null>(null, (set) => {
unsubscribe = onSnapshot(docRef, (snapshot) => {
set((snapshot.data() as T) ?? null);
});
return () => unsubscribe();
});
return {
subscribe,
ref: docRef,
id: docRef.id,
};
}
interface UserData {
username: string;
bio: string;
photoURL: string;
links: any[];
published: boolean;
}
export const userData: Readable<UserData | null> = derived(
user,
($user, set) => {
if ($user) {
return docStore<UserData>(`users/${$user.uid}`).subscribe(set);
} else {
set(null);
}
},
);

View file

@ -1,4 +1,46 @@
<script lang="ts"> <script lang="ts">
</script> import { userData, user, auth} from "$lib/firebase";
import { goto } from '$app/navigation';
import { onMount } from "svelte";
function getLogged() {
if ($userData?.username != undefined) {
goto(`/${$userData?.username}`)
console.log(user)
}
else {
goto(`/login`)
}
}
onMount(() => {
setTimeout(getLogged, 2000)
})
</script>
<head>
<title>Linky</title>
<meta name="description" content="Your web tree!" />
<meta property="og:image" content="https://nmd.mov/favicon.png" />
<meta property="og:title" content="Linky" />
<meta property="og:description" content="Your web tree!" />
<meta property="og:url" content="https://nmd.mov" />
</head>
{#if $userData == undefined}
<div class="mx-auto my-auto">
<a href="/login" class="btn btn-outline">/Start</a>
</div>
{/if}
{#if $userData != undefined}
<div class="m-auto">
<span class="loading loading-spinner text-success"></span>
</div>
{/if}
<a href="/login" class="btn btn-outline mx-auto my-auto">/Start</a>

View file

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import UserLink from "$lib/components/UserLink.svelte"; import UserLink from "$lib/components/UserLink.svelte";
import type { PageData } from "./$types"; import type { PageData } from "./$types";
import { userData, auth } from "$lib/firebase"; import { userData, auth} from "$lib/firebase";
import { import {
signInWithPopup, signInWithPopup,
GoogleAuthProvider, GoogleAuthProvider,
@ -9,9 +9,10 @@
} from "firebase/auth"; } from "firebase/auth";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import { toasts, ToastContainer, FlatToast } from "svelte-toasts"; import { toasts, ToastContainer, FlatToast } from "svelte-toasts";
import { page } from "$app/stores"; import Myerror from "$lib/components/Myerror.svelte"
export let data: PageData; export let data: PageData;
async function signInWithGoogle() { async function signInWithGoogle() {
const provider = new GoogleAuthProvider(); const provider = new GoogleAuthProvider();
const user = await signInWithPopup(auth, provider); const user = await signInWithPopup(auth, provider);
@ -37,6 +38,10 @@
navigator.clipboard.writeText(copytext) navigator.clipboard.writeText(copytext)
showToast() showToast()
} }
let unpublished = data.published
let username = data.username
</script> </script>
<svelte:head> <svelte:head>
@ -48,13 +53,18 @@
<meta property="og:url" content="https://nmd.mov/{data.username}" /> <meta property="og:url" content="https://nmd.mov/{data.username}" />
</svelte:head> </svelte:head>
{#if unpublished == false && data.username != $userData?.username}
<div class="card card-body m-auto">
<Myerror {username}></Myerror>
</div>
{:else}
<main class=" prose text-center mx-auto mt-8"> <main class=" prose text-center mx-auto mt-8">
<h1 class="text-4xl text-teal-700"> <h1 class="text-4xl text-teal-700">
@{data.username} @{data.username}
</h1> </h1>
<img <img
src={data.photoURL ?? "/user.png"} src={data.photoURL ?? "/user.png"}
alt="photoURL" alt="profile"
width="128" width="128"
class="mx-auto rounded-2xl mt-8" class="mx-auto rounded-2xl mt-8"
/> />
@ -91,6 +101,9 @@
<FlatToast {data} /> <FlatToast {data} />
</ToastContainer> </ToastContainer>
</div> </div>
{#if $userData?.published == false}
<h2 class="text-center text-background">Your profile is not published!</h2>
{/if}
{:else} {:else}
<div class="top-0 right-0 absolute"> <div class="top-0 right-0 absolute">
@ -100,3 +113,4 @@
> >
</div> </div>
{/if} {/if}
{/if}

View file

@ -1,6 +1,6 @@
import { collection, getDocs, limit, query, where } from 'firebase/firestore'; import { collection, getDocs, limit, query, where } from 'firebase/firestore';
import type { PageLoad } from './$types'; import type { PageLoad } from './$types';
import { db } from '$lib/firebase'; import { db, userData} from '$lib/firebase';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
export const load = (async ({ params }) => { export const load = (async ({ params }) => {
@ -20,14 +20,11 @@ export const load = (async ({ params }) => {
throw error(404, "that user does not exists") throw error(404, "that user does not exists")
} }
if (!data.published) {
throw error(403, `The Profile of @${data.username} is not public!`)
}
return { return {
username: data.username, username: data.username,
photoURL: data.photoURL, photoURL: data.photoURL,
bio: data.bio, bio: data.bio,
links: data.links ?? [], links: data.links ?? [],
published: data.published
} }
}) satisfies PageLoad; }) satisfies PageLoad;

View file

@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import { page } from "$app/stores";
import { db, user, userData } from "$lib/firebase"; import { db, user, userData } from "$lib/firebase";
import { import {
arrayRemove, arrayRemove,
@ -91,6 +90,15 @@
}); });
} }
let publish = $userData?.published
function togglePublish() {
publish == !publish
const userRef = doc(db, "users", $user!.uid);
updateDoc(userRef, {
published: publish
});
}
function moveUp(index: any) { function moveUp(index: any) {
const userRef = doc(db, "users", $user!.uid) const userRef = doc(db, "users", $user!.uid)
@ -222,6 +230,12 @@
> >
Add a Link Add a Link
</button> </button>
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Published</span>
<input type="checkbox" class="toggle" bind:checked={publish} on:change={togglePublish}/>
</label>
</div>
{/if} {/if}
{/if} {/if}

BIN
static/403.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -2,67 +2,67 @@ import { fontFamily } from "tailwindcss/defaultTheme";
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
const config = { const config = {
darkMode: ["class"], darkMode: ["class"],
content: ["./src/**/*.{html,js,svelte,ts}"], content: ["./src/**/*.{html,js,svelte,ts}"],
plugins: [require("daisyui")], plugins: [require("daisyui")],
daisyui: { daisyui: {
themes: ["dark"], themes: ["dark"],
},
safelist: ["dark"],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
}, },
safelist: ["dark"], extend: {
theme: { colors: {
container: { border: "hsl(var(--border) / <alpha-value>)",
center: true, input: "hsl(var(--input) / <alpha-value>)",
padding: "2rem", ring: "hsl(var(--ring) / <alpha-value>)",
screens: { background: "hsl(var(--background) / <alpha-value>)",
"2xl": "1400px" foreground: "hsl(var(--foreground) / <alpha-value>)",
} primary: {
}, DEFAULT: "hsl(var(--primary) / <alpha-value>)",
extend: { foreground: "hsl(var(--primary-foreground) / <alpha-value>)",
colors: { },
border: "hsl(var(--border) / <alpha-value>)", secondary: {
input: "hsl(var(--input) / <alpha-value>)", DEFAULT: "hsl(var(--secondary) / <alpha-value>)",
ring: "hsl(var(--ring) / <alpha-value>)", foreground: "hsl(var(--secondary-foreground) / <alpha-value>)",
background: "hsl(var(--background) / <alpha-value>)", },
foreground: "hsl(var(--foreground) / <alpha-value>)", destructive: {
primary: { DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
DEFAULT: "hsl(var(--primary) / <alpha-value>)", foreground: "hsl(var(--destructive-foreground) / <alpha-value>)",
foreground: "hsl(var(--primary-foreground) / <alpha-value>)" },
}, muted: {
secondary: { DEFAULT: "hsl(var(--muted) / <alpha-value>)",
DEFAULT: "hsl(var(--secondary) / <alpha-value>)", foreground: "hsl(var(--muted-foreground) / <alpha-value>)",
foreground: "hsl(var(--secondary-foreground) / <alpha-value>)" },
}, accent: {
destructive: { DEFAULT: "hsl(var(--accent) / <alpha-value>)",
DEFAULT: "hsl(var(--destructive) / <alpha-value>)", foreground: "hsl(var(--accent-foreground) / <alpha-value>)",
foreground: "hsl(var(--destructive-foreground) / <alpha-value>)" },
}, popover: {
muted: { DEFAULT: "hsl(var(--popover) / <alpha-value>)",
DEFAULT: "hsl(var(--muted) / <alpha-value>)", foreground: "hsl(var(--popover-foreground) / <alpha-value>)",
foreground: "hsl(var(--muted-foreground) / <alpha-value>)" },
}, card: {
accent: { DEFAULT: "hsl(var(--card) / <alpha-value>)",
DEFAULT: "hsl(var(--accent) / <alpha-value>)", foreground: "hsl(var(--card-foreground) / <alpha-value>)",
foreground: "hsl(var(--accent-foreground) / <alpha-value>)" },
}, },
popover: { borderRadius: {
DEFAULT: "hsl(var(--popover) / <alpha-value>)", lg: "var(--radius)",
foreground: "hsl(var(--popover-foreground) / <alpha-value>)" md: "calc(var(--radius) - 2px)",
}, sm: "calc(var(--radius) - 4px)",
card: { },
DEFAULT: "hsl(var(--card) / <alpha-value>)", fontFamily: {
foreground: "hsl(var(--card-foreground) / <alpha-value>)" sans: [...fontFamily.sans],
} },
}, },
borderRadius: { },
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)"
},
fontFamily: {
sans: [...fontFamily.sans]
}
}
},
}; };
export default config; export default config;