mirror of
https://github.com/edufeed-org/polloer.git
synced 2025-12-07 23:34:31 +00:00
cleanup,
This commit is contained in:
parent
34781e8c29
commit
1878d82fad
7 changed files with 218 additions and 252 deletions
29
package-lock.json
generated
29
package-lock.json
generated
|
|
@ -774,37 +774,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nostr-dev-kit/ndk-svelte": {
|
"node_modules/@nostr-dev-kit/ndk-svelte": {
|
||||||
"version": "2.4.10",
|
"version": "2.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk-svelte/-/ndk-svelte-2.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk-svelte/-/ndk-svelte-2.4.11.tgz",
|
||||||
"integrity": "sha512-meBNrgcVXqM/VFPO8LykhiSncMbqvfMWqbtRF4SOsLwDMbK7IigRKtuUUJoHvalEn+3OB2TZYp/FV9EIMHQ00w==",
|
"integrity": "sha512-HCHFVQJ0lJBaGmMooJzmUhpTCATMtssbZsyYDLs729xl2xO//fOOGkiV3IUePhk0nKS85TfalP0aCXouQG/XMQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nostr-dev-kit/ndk": "2.14.4"
|
"@nostr-dev-kit/ndk": "2.14.5"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"svelte": "*"
|
"svelte": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nostr-dev-kit/ndk-svelte/node_modules/@nostr-dev-kit/ndk": {
|
|
||||||
"version": "2.14.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-2.14.4.tgz",
|
|
||||||
"integrity": "sha512-mh7IoKvzDXoh6PJNyvrNVAMm6Zor1xyEKjnVfXQ11WTDXCwOA8Ksff1qxVQOn7u6ZlxlZN4wHBeUwWswuD9FSA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@noble/curves": "^1.6.0",
|
|
||||||
"@noble/hashes": "^1.5.0",
|
|
||||||
"@noble/secp256k1": "^2.1.0",
|
|
||||||
"@scure/base": "^1.1.9",
|
|
||||||
"debug": "^4.3.6",
|
|
||||||
"light-bolt11-decoder": "^3.2.0",
|
|
||||||
"tseep": "^1.3.1",
|
|
||||||
"typescript-lru-cache": "^2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"nostr-tools": "^2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@polka/url": {
|
"node_modules/@polka/url": {
|
||||||
"version": "1.0.0-next.29",
|
"version": "1.0.0-next.29",
|
||||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
|
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,14 @@
|
||||||
|
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
import { ndk, ndkReady, user } from '$lib/stores';
|
import { ndk } from '$lib/stores';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { login } from '$lib';
|
|
||||||
import { Carta, Markdown, MarkdownEditor } from 'carta-md';
|
import { Carta, Markdown, MarkdownEditor } from 'carta-md';
|
||||||
import 'carta-md/default.css'; /* Default theme */
|
import 'carta-md/default.css'; /* Default theme */
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import { Confetti } from "svelte-confetti"
|
import { Confetti } from 'svelte-confetti';
|
||||||
|
|
||||||
|
|
||||||
console.log("show reactions", showReactions, event)
|
|
||||||
|
|
||||||
|
console.log('show reactions', showReactions, event);
|
||||||
|
|
||||||
// Create a new instance of Carta (you might also want to add a sanitizer if you're processing user input)
|
// Create a new instance of Carta (you might also want to add a sanitizer if you're processing user input)
|
||||||
let carta = new Carta({
|
let carta = new Carta({
|
||||||
|
|
@ -21,7 +18,7 @@
|
||||||
});
|
});
|
||||||
let reactions = writable([]);
|
let reactions = writable([]);
|
||||||
let reacted = writable(window.localStorage.getItem(event.id));
|
let reacted = writable(window.localStorage.getItem(event.id));
|
||||||
let reaction = writable({})
|
let reaction = writable({});
|
||||||
let clicked = writable(false);
|
let clicked = writable(false);
|
||||||
|
|
||||||
async function sendReaction() {
|
async function sendReaction() {
|
||||||
|
|
@ -31,28 +28,28 @@
|
||||||
tags: [['e', event.id]]
|
tags: [['e', event.id]]
|
||||||
});
|
});
|
||||||
await reactionEvent.publish();
|
await reactionEvent.publish();
|
||||||
$reaction = reactionEvent;
|
$reaction = reactionEvent;
|
||||||
const r = await $ndk.fetchEvents({ kinds: [7], '#e': [event.id] });
|
const r = await $ndk.fetchEvents({ kinds: [7], '#e': [event.id] });
|
||||||
console.log('r', r);
|
console.log('r', r);
|
||||||
$reactions = Array.from(r);
|
$reactions = Array.from(r);
|
||||||
window.localStorage.setItem(event.id, 'true');
|
window.localStorage.setItem(event.id, 'true');
|
||||||
$reacted = true;
|
$reacted = 'true';
|
||||||
$clicked = true
|
$clicked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteVote() {
|
async function deleteVote() {
|
||||||
const deletionEvent = new NDKEvent($ndk, {
|
const deletionEvent = new NDKEvent($ndk, {
|
||||||
kind: 5,
|
kind: 5,
|
||||||
content: "User deleted vote",
|
content: 'User deleted vote',
|
||||||
tags: [
|
tags: [
|
||||||
["e", event.id],
|
['e', event.id],
|
||||||
["k", 7]
|
['k', 7]
|
||||||
]
|
]
|
||||||
})
|
});
|
||||||
await deletionEvent.publish()
|
await deletionEvent.publish();
|
||||||
window.localStorage.removeItem(event.id)
|
window.localStorage.removeItem(event.id);
|
||||||
$reacted = false
|
$reacted = 'false';
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const r = await $ndk.fetchEvents({ kinds: [7], '#e': [event.id] });
|
const r = await $ndk.fetchEvents({ kinds: [7], '#e': [event.id] });
|
||||||
|
|
@ -65,12 +62,12 @@
|
||||||
<Markdown {carta} value={event.content} />
|
<Markdown {carta} value={event.content} />
|
||||||
{/key}
|
{/key}
|
||||||
|
|
||||||
<div class="flex gap-2 reactions">
|
<div class="reactions flex gap-2">
|
||||||
{#if $reacted}
|
{#if $reacted}
|
||||||
<span>👍 {$reactions.length}</span>
|
<span>👍 {$reactions.length}</span>
|
||||||
<span class="thanks">Danke für deinen Vote!</span>
|
<span class="thanks">Danke für deinen Vote!</span>
|
||||||
<!-- <button onclick={() => deleteVote()} class="btn">Vote zurückziehen</button> -->
|
<!-- <button onclick={() => deleteVote()} class="btn">Vote zurückziehen</button> -->
|
||||||
{:else if showReactions === "true"}
|
{:else if showReactions === 'true'}
|
||||||
<button onclick={() => sendReaction()} class="like">👍</button>
|
<button onclick={() => sendReaction()} class="like">👍</button>
|
||||||
<span>{$reactions.length}</span>
|
<span>{$reactions.length}</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
@ -79,19 +76,20 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.like {
|
.like {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.reactions {
|
.reactions {
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.thanks {
|
.thanks {
|
||||||
color: #777;
|
color: #777;
|
||||||
}
|
}
|
||||||
.comment {
|
.comment {
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
border-color: #ccc;
|
border-color: #ccc;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,12 @@
|
||||||
import { NDKNip07Signer, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
import { NDKNip07Signer, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
||||||
import { ndk as ndkStore, user as userStore } from "$lib/stores";
|
import { ndk } from "$lib/stores";
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
|
|
||||||
export async function login() {
|
export async function login() {
|
||||||
let ndk = get(ndkStore)
|
|
||||||
let user = get(userStore)
|
|
||||||
if (window.nostr) {
|
if (window.nostr) {
|
||||||
const signer = new NDKNip07Signer();
|
const signer = new NDKNip07Signer();
|
||||||
ndk.signer = signer;
|
get(ndk).signer = signer;
|
||||||
const signedUser = await signer.user();
|
signer.user();
|
||||||
userStore.set(signedUser)
|
|
||||||
} else {
|
} else {
|
||||||
console.log("no extension")
|
console.log("no extension")
|
||||||
const storedPrivateKey = window.localStorage.getItem('nostrPrivateKey');
|
const storedPrivateKey = window.localStorage.getItem('nostrPrivateKey');
|
||||||
|
|
@ -17,24 +14,20 @@ export async function login() {
|
||||||
const privateKey = JSON.parse(storedPrivateKey);
|
const privateKey = JSON.parse(storedPrivateKey);
|
||||||
console.log("stored private key", privateKey)
|
console.log("stored private key", privateKey)
|
||||||
const signer = new NDKPrivateKeySigner(privateKey);
|
const signer = new NDKPrivateKeySigner(privateKey);
|
||||||
const signedUser = await signer.user();
|
ndk.update((ndk) => {
|
||||||
userStore.set(signedUser)
|
ndk.signer = signer;
|
||||||
ndk.signer = signer;
|
return ndk;
|
||||||
console.log("ndk signer", ndk)
|
});
|
||||||
ndkStore.set(ndk)
|
// get(ndk).signer = signer;
|
||||||
|
signer.user();
|
||||||
} else {
|
} else {
|
||||||
console.log('No private key found, generating a new one...');
|
console.log('No private key found, generating a new one...');
|
||||||
const privateKey = NDKPrivateKeySigner.generate();
|
const privateKey = NDKPrivateKeySigner.generate();
|
||||||
const signer = new NDKPrivateKeySigner(privateKey.privateKey);
|
const signer = new NDKPrivateKeySigner(privateKey.privateKey);
|
||||||
console.log('Generated Private Key:', privateKey);
|
console.log('Generated Private Key:', privateKey);
|
||||||
|
signer.user();
|
||||||
const signedUser = await signer.user();
|
|
||||||
userStore.set(signedUser)
|
|
||||||
|
|
||||||
window.localStorage.setItem('nostrPrivateKey', JSON.stringify(privateKey.privateKey));
|
window.localStorage.setItem('nostrPrivateKey', JSON.stringify(privateKey.privateKey));
|
||||||
ndk.signer = signer;
|
get(ndk).signer = signer;
|
||||||
console.log("ndk signer", ndk)
|
|
||||||
ndkStore.set(ndk)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,21 @@
|
||||||
import NDKSvelte from "@nostr-dev-kit/ndk-svelte";
|
import NDKSvelte from "@nostr-dev-kit/ndk-svelte";
|
||||||
import { writable, derived } from "svelte/store";
|
import { writable, derived } from "svelte/store";
|
||||||
import { NDKNip07Signer } from "@nostr-dev-kit/ndk";
|
import { NDKUser } from "@nostr-dev-kit/ndk";
|
||||||
|
|
||||||
export let connected = writable(false);
|
export let connected = writable(false);
|
||||||
|
|
||||||
export const ndk = writable(new NDKSvelte());
|
const _ndk = new NDKSvelte({
|
||||||
|
explicitRelayUrls: [
|
||||||
|
'wss://relay-rpi.edufeed.org'
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
export const ndkReady = derived(ndk, $ndk => $ndk !== null);
|
export const ndk = writable(_ndk)
|
||||||
|
|
||||||
export async function initializeNDK() {
|
export const user = derived(ndk, $ndk => {
|
||||||
const signer = new NDKNip07Signer
|
console.log("updating user")
|
||||||
const ndkInstance = new NDKSvelte({
|
if ($ndk.signer !== undefined) {
|
||||||
explicitRelayUrls: [
|
return new NDKUser($ndk.signer);
|
||||||
'wss://relay-rpi.edufeed.org'
|
}
|
||||||
],
|
return undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
await ndkInstance.connect();
|
|
||||||
|
|
||||||
// ndkInstance.signer = signer;
|
|
||||||
ndk.set(ndkInstance);
|
|
||||||
|
|
||||||
return ndkInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const user = writable(null);
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,19 @@
|
||||||
<script>
|
<script>
|
||||||
import '../app.css';
|
import '../app.css';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { NDKNip07Signer } from '@nostr-dev-kit/ndk';
|
import { ndk, connected } from '$lib/stores';
|
||||||
import NDKSvelte from '@nostr-dev-kit/ndk-svelte/svelte5';
|
|
||||||
import { ndk, connected, initializeNDK } from '$lib/stores';
|
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import { login } from '$lib';
|
import { login } from '$lib';
|
||||||
|
|
||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const nip07signer = new NDKNip07Signer();
|
|
||||||
if (browser) {
|
if (browser) {
|
||||||
try {
|
try {
|
||||||
await initializeNDK();
|
await login()
|
||||||
|
await $ndk.connect();
|
||||||
console.log('NDK initialized successfully');
|
console.log('NDK initialized successfully');
|
||||||
connected.set(true);
|
connected.set(true);
|
||||||
await login()
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to initialize NDK:', error);
|
console.error('Failed to initialize NDK:', error);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,23 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { ndk, connected, user, ndkReady } from '$lib/stores';
|
import { ndk, connected, user } from '$lib/stores';
|
||||||
import { NDKEvent, NDKNip07Signer, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import QRCode from 'qrcode';
|
import QRCode from 'qrcode';
|
||||||
import { login } from '$lib';
|
import { login } from '$lib';
|
||||||
import { Carta, MarkdownEditor } from 'carta-md';
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
import 'carta-md/default.css'; /* Default theme */
|
import 'carta-md/default.css'; /* Default theme */
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import { Confetti } from "svelte-confetti"
|
import { Confetti } from 'svelte-confetti';
|
||||||
|
|
||||||
// Create a new instance of Carta (you might also want to add a sanitizer if you're processing user input)
|
// Create a new instance of Carta (you might also want to add a sanitizer if you're processing user input)
|
||||||
let carta = new Carta({
|
let carta = new Carta({
|
||||||
sanitizer: DOMPurify.sanitize
|
sanitizer: DOMPurify.sanitize
|
||||||
});
|
});
|
||||||
let question = writable('');
|
let question = writable('');
|
||||||
let questionId = writable(''); ;
|
let questionId = writable('');
|
||||||
let questionShortId = 0;
|
let questionShortId = 0;
|
||||||
let qrCodeUrl = writable('');
|
let qrCodeUrl = writable('');
|
||||||
let timer = 0;
|
|
||||||
let votingEnabled = false;
|
let votingEnabled = false;
|
||||||
let sessionId = '';
|
let sessionId = '';
|
||||||
let event = writable();
|
let event = writable();
|
||||||
|
|
@ -27,13 +26,13 @@
|
||||||
console.log('join ' + sessionId);
|
console.log('join ' + sessionId);
|
||||||
const filter = {
|
const filter = {
|
||||||
kinds: [1342],
|
kinds: [1342],
|
||||||
'#d': [sessionId + ''], // filter by `d` tag
|
'#d': [sessionId + ''] // filter by `d` tag
|
||||||
};
|
};
|
||||||
console.log(filter)
|
console.log(filter);
|
||||||
const question = await $ndk.fetchEvent(filter);
|
const question = await $ndk.fetchEvent(filter);
|
||||||
console.log('question', question);
|
console.log('question', question);
|
||||||
goto('/q/' + question.id);
|
goto('/q/' + question.id);
|
||||||
login()
|
login();
|
||||||
}
|
}
|
||||||
async function postQuestion() {
|
async function postQuestion() {
|
||||||
questionShortId = 10000000 + Math.floor(Math.random() * 90000000);
|
questionShortId = 10000000 + Math.floor(Math.random() * 90000000);
|
||||||
|
|
@ -43,39 +42,20 @@
|
||||||
tags: [['d', questionShortId + '']]
|
tags: [['d', questionShortId + '']]
|
||||||
});
|
});
|
||||||
await $event.publishReplaceable();
|
await $event.publishReplaceable();
|
||||||
console.log("event id", $event.id)
|
console.log('event id', $event.id);
|
||||||
$questionId = `${$event.kind}:${$event.pubkey}:${$event.dTag}`;
|
$questionId = `${$event.kind}:${$event.pubkey}:${$event.dTag}`;
|
||||||
$qrCodeUrl = await QRCode.toDataURL(`${window.location.origin}/q/${$questionId}`, {
|
$qrCodeUrl = await QRCode.toDataURL(`${window.location.origin}/q/${$questionId}`, {
|
||||||
width: 800,
|
width: 800
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function startTimer() {
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
if (timer > 0) {
|
|
||||||
timer--;
|
|
||||||
} else {
|
|
||||||
clearInterval(interval);
|
|
||||||
votingEnabled = true;
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if ($ndkReady) {
|
|
||||||
if ($ndk.activeUser) {
|
|
||||||
console.log('User:', $user);
|
|
||||||
} else {
|
|
||||||
login();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="main-layout">
|
<div class="main-layout">
|
||||||
<img src="logo.png" alt="" class="logo">
|
<img src="logo.png" alt="" class="logo" />
|
||||||
{#if !$user}
|
{#if $ndk?.signer === undefined}
|
||||||
<div class="login"><button class="btn btn-primary" onclick={() => login()}>Login</button></div>
|
<div class="login">
|
||||||
|
<button class="btn btn-primary" onclick={login}>Login</button>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if !$questionId}
|
{#if !$questionId}
|
||||||
|
|
@ -84,7 +64,9 @@
|
||||||
{#if $connected}
|
{#if $connected}
|
||||||
<div class="join">
|
<div class="join">
|
||||||
<input type="number" class="border p-2" placeholder="12345678" bind:value={sessionId} />
|
<input type="number" class="border p-2" placeholder="12345678" bind:value={sessionId} />
|
||||||
<button class="btn btn-primary rounded" onclick={() => joinSession()}> Teilnehmen </button>
|
<button class="btn btn-primary rounded" onclick={() => joinSession()}>
|
||||||
|
Teilnehmen
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -93,23 +75,36 @@
|
||||||
<div class="mx-auto p-4">
|
<div class="mx-auto p-4">
|
||||||
<h1 class="mb-4 text-2xl font-bold">Eine Ideensammlung starten</h1>
|
<h1 class="mb-4 text-2xl font-bold">Eine Ideensammlung starten</h1>
|
||||||
{#if $connected}
|
{#if $connected}
|
||||||
{#key $questionId}
|
{#key $questionId}
|
||||||
{#if $questionId === ''}
|
{#if $questionId === ''}
|
||||||
<div class="flex flex-col justify-center items-center">
|
<div class="flex flex-col items-center justify-center">
|
||||||
<MarkdownEditor bind:value={$question} {carta} />
|
<MarkdownEditor bind:value={$question} {carta} />
|
||||||
<div><button class="btn btn-primary rounded mt-5" onclick={postQuestion}> Starten </button></div>
|
<div>
|
||||||
</div>
|
<button class="btn btn-primary mt-5 rounded" onclick={postQuestion}> Starten </button>
|
||||||
{:else}
|
</div>
|
||||||
<div class="qr-share mt-4">
|
</div>
|
||||||
<h2 class="text-xl font-bold">Diese Sammlung teilen</h2>
|
{:else}
|
||||||
<h3 class="text-center short-id">{questionShortId}</h3>
|
<div class="qr-share mt-4">
|
||||||
<div class="flex justify-center"><Confetti amount="600" size="15" cone x={[-4.5, 4.5]} y={[-1.5, 1.5]} delay={[0, 1000]} /></div>
|
<h2 class="text-xl font-bold">Diese Sammlung teilen</h2>
|
||||||
<img src={$qrCodeUrl} alt="QR Code" class="mt-2" />
|
<h3 class="short-id text-center">{questionShortId}</h3>
|
||||||
<p class="mb-1 text-center">Diesen QR-Code oder Link teilen:</p>
|
<div class="flex justify-center">
|
||||||
<p class="text-center mb-2 text-xl">
|
<Confetti
|
||||||
<a href={`/q/${$questionId}`}>{`${window.location.origin}/q/`}<span class="font-bold">{questionShortId}</span></a>
|
amount="600"
|
||||||
</p>
|
size="15"
|
||||||
<!--
|
cone
|
||||||
|
x={[-4.5, 4.5]}
|
||||||
|
y={[-1.5, 1.5]}
|
||||||
|
delay={[0, 1000]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<img src={$qrCodeUrl} alt="QR Code" class="mt-2" />
|
||||||
|
<p class="mb-1 text-center">Diesen QR-Code oder Link teilen:</p>
|
||||||
|
<p class="mb-2 text-center text-xl">
|
||||||
|
<a href={`/q/${$questionId}`}
|
||||||
|
>{`${window.location.origin}/q/`}<span class="font-bold">{questionShortId}</span></a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<!--
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<label for="timer" class="mb-2 block">Set Timer (seconds):</label>
|
<label for="timer" class="mb-2 block">Set Timer (seconds):</label>
|
||||||
<input type="number" id="timer" class="rounded border p-2" bind:value={timer} />
|
<input type="number" id="timer" class="rounded border p-2" bind:value={timer} />
|
||||||
|
|
@ -118,48 +113,48 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
-->
|
-->
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if votingEnabled}
|
{#if votingEnabled}
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<h2 class="text-xl font-bold">Abstimmung ist jetzt aktiv!</h2>
|
<h2 class="text-xl font-bold">Abstimmung ist jetzt aktiv!</h2>
|
||||||
<p>Es können jetzt Stimmen abgegeben werden.</p>
|
<p>Es können jetzt Stimmen abgegeben werden.</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/key}
|
{/key}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
:global(.carta-input) {
|
:global(.carta-input) {
|
||||||
height: 150px !important;
|
height: 150px !important;
|
||||||
|
}
|
||||||
|
:global(.carta-editor) {
|
||||||
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
:global(.carta-editor) {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
input[type='number']::-webkit-inner-spin-button,
|
input[type='number']::-webkit-inner-spin-button,
|
||||||
input[type='number']::-webkit-outer-spin-button {
|
input[type='number']::-webkit-outer-spin-button {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
.short-id {
|
.short-id {
|
||||||
font-size: 60px;
|
font-size: 60px;
|
||||||
}
|
}
|
||||||
.qr-share img {
|
.qr-share img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 3px solid #eee;
|
border: 3px solid #eee;
|
||||||
}
|
}
|
||||||
.login {
|
.login {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
.logo {
|
.logo {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
.main-layout {
|
.main-layout {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import { Confetti } from 'svelte-confetti';
|
import { onDestroy } from 'svelte';
|
||||||
|
|
||||||
/** @type {import('./$types').PageProps} */
|
/** @type {import('./$types').PageProps} */
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
import Comment from '$lib/components/Comment.svelte';
|
import Comment from '$lib/components/Comment.svelte';
|
||||||
import { ndk, ndkReady, user } from '$lib/stores';
|
import { ndk, user } from '$lib/stores';
|
||||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
||||||
import { writable } from 'svelte/store';
|
import { derived, writable } from 'svelte/store';
|
||||||
import { Carta, Markdown, MarkdownEditor } from 'carta-md';
|
import { Carta, Markdown, MarkdownEditor } from 'carta-md';
|
||||||
import 'carta-md/default.css'; /* Default theme */
|
import 'carta-md/default.css'; /* Default theme */
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
let carta = new Carta({
|
let carta = new Carta({
|
||||||
sanitizer: DOMPurify.sanitize
|
sanitizer: DOMPurify.sanitize
|
||||||
});
|
});
|
||||||
|
|
||||||
function submitComment() {
|
function submitComment() {
|
||||||
if (!$comment) {
|
if (!$comment) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -24,61 +25,71 @@
|
||||||
const commentEvent = new NDKEvent($ndk, {
|
const commentEvent = new NDKEvent($ndk, {
|
||||||
kind: 2222,
|
kind: 2222,
|
||||||
content: $comment,
|
content: $comment,
|
||||||
tags: [['E', data.id]]
|
tags: [['A', data.id]]
|
||||||
});
|
});
|
||||||
commentEvent.publish();
|
commentEvent.publish();
|
||||||
comment.set('');
|
comment.set('');
|
||||||
setTimeout(() => submit.set(false), 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters an array of objects to keep only unique objects based on their id property
|
|
||||||
* @param {Array} array - The array of objects to filter
|
|
||||||
* @returns {Array} - A new array containing only unique objects by id
|
|
||||||
*/
|
|
||||||
function getUniqueById(array) {
|
|
||||||
// Create a Map to track unique objects by their id
|
|
||||||
const uniqueMap = new Map();
|
|
||||||
|
|
||||||
// Loop through the array and add each object to the map with its id as the key
|
|
||||||
// This automatically overrides any previous entries with the same id
|
|
||||||
array.forEach((item) => {
|
|
||||||
if (item && item.id !== undefined) {
|
|
||||||
uniqueMap.set(item.id, item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Convert the Map values back to an array
|
|
||||||
return Array.from(uniqueMap.values());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let comment = writable('');
|
let comment = writable('');
|
||||||
let submit = writable(false);
|
let submit = writable(false);
|
||||||
let comments = writable([]);
|
|
||||||
let question = writable();
|
|
||||||
const [kind, pubkey, d] = data.id.split(':');
|
const [kind, pubkey, d] = data.id.split(':');
|
||||||
const showReactions = writable(false);
|
const showReactions = writable('false');
|
||||||
|
const comments = writable([]);
|
||||||
|
|
||||||
|
let commentStore = $ndk.storeSubscribe(
|
||||||
|
{ kinds: [2222], '#A': [data.id] },
|
||||||
|
{
|
||||||
|
onEvent: (event) => {
|
||||||
|
console.log('Event received', event);
|
||||||
|
$comments = [event, ...$comments];
|
||||||
|
},
|
||||||
|
onEose: () => console.log('Subscription EOSE reached'),
|
||||||
|
closeOnEose: false,
|
||||||
|
autoStart: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
commentStore.ref();
|
||||||
|
|
||||||
|
let questionStore = $ndk.storeSubscribe({ kinds: [Number(kind)], authors: [pubkey], '#d': [d] });
|
||||||
|
questionStore.ref();
|
||||||
|
|
||||||
|
let question = derived(
|
||||||
|
[questionStore],
|
||||||
|
([$questionStore]) => {
|
||||||
|
if ($questionStore.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$questionStore.sort((a, b) => {
|
||||||
|
return b.created_at - a.created_at;
|
||||||
|
});
|
||||||
|
const reactionTag = $questionStore[0].tags.find((t) => t[0] === 'reactions');
|
||||||
|
if (reactionTag && reactionTag[1] === 'true') {
|
||||||
|
console.log('reactions enabled');
|
||||||
|
$showReactions = 'true';
|
||||||
|
}
|
||||||
|
console.log('question', $questionStore[0]);
|
||||||
|
return $questionStore[0];
|
||||||
|
},
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
commentStore.unsubscribe();
|
||||||
|
questionStore.unsubscribe();
|
||||||
|
});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if ($ndkReady) {
|
if ($ndk.signer) {
|
||||||
console.log('ndk ready');
|
console.log('got a signer');
|
||||||
const sub = $ndk.subscribe({ kinds: [2222], '#E': [data.id] });
|
console.log('start subscription');
|
||||||
sub.on('event', (event) => {
|
commentStore.startSubscription();
|
||||||
console.log(event);
|
|
||||||
const unique = getUniqueById([...$comments, event]);
|
|
||||||
$comments = [...unique];
|
|
||||||
console.log(`${event.content}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
const questionSub = $ndk.subscribe({ kinds: [Number(kind)], authors: [pubkey], '#d': [d] });
|
questionStore.startSubscription();
|
||||||
questionSub.on('event', (event) => {
|
console.log('comment store', $commentStore);
|
||||||
$question = event;
|
|
||||||
if ($question?.tags.find((t) => t[0] === 'reactions')[1] === 'true') {
|
|
||||||
console.log('reactions enabled');
|
|
||||||
$showReactions = 'true';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
console.log('user', $user);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function startReactions(event) {
|
async function startReactions(event) {
|
||||||
|
|
@ -93,35 +104,34 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="main-layout mx-auto flex w-3/4 flex-col items-center justify-center">
|
<div class="main-layout mx-auto flex w-3/4 flex-col items-center justify-center">
|
||||||
{#if $question}
|
{#key $question?.pubkey}
|
||||||
{#if $question.pubkey === $user.pubkey && $showReactions === false}
|
{#if $question}
|
||||||
<button class="btn btn-primary mb-4" onclick={() => startReactions($question)}
|
{#if $user && $question?.pubkey === $user?.pubkey && $showReactions === "false"}
|
||||||
>Reaktionen/Voting aktivieren</button
|
<button class="btn btn-primary mb-4" onclick={() => startReactions($question)}
|
||||||
>
|
>Reaktionen/Voting aktivieren</button
|
||||||
{/if}
|
>
|
||||||
<div class="question mb-4 w-full rounded border p-4 text-xl">
|
|
||||||
<h2 class="text-xl font-bold">Frage / Thema:</h2>
|
|
||||||
<Markdown {carta} value={$question.content} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-2 flex w-full flex-col items-center justify-center gap-2">
|
|
||||||
<h1 class="pt-15 text-xl font-bold">Meine Idee hinzufügen</h1>
|
|
||||||
<MarkdownEditor bind:value={$comment} {carta} />
|
|
||||||
<button class="btn btn-primary mt-5 mb-10" onclick={() => submitComment()}>Hinzufügen</button>
|
|
||||||
{#if $submit === true}
|
|
||||||
<div class="flex justify-center"><Confetti amount="50"} /></div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
<div class="question mb-4 w-full rounded border p-4 text-xl">
|
||||||
{:else}
|
<h2 class="text-xl font-bold">Frage / Thema:</h2>
|
||||||
<p>Loading...</p>
|
<Markdown {carta} value={$question.content} />
|
||||||
{/if}
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-2 flex w-full flex-col items-center justify-center gap-2">
|
||||||
|
<h1 class="pt-15 text-xl font-bold">Meine Idee hinzufügen</h1>
|
||||||
|
<MarkdownEditor bind:value={$comment} {carta} />
|
||||||
|
<button class="btn btn-primary mt-5 mb-10" onclick={() => submitComment()}
|
||||||
|
>Hinzufügen</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<p>Loading...</p>
|
||||||
|
{/if}
|
||||||
|
{/key}
|
||||||
|
|
||||||
<div class="mx-auto flex w-full flex-col items-center justify-center gap-5">
|
<div class="mx-auto flex w-full flex-col items-center justify-center gap-5">
|
||||||
{#key $showReactions}
|
{#each $commentStore as event}
|
||||||
{#each $comments.sort((a, b) => b.created_at - a.created_at) as event}
|
<Comment {event} showReactions={$showReactions} />
|
||||||
<Comment event={event} showReactions={$showReactions} />
|
{/each}
|
||||||
{/each}
|
|
||||||
{/key}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue