Compare commits
3 Commits
1e057245ef
...
70d011423b
Author | SHA1 | Date | |
---|---|---|---|
70d011423b | |||
1374dbece4 | |||
630a1c043c |
17
src/components/tooltip.svelte
Normal file
17
src/components/tooltip.svelte
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Window from "./window.svelte";
|
||||||
|
|
||||||
|
export let x: string = "translate-x-none"
|
||||||
|
export let y: string = "translate-y-full"
|
||||||
|
export let targetY: string = "group-hover:-translate-y-[105%]"
|
||||||
|
export let targetX: string = "group-hover:-translate-x-2/3"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="group">
|
||||||
|
<div class="absolute scale-0 transition-all [transition-duration:300ms] opacity-0 group-hover:scale-100 group-hover:opacity-100 {y} {x} {targetY} {targetX} transform-gpu">
|
||||||
|
<Window tooltip>
|
||||||
|
<slot name="tooltipContent">Hello world!</slot>
|
||||||
|
</Window>
|
||||||
|
</div>
|
||||||
|
<slot/>
|
||||||
|
</div>
|
@ -11,6 +11,7 @@
|
|||||||
export let center: boolean = false;
|
export let center: boolean = false;
|
||||||
export let layered: boolean = false;
|
export let layered: boolean = false;
|
||||||
export let style: string = "";
|
export let style: string = "";
|
||||||
|
export let tooltip: boolean = false;
|
||||||
|
|
||||||
const scaleKeyframes = [
|
const scaleKeyframes = [
|
||||||
"window-open",
|
"window-open",
|
||||||
@ -46,9 +47,9 @@
|
|||||||
on:click={(data) => {focusWindow(data.currentTarget)}}
|
on:click={(data) => {focusWindow(data.currentTarget)}}
|
||||||
class="
|
class="
|
||||||
relative {layered ? "col-[1] row-[1]" : ""} flex flex-col {sticky ? 'md:sticky md:-top-9' : ''} {center ? "mx-auto" : ""}
|
relative {layered ? "col-[1] row-[1]" : ""} flex flex-col {sticky ? 'md:sticky md:-top-9' : ''} {center ? "mx-auto" : ""}
|
||||||
max-w-screen-md xl:max-w-screen-lg 2xl:max-w-screen-xl min-w-[30ch] lg:min-w-[40ch] w-full md:w-fit [height:fit-content]
|
max-w-screen-md xl:max-w-screen-lg 2xl:max-w-screen-xl {tooltip ? "min-w-fit" : "min-w-[30ch] lg:min-w-[40ch]"} w-full md:w-fit [height:fit-content]
|
||||||
bg-ralsei-black border-ralsei-white border-ridge border-8 border-t-[12px]
|
bg-ralsei-black border-ralsei-white border-ridge {tooltip ? "border-[6px] border-t-[9px]" : "border-8 border-t-[12px]"}
|
||||||
animate-{chosenKeyframe} drop-shadow-[35px_35px_35px_rgba(1,1,1,0.5)]
|
animate-{chosenKeyframe} drop-shadow-[24px_24px_24px_rgba(1,1,1,0.8)]
|
||||||
{style}
|
{style}
|
||||||
"
|
"
|
||||||
{id}
|
{id}
|
||||||
@ -83,7 +84,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="
|
<div class="
|
||||||
{removePadding ? "" : "p-2"} bg-gradient-to-tl
|
{removePadding ? "" : tooltip ? "p-1" : "p-2"} bg-gradient-to-tl
|
||||||
to-ralsei-pink-neon/15 from-ralsei-pink-regular/20
|
to-ralsei-pink-neon/15 from-ralsei-pink-regular/20
|
||||||
">
|
">
|
||||||
<slot />
|
<slot />
|
||||||
|
@ -7,31 +7,61 @@ import { get, writable } from "svelte/store";
|
|||||||
const visitCountFile = `${env.WEBSITE_DATA_DIR}/visitcount`
|
const visitCountFile = `${env.WEBSITE_DATA_DIR}/visitcount`
|
||||||
const visitCount = writable(parseInt(existsSync(visitCountFile) ? readFileSync(visitCountFile).toString() : '0'));
|
const visitCount = writable(parseInt(existsSync(visitCountFile) ? readFileSync(visitCountFile).toString() : '0'));
|
||||||
|
|
||||||
|
type Visitor = { since: number }
|
||||||
|
const lastVisitors = writable<Visitor[]>([]);
|
||||||
|
const MAX_VISITORS = 10
|
||||||
|
const VISITOR_EXPIRY_SECONDS = 60 * 30 // half an hour is reasonable
|
||||||
|
|
||||||
export const incrementVisitCount = (request: Request, cookies: Cookies) => {
|
export const incrementVisitCount = (request: Request, cookies: Cookies) => {
|
||||||
let currentVisitCount = get(visitCount)
|
let currentVisitCount = get(visitCount)
|
||||||
// check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
|
// check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
|
||||||
const ua = request.headers.get('user-agent')
|
if (isBot(request)) { return currentVisitCount }
|
||||||
const isBot = ua ? ua.toLowerCase().match(/(bot|crawl|spider|walk)/) !== null : true
|
const scopedCookies = scopeCookies(cookies, '/')
|
||||||
if (!isBot) {
|
// parse the last visit timestamp from cookies if it exists
|
||||||
const scopedCookies = scopeCookies(cookies, '/')
|
const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || "0")
|
||||||
// parse the last visit timestamp from cookies if it exists
|
// get unix timestamp
|
||||||
const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || "0")
|
const currentTime = Date.now()
|
||||||
// get unix timestamp
|
const timeSinceVisit = currentTime - visitedTimestamp
|
||||||
const currentTime = new Date().getTime()
|
// check if this is the first time a client is visiting or if an hour has passed since they last visited
|
||||||
const timeSinceVisit = currentTime - visitedTimestamp
|
if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) {
|
||||||
// check if this is the first time a client is visiting or if an hour has passed since they last visited
|
// increment current and write to the store
|
||||||
if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) {
|
currentVisitCount += 1; visitCount.set(currentVisitCount)
|
||||||
// increment current and write to the store
|
// update the cookie with the current timestamp
|
||||||
currentVisitCount += 1; visitCount.set(currentVisitCount)
|
scopedCookies.set('visitedTimestamp', currentTime.toString())
|
||||||
// update the cookie with the current timestamp
|
// write the visit count to a file so we can load it later again
|
||||||
scopedCookies.set('visitedTimestamp', currentTime.toString())
|
writeFileSync(visitCountFile, currentVisitCount.toString())
|
||||||
// write the visit count to a file so we can load it later again
|
|
||||||
writeFileSync(visitCountFile, currentVisitCount.toString())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return currentVisitCount
|
return currentVisitCount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const addLastVisitor = (request: Request, cookies: Cookies) => {
|
||||||
|
const currentTime = Date.now()
|
||||||
|
let visitors = get(lastVisitors).filter(
|
||||||
|
(value) => { return currentTime - value.since > 1000 * VISITOR_EXPIRY_SECONDS }
|
||||||
|
)
|
||||||
|
// check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
|
||||||
|
if (isBot(request)) { return visitors }
|
||||||
|
const scopedCookies = scopeCookies(cookies, '/')
|
||||||
|
// parse the last visit timestamp from cookies if it exists
|
||||||
|
const visitorTimestamp = parseInt(scopedCookies.get('visitorTimestamp') || "0")
|
||||||
|
// get unix timestamp
|
||||||
|
const timeSinceVisit = currentTime - visitorTimestamp
|
||||||
|
// check if this is the first time a client is visiting or if an hour has passed since they last visited
|
||||||
|
if (visitorTimestamp === 0 || timeSinceVisit > 1000 * VISITOR_EXPIRY_SECONDS) {
|
||||||
|
visitors.push({ since: currentTime })
|
||||||
|
if (visitors.length > MAX_VISITORS) { visitors.shift() }
|
||||||
|
// update the cookie with the current timestamp
|
||||||
|
scopedCookies.set('visitorTimestamp', currentTime.toString())
|
||||||
|
}
|
||||||
|
return visitors
|
||||||
|
}
|
||||||
|
|
||||||
|
const isBot = (request: Request) => {
|
||||||
|
const ua = request.headers.get('user-agent')
|
||||||
|
return ua ? ua.toLowerCase().match(/(bot|crawl|spider|walk|fetch|scrap|proxy|image)/) !== null : true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export const notifyDarkVisitors = (url: URL, request: Request) => {
|
export const notifyDarkVisitors = (url: URL, request: Request) => {
|
||||||
fetch('https://api.darkvisitors.com/visits', {
|
fetch('https://api.darkvisitors.com/visits', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import getTitle from '$lib/getTitle';
|
import getTitle from '$lib/getTitle';
|
||||||
import NavButton from '../components/navButton.svelte';
|
import NavButton from '../components/navButton.svelte';
|
||||||
|
import Tooltip from '../components/tooltip.svelte';
|
||||||
import Window from '../components/window.svelte';
|
import Window from '../components/window.svelte';
|
||||||
import '../styles/app.css';
|
import '../styles/app.css';
|
||||||
|
|
||||||
@ -157,14 +158,12 @@
|
|||||||
<a class="hover:underline" href="https://xn--sr8hvo.ws">IndieWeb 🕸💍</a>
|
<a class="hover:underline" href="https://xn--sr8hvo.ws">IndieWeb 🕸💍</a>
|
||||||
<a title="next site" class="hover:underline" href="https://xn--sr8hvo.ws/next">⮞</a>
|
<a title="next site" class="hover:underline" href="https://xn--sr8hvo.ws/next">⮞</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="group navbox">
|
<Tooltip>
|
||||||
<div class="absolute transition-all opacity-0 group-hover:opacity-100 translate-y-2/3 -translate-x-1/3 group-hover:-translate-y-2/3 transform-gpu">
|
<svelte:fragment slot="tooltipContent">
|
||||||
<Window style="!min-w-fit">
|
<img class="min-w-64" style="image-rendering: crisp-edges pixelated;" alt="visits" src="https://count.getloli.com/@yusdacrawebsite?name=yusdacrawebsitetest&theme=booru-lewd&padding=5&offset=0&align=center&scale=1&pixelated=1&darkmode=0&num={data.visitCount}"/>
|
||||||
<img class="min-w-64" style="image-rendering: crisp-edges pixelated;" alt="visits" src="https://count.getloli.com/@yusdacrawebsite?name=yusdacrawebsitetest&theme=booru-lewd&padding=5&offset=0&align=center&scale=1&pixelated=1&darkmode=0&num={data.visitCount}"/>
|
</svelte:fragment>
|
||||||
</Window>
|
<div class="navbox"><p><span class="text-ralsei-green-light text-shadow-green">{data.visitCount}</span> visit(s)</p></div>
|
||||||
</div>
|
</Tooltip>
|
||||||
<p><span class="text-ralsei-green-light text-shadow-green">{data.visitCount}</span> visit(s)</p>
|
|
||||||
</div>
|
|
||||||
{#if isRoute("entries")}
|
{#if isRoute("entries")}
|
||||||
<div class="navbox !gap-1">
|
<div class="navbox !gap-1">
|
||||||
<a class="align-middle hover:underline" href="/entries/_rss">rss</a>
|
<a class="align-middle hover:underline" href="/entries/_rss">rss</a>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { PUBLIC_BASE_URL } from '$env/static/public';
|
import { PUBLIC_BASE_URL } from '$env/static/public';
|
||||||
|
import Tooltip from '../components/tooltip.svelte';
|
||||||
import Window from '../components/window.svelte';
|
import Window from '../components/window.svelte';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
@ -10,12 +11,14 @@
|
|||||||
<Window title="readme?" iconUri="/icons/question.png">
|
<Window title="readme?" iconUri="/icons/question.png">
|
||||||
<div class="flex flex-col prose prose-ralsei prose-img:m-0 leading-none">
|
<div class="flex flex-col prose prose-ralsei prose-img:m-0 leading-none">
|
||||||
<div class="flex flex-grow">
|
<div class="flex flex-grow">
|
||||||
<img
|
<Tooltip x="-translate-x-[40%]" targetX="group-hover:-translate-x-[10%]">
|
||||||
class="mt-1 ml-1 w-36 [height:9rem] u-photo"
|
<svelte:fragment slot="tooltipContent">character from q.u.q. (good vn go read it NOW)</svelte:fragment>
|
||||||
src="/pfp.png"
|
<img
|
||||||
alt="character from q.u.q."
|
class="mt-1 ml-1 w-36 [height:9rem] u-photo"
|
||||||
title="character from q.u.q. (good vn go read it NOW)"
|
src="/pfp.png"
|
||||||
/>
|
alt="character from q.u.q."
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
<ul
|
<ul
|
||||||
class="place-self-center m-0 leading-none marker:[content:'->'] [list-style-type:'->']"
|
class="place-self-center m-0 leading-none marker:[content:'->'] [list-style-type:'->']"
|
||||||
>
|
>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Window from '../../components/window.svelte';
|
import Tooltip from '../../components/tooltip.svelte';
|
||||||
|
import Window from '../../components/window.svelte';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
$: hasPreviousPage = data.page > 1;
|
$: hasPreviousPage = data.page > 1;
|
||||||
@ -43,15 +44,18 @@
|
|||||||
<p class="text-sm font-monospace">--- posted by ...</p>
|
<p class="text-sm font-monospace">--- posted by ...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="entry flex flex-wrap gap-1.5 p-1">
|
<div class="entry flex flex-wrap gap-1.5 p-1 items-center">
|
||||||
<p class="text-xl ms-2">auth via:</p>
|
<p class="text-xl ms-2 align-middle">auth via:</p>
|
||||||
{#each ['discord', 'github'] as platform}
|
{#each ['discord', 'github'] as platform}
|
||||||
|
<Tooltip x="" y="translate-y-[70%]" targetY="" targetX="">
|
||||||
|
<svelte:fragment slot="tooltipContent">post with {platform}</svelte:fragment>
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
value={platform}
|
value={platform}
|
||||||
formaction="?/post_{platform}"
|
formaction="?/post_{platform}"
|
||||||
class="text-lg text-ralsei-green-light leading-5 hover:underline motion-safe:hover:animate-squiggle w-fit p-0.5"
|
class="text-lg text-ralsei-green-light leading-5 hover:underline motion-safe:hover:animate-squiggle w-fit p-0.5"
|
||||||
/>
|
/>
|
||||||
|
</Tooltip>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if data.sendRatelimited}
|
{#if data.sendRatelimited}
|
||||||
|
Loading…
Reference in New Issue
Block a user