Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ced5689c3 | |||
| 263a803875 | |||
| 58afb8fa0c | |||
| 4aaa19ea1b | |||
| 2f9010cd13 | |||
| 12fcdfcd4f | |||
| 317ab57ed2 | |||
| 52ef67740a |
@@ -7,8 +7,11 @@ VITE_PLATFORM_NAME=Flotilla
|
|||||||
VITE_PLATFORM_LOGO=static/flotilla.png
|
VITE_PLATFORM_LOGO=static/flotilla.png
|
||||||
VITE_PLATFORM_RELAY=
|
VITE_PLATFORM_RELAY=
|
||||||
VITE_PLATFORM_ACCENT="#7161FF"
|
VITE_PLATFORM_ACCENT="#7161FF"
|
||||||
|
VITE_PLATFORM_SECONDARY="#EB5E28"
|
||||||
VITE_PLATFORM_DESCRIPTION="Flotilla is nostr — for communities."
|
VITE_PLATFORM_DESCRIPTION="Flotilla is nostr — for communities."
|
||||||
VITE_INDEXER_RELAYS=wss://purplepag.es/,wss://relay.damus.io/,wss://relay.nostr.band/
|
VITE_INDEXER_RELAYS=wss://purplepag.es/,wss://relay.damus.io/,wss://relay.nostr.band/
|
||||||
VITE_SIGNER_RELAYS=wss://relay.nsec.app/,wss://bucket.coracle.social/
|
VITE_SIGNER_RELAYS=wss://relay.nsec.app/,wss://bucket.coracle.social/
|
||||||
|
VITE_NOTIFIER_PUBKEY=27b7c2ed89ef78322114225ea3ebf5f72c7767c2528d4d0c1854d039c00085df
|
||||||
|
VITE_NOTIFIER_RELAY=wss://anchor.coracle.social/
|
||||||
VITE_GLITCHTIP_API_KEY=
|
VITE_GLITCHTIP_API_KEY=
|
||||||
GLITCHTIP_AUTH_TOKEN=
|
GLITCHTIP_AUTH_TOKEN=
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# Env
|
# Env
|
||||||
.env.local
|
.env
|
||||||
|
|
||||||
# Vite
|
# Vite
|
||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
# 1.0.3
|
||||||
|
|
||||||
|
* Add light theme
|
||||||
|
* Use correct alerts server
|
||||||
|
* Ignore relay errors for claims
|
||||||
|
* Fix inline code blocks
|
||||||
|
* Add custom emoji parsing and display
|
||||||
|
|
||||||
# 1.0.2
|
# 1.0.2
|
||||||
|
|
||||||
* Fix add relay button
|
* Fix add relay button
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ To run your own Flotilla, it's as simple as:
|
|||||||
|
|
||||||
## Environment
|
## Environment
|
||||||
|
|
||||||
You can also optionally create an `.env.local` file and populate it with the following environment variables (see `.env` for examples):
|
You can also optionally create an `.env` file and populate it with the following environment variables (see `.env` for examples):
|
||||||
|
|
||||||
- `VITE_DEFAULT_PUBKEYS` - A comma-separated list of hex pubkeys for bootstrapping web of trust.
|
- `VITE_DEFAULT_PUBKEYS` - A comma-separated list of hex pubkeys for bootstrapping web of trust.
|
||||||
- `VITE_PLATFORM_URL` - The url where the app will be hosted. This is only used for build-time population of meta tags.
|
- `VITE_PLATFORM_URL` - The url where the app will be hosted. This is only used for build-time population of meta tags.
|
||||||
@@ -38,7 +38,7 @@ First, create an `A` record with your DNS provider pointing to the IP of your se
|
|||||||
|
|
||||||
Next install `nginx`, `git`, and `certbot`. If you're on a debian- or ubuntu-based distro, run `sudo apt-get update && sudo apt-get install nginx git certbot python3-certbot-nginx`.
|
Next install `nginx`, `git`, and `certbot`. If you're on a debian- or ubuntu-based distro, run `sudo apt-get update && sudo apt-get install nginx git certbot python3-certbot-nginx`.
|
||||||
|
|
||||||
Now, create a new user where your code will be stored, clone the repository, fill in your `.env.local` file, and build the app.
|
Now, create a new user where your code will be stored, clone the repository, fill in your `.env` file, and build the app.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Replace with your password
|
# Replace with your password
|
||||||
@@ -67,7 +67,7 @@ nvm install
|
|||||||
nvm use
|
nvm use
|
||||||
pnpm i
|
pnpm i
|
||||||
|
|
||||||
# Optionally create and populate .env.local to suit your use case
|
# Optionally create and populate .env to suit your use case
|
||||||
|
|
||||||
# Build the app
|
# Build the app
|
||||||
NODE_OPTIONS=--max_old_space_size=16384 pnpm run build
|
NODE_OPTIONS=--max_old_space_size=16384 pnpm run build
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ android {
|
|||||||
applicationId "social.flotilla"
|
applicationId "social.flotilla"
|
||||||
minSdk rootProject.ext.minSdkVersion
|
minSdk rootProject.ext.minSdkVersion
|
||||||
targetSdk rootProject.ext.targetSdkVersion
|
targetSdk rootProject.ext.targetSdkVersion
|
||||||
versionCode 16
|
versionCode 17
|
||||||
versionName "1.0.2"
|
versionName "1.0.3"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
temp_env=$(declare -p -x)
|
temp_env=$(declare -p -x)
|
||||||
|
|
||||||
if [ -f .env ]; then
|
if [ -f .env.template ]; then
|
||||||
source .env
|
source .env.template
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f .env.local ]; then
|
if [ -f .env ]; then
|
||||||
source .env.local
|
source .env
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Avoid overwriting env vars provided directly
|
# Avoid overwriting env vars provided directly
|
||||||
|
|||||||
@@ -358,7 +358,7 @@
|
|||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
|
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@@ -383,7 +383,7 @@
|
|||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
|
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
|||||||
+5
-5
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "flotilla",
|
"name": "flotilla",
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
@@ -51,10 +51,10 @@
|
|||||||
"@types/qrcode": "^1.5.5",
|
"@types/qrcode": "^1.5.5",
|
||||||
"@vite-pwa/assets-generator": "^0.2.6",
|
"@vite-pwa/assets-generator": "^0.2.6",
|
||||||
"@vite-pwa/sveltekit": "^0.6.6",
|
"@vite-pwa/sveltekit": "^0.6.6",
|
||||||
"@welshman/app": "^0.2.4",
|
"@welshman/app": "^0.2.5",
|
||||||
"@welshman/content": "^0.2.1",
|
"@welshman/content": "^0.2.2",
|
||||||
"@welshman/dvm": "^0.2.0",
|
"@welshman/dvm": "^0.2.0",
|
||||||
"@welshman/editor": "^0.2.1",
|
"@welshman/editor": "^0.2.4",
|
||||||
"@welshman/feeds": "^0.2.2",
|
"@welshman/feeds": "^0.2.2",
|
||||||
"@welshman/lib": "^0.2.2",
|
"@welshman/lib": "^0.2.2",
|
||||||
"@welshman/net": "^0.2.3",
|
"@welshman/net": "^0.2.3",
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
"@welshman/router": "^0.2.0",
|
"@welshman/router": "^0.2.0",
|
||||||
"@welshman/signer": "^0.2.3",
|
"@welshman/signer": "^0.2.3",
|
||||||
"@welshman/store": "^0.2.0",
|
"@welshman/store": "^0.2.0",
|
||||||
"@welshman/util": "^0.2.2",
|
"@welshman/util": "^0.2.3",
|
||||||
"daisyui": "^4.12.10",
|
"daisyui": "^4.12.10",
|
||||||
"date-picker-svelte": "^2.13.0",
|
"date-picker-svelte": "^2.13.0",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import dotenv from "dotenv"
|
import dotenv from "dotenv"
|
||||||
import {defineConfig, minimalPreset as preset} from "@vite-pwa/assets-generator/config"
|
import {defineConfig, minimalPreset as preset} from "@vite-pwa/assets-generator/config"
|
||||||
|
|
||||||
dotenv.config({path: ".env.local"})
|
|
||||||
dotenv.config({path: ".env"})
|
dotenv.config({path: ".env"})
|
||||||
|
dotenv.config({path: ".env.template"})
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
preset,
|
preset,
|
||||||
|
|||||||
+16
-10
@@ -46,6 +46,14 @@
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
font-family: Lato;
|
font-family: Lato;
|
||||||
|
--sait: env(safe-area-inset-top);
|
||||||
|
--saib: env(safe-area-inset-bottom);
|
||||||
|
--sail: env(safe-area-inset-left);
|
||||||
|
--sair: env(safe-area-inset-right);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme] {
|
||||||
|
@apply bg-base-300;
|
||||||
--base-100: oklch(var(--b1));
|
--base-100: oklch(var(--b1));
|
||||||
--base-200: oklch(var(--b2));
|
--base-200: oklch(var(--b2));
|
||||||
--base-300: oklch(var(--b3));
|
--base-300: oklch(var(--b3));
|
||||||
@@ -54,16 +62,6 @@
|
|||||||
--primary-content: oklch(var(--pc));
|
--primary-content: oklch(var(--pc));
|
||||||
--secondary: oklch(var(--s));
|
--secondary: oklch(var(--s));
|
||||||
--secondary-content: oklch(var(--sc));
|
--secondary-content: oklch(var(--sc));
|
||||||
--sait: env(safe-area-inset-top);
|
|
||||||
--saib: env(safe-area-inset-bottom);
|
|
||||||
--sail: env(safe-area-inset-left);
|
|
||||||
--sair: env(safe-area-inset-right);
|
|
||||||
}
|
|
||||||
|
|
||||||
:root,
|
|
||||||
body,
|
|
||||||
html {
|
|
||||||
@apply bg-base-300;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* safe area insets */
|
/* safe area insets */
|
||||||
@@ -293,6 +291,14 @@ html {
|
|||||||
--tiptap-active-fg: var(--base-content);
|
--tiptap-active-fg: var(--base-content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tiptap-suggestions__item {
|
||||||
|
@apply border-l-2 border-solid border-base-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiptap-suggestions__selected {
|
||||||
|
@apply border-primary;
|
||||||
|
}
|
||||||
|
|
||||||
.tiptap {
|
.tiptap {
|
||||||
@apply max-h-[350px] overflow-y-auto p-2 px-4;
|
@apply max-h-[350px] overflow-y-auto p-2 px-4;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-5
@@ -276,9 +276,12 @@ export const checkRelayAccess = async (url: string, claim = "") => {
|
|||||||
|
|
||||||
// If it's a strict NIP 29 relay don't worry about requesting access
|
// If it's a strict NIP 29 relay don't worry about requesting access
|
||||||
// TODO: remove this if relay29 ever gets less strict
|
// TODO: remove this if relay29 ever gets less strict
|
||||||
if (message !== "missing group (`h`) tag") {
|
if (message === "missing group (`h`) tag") return
|
||||||
return message
|
|
||||||
}
|
// Ignore messages about the relay ignoring ours
|
||||||
|
if (error?.startsWith("mute: ")) return
|
||||||
|
|
||||||
|
return message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,10 +380,12 @@ export const publishReport = ({
|
|||||||
export type ReactionParams = {
|
export type ReactionParams = {
|
||||||
event: TrustedEvent
|
event: TrustedEvent
|
||||||
content: string
|
content: string
|
||||||
|
tags?: string[][]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const makeReaction = ({event, content}: ReactionParams) => {
|
export const makeReaction = ({content, event, tags: paramTags = []}: ReactionParams) => {
|
||||||
const tags = tagEventForReaction(event)
|
const tags = [...paramTags, ...tagEventForReaction(event)]
|
||||||
|
|
||||||
const groupTag = getTag("h", event.tags)
|
const groupTag = getTag("h", event.tags)
|
||||||
|
|
||||||
if (groupTag) {
|
if (groupTag) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||||
import {pubkey} from "@welshman/app"
|
import {pubkey} from "@welshman/app"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
@@ -26,20 +26,15 @@
|
|||||||
|
|
||||||
const editEvent = () => pushModal(CalendarEventEdit, {url, event})
|
const editEvent = () => pushModal(CalendarEventEdit, {url, event})
|
||||||
|
|
||||||
const onReactionClick = (content: string, events: TrustedEvent[]) => {
|
const deleteReaction = (event: TrustedEvent) => publishDelete({relays: [url], event})
|
||||||
const reaction = events.find(e => e.pubkey === $pubkey)
|
|
||||||
|
|
||||||
if (reaction) {
|
const createReaction = (template: EventContent) =>
|
||||||
publishDelete({relays: [url], event: reaction})
|
publishReaction({...template, event, relays: [url]})
|
||||||
} else {
|
|
||||||
publishReaction({event, content, relays: [url]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center justify-between gap-2">
|
<div class="flex flex-wrap items-center justify-between gap-2">
|
||||||
<div class="flex flex-grow flex-wrap justify-end gap-2">
|
<div class="flex flex-grow flex-wrap justify-end gap-2">
|
||||||
<ReactionSummary {url} {event} {onReactionClick} reactionClass="tooltip-left" />
|
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tooltip-left" />
|
||||||
<ThunkStatusOrDeleted {event} />
|
<ThunkStatusOrDeleted {event} />
|
||||||
{#if showActivity}
|
{#if showActivity}
|
||||||
<EventActivity {url} {path} {event} />
|
<EventActivity {url} {path} {event} />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {hash, now, formatTimestampAsTime, formatTimestampAsDate} from "@welshman/lib"
|
import {hash, now, formatTimestampAsTime, formatTimestampAsDate} from "@welshman/lib"
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||||
import {thunks, pubkey, deriveProfile, deriveProfileDisplay} from "@welshman/app"
|
import {thunks, deriveProfile, deriveProfileDisplay} from "@welshman/app"
|
||||||
import {isMobile} from "@lib/html"
|
import {isMobile} from "@lib/html"
|
||||||
import TapTarget from "@lib/components/TapTarget.svelte"
|
import TapTarget from "@lib/components/TapTarget.svelte"
|
||||||
import Avatar from "@lib/components/Avatar.svelte"
|
import Avatar from "@lib/components/Avatar.svelte"
|
||||||
@@ -41,15 +41,10 @@
|
|||||||
|
|
||||||
const openProfile = () => pushModal(ProfileDetail, {pubkey: event.pubkey, url})
|
const openProfile = () => pushModal(ProfileDetail, {pubkey: event.pubkey, url})
|
||||||
|
|
||||||
const onReactionClick = (content: string, events: TrustedEvent[]) => {
|
const deleteReaction = (event: TrustedEvent) => publishDelete({relays: [url], event})
|
||||||
const reaction = events.find(e => e.pubkey === $pubkey)
|
|
||||||
|
|
||||||
if (reaction) {
|
const createReaction = (template: EventContent) =>
|
||||||
publishDelete({relays: [url], event: reaction})
|
publishReaction({...template, event, relays: [url]})
|
||||||
} else {
|
|
||||||
publishReaction({event, content, relays: [url]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<TapTarget
|
<TapTarget
|
||||||
@@ -89,7 +84,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-2 ml-10 mt-1">
|
<div class="row-2 ml-10 mt-1">
|
||||||
<ReactionSummary {url} {event} {onReactionClick} reactionClass="tooltip-right" />
|
<ReactionSummary
|
||||||
|
{url}
|
||||||
|
{event}
|
||||||
|
{deleteReaction}
|
||||||
|
{createReaction}
|
||||||
|
reactionClass="tooltip-right" />
|
||||||
</div>
|
</div>
|
||||||
{#if !isMobile}
|
{#if !isMobile}
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {type Instance} from "tippy.js"
|
import {type Instance} from "tippy.js"
|
||||||
import {hash, formatTimestampAsTime} from "@welshman/lib"
|
import {hash, formatTimestampAsTime} from "@welshman/lib"
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||||
import {thunks, pubkey, deriveProfile, deriveProfileDisplay, sendWrapped} from "@welshman/app"
|
import {thunks, pubkey, deriveProfile, deriveProfileDisplay, sendWrapped} from "@welshman/app"
|
||||||
import {isMobile} from "@lib/html"
|
import {isMobile} from "@lib/html"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
@@ -36,12 +36,11 @@
|
|||||||
|
|
||||||
const reply = () => replyTo(event)
|
const reply = () => replyTo(event)
|
||||||
|
|
||||||
const onReactionClick = async (content: string, events: TrustedEvent[]) => {
|
const deleteReaction = (event: TrustedEvent) =>
|
||||||
const reaction = events.find(e => e.pubkey === $pubkey)
|
sendWrapped({template: makeDelete({event}), pubkeys})
|
||||||
const template = reaction ? makeDelete({event: reaction}) : makeReaction({event, content})
|
|
||||||
|
|
||||||
await sendWrapped({template, pubkeys})
|
const createReaction = (template: EventContent) =>
|
||||||
}
|
sendWrapped({template: makeReaction({event, ...template}), pubkeys})
|
||||||
|
|
||||||
const openProfile = () => pushModal(ProfileDetail, {pubkey: event.pubkey})
|
const openProfile = () => pushModal(ProfileDetail, {pubkey: event.pubkey})
|
||||||
|
|
||||||
@@ -120,7 +119,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</TapTarget>
|
</TapTarget>
|
||||||
<div class="row-2 z-feature -mt-4 ml-4">
|
<div class="row-2 z-feature -mt-4 ml-4">
|
||||||
<ReactionSummary {event} {onReactionClick} noTooltip />
|
<ReactionSummary {event} {deleteReaction} {createReaction} noTooltip />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
truncate,
|
truncate,
|
||||||
renderAsHtml,
|
renderAsHtml,
|
||||||
isText,
|
isText,
|
||||||
|
isEmoji,
|
||||||
isTopic,
|
isTopic,
|
||||||
isCode,
|
isCode,
|
||||||
isCashu,
|
isCashu,
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import ContentToken from "@app/components/ContentToken.svelte"
|
import ContentToken from "@app/components/ContentToken.svelte"
|
||||||
|
import ContentEmoji from "@app/components/ContentEmoji.svelte"
|
||||||
import ContentCode from "@app/components/ContentCode.svelte"
|
import ContentCode from "@app/components/ContentCode.svelte"
|
||||||
import ContentLinkInline from "@app/components/ContentLinkInline.svelte"
|
import ContentLinkInline from "@app/components/ContentLinkInline.svelte"
|
||||||
import ContentLinkBlock from "@app/components/ContentLinkBlock.svelte"
|
import ContentLinkBlock from "@app/components/ContentLinkBlock.svelte"
|
||||||
@@ -133,6 +135,8 @@
|
|||||||
<ContentNewline value={parsed.value} />
|
<ContentNewline value={parsed.value} />
|
||||||
{:else if isTopic(parsed)}
|
{:else if isTopic(parsed)}
|
||||||
<ContentTopic value={parsed.value} />
|
<ContentTopic value={parsed.value} />
|
||||||
|
{:else if isEmoji(parsed)}
|
||||||
|
<ContentEmoji value={parsed.value} />
|
||||||
{:else if isCode(parsed)}
|
{:else if isCode(parsed)}
|
||||||
<ContentCode
|
<ContentCode
|
||||||
value={parsed.value}
|
value={parsed.value}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type {ParsedEmojiValue} from "@welshman/content"
|
||||||
|
import {imgproxy} from "@app/state"
|
||||||
|
|
||||||
|
export let value: ParsedEmojiValue
|
||||||
|
|
||||||
|
const alt = `:${value.name}:`
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if value.url}
|
||||||
|
<img
|
||||||
|
{alt}
|
||||||
|
src={imgproxy(value.url, {w: 24, h: 24})}
|
||||||
|
class="-mt-0.5 inline h-[1em] min-w-[1em] align-middle" />
|
||||||
|
{:else}
|
||||||
|
{alt}
|
||||||
|
{/if}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {NativeEmoji} from "emoji-picker-element/shared"
|
import type {NativeEmoji} from "emoji-picker-element/shared"
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||||
import {pubkey} from "@welshman/app"
|
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import EmojiButton from "@lib/components/EmojiButton.svelte"
|
import EmojiButton from "@lib/components/EmojiButton.svelte"
|
||||||
import NoteContent from "@app/components/NoteContent.svelte"
|
import NoteContent from "@app/components/NoteContent.svelte"
|
||||||
@@ -11,15 +10,10 @@
|
|||||||
|
|
||||||
const {url, event} = $props()
|
const {url, event} = $props()
|
||||||
|
|
||||||
const onReactionClick = (content: string, events: TrustedEvent[]) => {
|
const deleteReaction = (event: TrustedEvent) => publishDelete({relays: [url], event})
|
||||||
const reaction = events.find(e => e.pubkey === $pubkey)
|
|
||||||
|
|
||||||
if (reaction) {
|
const createReaction = (template: EventContent) =>
|
||||||
publishDelete({relays: [url], event: reaction})
|
publishReaction({...template, event, relays: [url]})
|
||||||
} else {
|
|
||||||
publishReaction({event, content, relays: [url]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const onEmoji = (emoji: NativeEmoji) =>
|
const onEmoji = (emoji: NativeEmoji) =>
|
||||||
publishReaction({event, content: emoji.unicode, relays: [url]})
|
publishReaction({event, content: emoji.unicode, relays: [url]})
|
||||||
@@ -28,7 +22,7 @@
|
|||||||
<NoteCard {event} {url} class="card2 bg-alt">
|
<NoteCard {event} {url} class="card2 bg-alt">
|
||||||
<NoteContent {event} expandMode="inline" />
|
<NoteContent {event} expandMode="inline" />
|
||||||
<div class="flex w-full justify-between gap-2">
|
<div class="flex w-full justify-between gap-2">
|
||||||
<ReactionSummary {url} {event} {onReactionClick} reactionClass="tooltip-right">
|
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tooltip-right">
|
||||||
<EmojiButton {onEmoji} class="btn btn-neutral btn-xs h-[26px] rounded-box">
|
<EmojiButton {onEmoji} class="btn btn-neutral btn-xs h-[26px] rounded-box">
|
||||||
<Icon icon="smile-circle" size={4} />
|
<Icon icon="smile-circle" size={4} />
|
||||||
</EmojiButton>
|
</EmojiButton>
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {parse, isEmoji, renderAsHtml} from "@welshman/content"
|
||||||
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
|
import ContentEmoji from "@app/components/ContentEmoji.svelte"
|
||||||
|
|
||||||
|
export let event
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if event.content === "+" || event.content === ""}
|
||||||
|
<Icon icon="heart" />
|
||||||
|
{:else if event.content === "-"}
|
||||||
|
<Icon icon="thumbs-down" />
|
||||||
|
{:else}
|
||||||
|
{#each parse(event) as parsed}
|
||||||
|
{#if isEmoji(parsed)}
|
||||||
|
<ContentEmoji value={parsed.value} />
|
||||||
|
{:else}
|
||||||
|
{@html renderAsHtml(parsed)}
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
@@ -2,20 +2,29 @@
|
|||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
import type {Snippet} from "svelte"
|
import type {Snippet} from "svelte"
|
||||||
import {groupBy, uniq, uniqBy, batch, displayList} from "@welshman/lib"
|
import {groupBy, uniq, uniqBy, batch, displayList} from "@welshman/lib"
|
||||||
import {REACTION, getReplyFilters, getTag, REPORT, DELETE} from "@welshman/util"
|
import {
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
REACTION,
|
||||||
|
getReplyFilters,
|
||||||
|
getEmojiTags,
|
||||||
|
getEmojiTag,
|
||||||
|
getTag,
|
||||||
|
REPORT,
|
||||||
|
DELETE,
|
||||||
|
} from "@welshman/util"
|
||||||
|
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||||
import {deriveEvents} from "@welshman/store"
|
import {deriveEvents} from "@welshman/store"
|
||||||
import {load} from "@welshman/net"
|
import {load} from "@welshman/net"
|
||||||
import {pubkey, repository, displayProfileByPubkey} from "@welshman/app"
|
import {pubkey, repository, displayProfileByPubkey} from "@welshman/app"
|
||||||
import {isMobile, preventDefault, stopPropagation} from "@lib/html"
|
import {isMobile, preventDefault, stopPropagation} from "@lib/html"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
|
import Reaction from "@app/components/Reaction.svelte"
|
||||||
import EventReportDetails from "@app/components/EventReportDetails.svelte"
|
import EventReportDetails from "@app/components/EventReportDetails.svelte"
|
||||||
import {displayReaction} from "@app/state"
|
|
||||||
import {pushModal} from "@app/modal"
|
import {pushModal} from "@app/modal"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
event: any
|
event: TrustedEvent
|
||||||
onReactionClick: any
|
deleteReaction: (event: TrustedEvent) => void
|
||||||
|
createReaction: (event: EventContent) => void
|
||||||
url?: string
|
url?: string
|
||||||
reactionClass?: string
|
reactionClass?: string
|
||||||
noTooltip?: boolean
|
noTooltip?: boolean
|
||||||
@@ -24,7 +33,8 @@
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
event,
|
event,
|
||||||
onReactionClick,
|
deleteReaction,
|
||||||
|
createReaction,
|
||||||
url = "",
|
url = "",
|
||||||
reactionClass = "",
|
reactionClass = "",
|
||||||
noTooltip = false,
|
noTooltip = false,
|
||||||
@@ -39,14 +49,31 @@
|
|||||||
filters: [{kinds: [REACTION], "#e": [event.id]}],
|
filters: [{kinds: [REACTION], "#e": [event.id]}],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const onReactionClick = (events: TrustedEvent[]) => {
|
||||||
|
const reaction = events.find(e => e.pubkey === $pubkey)
|
||||||
|
|
||||||
|
if (reaction) {
|
||||||
|
deleteReaction(reaction)
|
||||||
|
} else {
|
||||||
|
const [event] = events
|
||||||
|
|
||||||
|
createReaction({
|
||||||
|
content: event.content,
|
||||||
|
tags: getEmojiTags(event.content.replace(/:/g, ""), event.tags),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onReportClick = () => pushModal(EventReportDetails, {url, event})
|
const onReportClick = () => pushModal(EventReportDetails, {url, event})
|
||||||
|
|
||||||
const reportReasons = $derived(uniq($reports.map(e => getTag("e", e.tags)?.[2])))
|
const reportReasons = $derived(uniq($reports.map(e => getTag("e", e.tags)?.[2])))
|
||||||
|
|
||||||
|
const getReactionKey = (e: TrustedEvent) => getEmojiTag(e.content, e.tags)?.join("") || e.content
|
||||||
|
|
||||||
const groupedReactions = $derived(
|
const groupedReactions = $derived(
|
||||||
groupBy(
|
groupBy(
|
||||||
e => e.content,
|
getReactionKey,
|
||||||
uniqBy(e => e.pubkey + e.content, $reactions),
|
uniqBy(e => `${e.pubkey}${getReactionKey(e)}`, $reactions),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,12 +113,12 @@
|
|||||||
<span>{$reports.length}</span>
|
<span>{$reports.length}</span>
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{#each groupedReactions.entries() as [content, events]}
|
{#each groupedReactions.entries() as [key, events]}
|
||||||
{@const pubkeys = events.map(e => e.pubkey)}
|
{@const pubkeys = events.map(e => e.pubkey)}
|
||||||
{@const isOwn = $pubkey && pubkeys.includes($pubkey)}
|
{@const isOwn = $pubkey && pubkeys.includes($pubkey)}
|
||||||
{@const info = displayList(pubkeys.map(pubkey => displayProfileByPubkey(pubkey)))}
|
{@const info = displayList(pubkeys.map(pubkey => displayProfileByPubkey(pubkey)))}
|
||||||
{@const tooltip = `${info} reacted ${displayReaction(content)}`}
|
{@const tooltip = `${info} reacted`}
|
||||||
{@const onClick = () => onReactionClick(content, events)}
|
{@const onClick = () => onReactionClick(events)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-tip={tooltip}
|
data-tip={tooltip}
|
||||||
@@ -101,7 +128,7 @@
|
|||||||
class:border-solid={isOwn}
|
class:border-solid={isOwn}
|
||||||
class:border-primary={isOwn}
|
class:border-primary={isOwn}
|
||||||
onclick={stopPropagation(preventDefault(onClick))}>
|
onclick={stopPropagation(preventDefault(onClick))}>
|
||||||
<span>{displayReaction(content)}</span>
|
<Reaction event={events[0]} />
|
||||||
{#if events.length > 1}
|
{#if events.length > 1}
|
||||||
<span>{events.length}</span>
|
<span>{events.length}</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||||
import {pubkey} from "@welshman/app"
|
|
||||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||||
import ThunkStatusOrDeleted from "@app/components/ThunkStatusOrDeleted.svelte"
|
import ThunkStatusOrDeleted from "@app/components/ThunkStatusOrDeleted.svelte"
|
||||||
import EventActivity from "@app/components/EventActivity.svelte"
|
import EventActivity from "@app/components/EventActivity.svelte"
|
||||||
@@ -18,20 +17,15 @@
|
|||||||
|
|
||||||
const path = makeThreadPath(url, event.id)
|
const path = makeThreadPath(url, event.id)
|
||||||
|
|
||||||
const onReactionClick = (content: string, events: TrustedEvent[]) => {
|
const deleteReaction = (event: TrustedEvent) => publishDelete({relays: [url], event})
|
||||||
const reaction = events.find(e => e.pubkey === $pubkey)
|
|
||||||
|
|
||||||
if (reaction) {
|
const createReaction = (template: EventContent) =>
|
||||||
publishDelete({relays: [url], event: reaction})
|
publishReaction({...template, event, relays: [url]})
|
||||||
} else {
|
|
||||||
publishReaction({event, content, relays: [url]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center justify-between gap-2">
|
<div class="flex flex-wrap items-center justify-between gap-2">
|
||||||
<div class="flex flex-grow flex-wrap justify-end gap-2">
|
<div class="flex flex-grow flex-wrap justify-end gap-2">
|
||||||
<ReactionSummary {url} {event} {onReactionClick} reactionClass="tooltip-left" />
|
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tooltip-left" />
|
||||||
<ThunkStatusOrDeleted {event} />
|
<ThunkStatusOrDeleted {event} />
|
||||||
{#if showActivity}
|
{#if showActivity}
|
||||||
<EventActivity {url} {path} {event} />
|
<EventActivity {url} {path} {event} />
|
||||||
|
|||||||
+2
-3
@@ -79,10 +79,9 @@ export const ALERT = 32830
|
|||||||
|
|
||||||
export const ALERT_STATUS = 32831
|
export const ALERT_STATUS = 32831
|
||||||
|
|
||||||
export const NOTIFIER_PUBKEY = "27b7c2ed89ef78322114225ea3ebf5f72c7767c2528d4d0c1854d039c00085df"
|
export const NOTIFIER_PUBKEY = import.meta.env.VITE_NOTIFIER_PUBKEY
|
||||||
|
|
||||||
// export const NOTIFIER_RELAY = 'wss://notifier.flotilla.social/'
|
export const NOTIFIER_RELAY = import.meta.env.VITE_NOTIFIER_RELAY
|
||||||
export const NOTIFIER_RELAY = "ws://localhost:4738/"
|
|
||||||
|
|
||||||
export const INDEXER_RELAYS = fromCsv(import.meta.env.VITE_INDEXER_RELAYS)
|
export const INDEXER_RELAYS = fromCsv(import.meta.env.VITE_INDEXER_RELAYS)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M21.0672 11.8568L20.4253 11.469L21.0672 11.8568ZM12.1432 2.93276L11.7553 2.29085V2.29085L12.1432 2.93276ZM21.25 12C21.25 17.1086 17.1086 21.25 12 21.25V22.75C17.9371 22.75 22.75 17.9371 22.75 12H21.25ZM12 21.25C6.89137 21.25 2.75 17.1086 2.75 12H1.25C1.25 17.9371 6.06294 22.75 12 22.75V21.25ZM2.75 12C2.75 6.89137 6.89137 2.75 12 2.75V1.25C6.06294 1.25 1.25 6.06294 1.25 12H2.75ZM15.5 14.25C12.3244 14.25 9.75 11.6756 9.75 8.5H8.25C8.25 12.5041 11.4959 15.75 15.5 15.75V14.25ZM20.4253 11.469C19.4172 13.1373 17.5882 14.25 15.5 14.25V15.75C18.1349 15.75 20.4407 14.3439 21.7092 12.2447L20.4253 11.469ZM9.75 8.5C9.75 6.41182 10.8627 4.5828 12.531 3.57467L11.7553 2.29085C9.65609 3.5593 8.25 5.86509 8.25 8.5H9.75ZM12 2.75C11.9115 2.75 11.8077 2.71008 11.7324 2.63168C11.6686 2.56527 11.6538 2.50244 11.6503 2.47703C11.6461 2.44587 11.6482 2.35557 11.7553 2.29085L12.531 3.57467C13.0342 3.27065 13.196 2.71398 13.1368 2.27627C13.0754 1.82126 12.7166 1.25 12 1.25V2.75ZM21.7092 12.2447C21.6444 12.3518 21.5541 12.3539 21.523 12.3497C21.4976 12.3462 21.4347 12.3314 21.3683 12.2676C21.2899 12.1923 21.25 12.0885 21.25 12H22.75C22.75 11.2834 22.1787 10.9246 21.7237 10.8632C21.286 10.804 20.7293 10.9658 20.4253 11.469L21.7092 12.2447Z" fill="#1C274C"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -59,6 +59,7 @@
|
|||||||
import MapPoint from "@assets/icons/Map Point.svg?dataurl"
|
import MapPoint from "@assets/icons/Map Point.svg?dataurl"
|
||||||
import MenuDots from "@assets/icons/Menu Dots.svg?dataurl"
|
import MenuDots from "@assets/icons/Menu Dots.svg?dataurl"
|
||||||
import MenuDotsCircle from "@assets/icons/Menu Dots Circle.svg?dataurl"
|
import MenuDotsCircle from "@assets/icons/Menu Dots Circle.svg?dataurl"
|
||||||
|
import Moon from "@assets/icons/Moon.svg?dataurl"
|
||||||
import NotesMinimalistic from "@assets/icons/Notes Minimalistic.svg?dataurl"
|
import NotesMinimalistic from "@assets/icons/Notes Minimalistic.svg?dataurl"
|
||||||
import Pallete2 from "@assets/icons/Pallete 2.svg?dataurl"
|
import Pallete2 from "@assets/icons/Pallete 2.svg?dataurl"
|
||||||
import Paperclip from "@assets/icons/Paperclip.svg?dataurl"
|
import Paperclip from "@assets/icons/Paperclip.svg?dataurl"
|
||||||
@@ -151,6 +152,7 @@
|
|||||||
"map-point": MapPoint,
|
"map-point": MapPoint,
|
||||||
"menu-dots": MenuDots,
|
"menu-dots": MenuDots,
|
||||||
"menu-dots-circle": MenuDotsCircle,
|
"menu-dots-circle": MenuDotsCircle,
|
||||||
|
moon: Moon,
|
||||||
"notes-minimalistic": NotesMinimalistic,
|
"notes-minimalistic": NotesMinimalistic,
|
||||||
"pallete-2": Pallete2,
|
"pallete-2": Pallete2,
|
||||||
paperclip: Paperclip,
|
paperclip: Paperclip,
|
||||||
|
|||||||
@@ -51,9 +51,9 @@
|
|||||||
import {setupTracking} from "@app/tracking"
|
import {setupTracking} from "@app/tracking"
|
||||||
import {setupAnalytics} from "@app/analytics"
|
import {setupAnalytics} from "@app/analytics"
|
||||||
import {nsecDecode} from "@lib/util"
|
import {nsecDecode} from "@lib/util"
|
||||||
import {theme} from "@app/theme"
|
|
||||||
import {INDEXER_RELAYS, userMembership, ensureUnwrapped, canDecrypt} from "@app/state"
|
import {INDEXER_RELAYS, userMembership, ensureUnwrapped, canDecrypt} from "@app/state"
|
||||||
import {loadUserData, listenForNotifications} from "@app/requests"
|
import {loadUserData, listenForNotifications} from "@app/requests"
|
||||||
|
import {theme} from "@app/theme"
|
||||||
import * as commands from "@app/commands"
|
import * as commands from "@app/commands"
|
||||||
import * as requests from "@app/requests"
|
import * as requests from "@app/requests"
|
||||||
import * as notifications from "@app/notifications"
|
import * as notifications from "@app/notifications"
|
||||||
@@ -121,6 +121,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync theme
|
||||||
|
theme.subscribe($theme => {
|
||||||
|
document.body.setAttribute("data-theme", $theme)
|
||||||
|
})
|
||||||
|
|
||||||
if (!db) {
|
if (!db) {
|
||||||
setupTracking()
|
setupTracking()
|
||||||
setupAnalytics()
|
setupAnalytics()
|
||||||
@@ -228,9 +233,9 @@
|
|||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
{#await ready}
|
{#await ready}
|
||||||
<div data-theme={$theme}></div>
|
<div></div>
|
||||||
{:then}
|
{:then}
|
||||||
<div data-theme={$theme}>
|
<div>
|
||||||
<AppContainer>
|
<AppContainer>
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</AppContainer>
|
</AppContainer>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type {Snippet} from "svelte"
|
||||||
import {fly} from "@lib/transition"
|
import {fly} from "@lib/transition"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Page from "@lib/components/Page.svelte"
|
import Page from "@lib/components/Page.svelte"
|
||||||
@@ -7,13 +8,17 @@
|
|||||||
import SecondaryNavSection from "@lib/components/SecondaryNavSection.svelte"
|
import SecondaryNavSection from "@lib/components/SecondaryNavSection.svelte"
|
||||||
import LogOut from "@app/components/LogOut.svelte"
|
import LogOut from "@app/components/LogOut.svelte"
|
||||||
import {pushModal} from "@app/modal"
|
import {pushModal} from "@app/modal"
|
||||||
interface Props {
|
import {theme} from "@app/theme"
|
||||||
children?: import("svelte").Snippet
|
|
||||||
|
type Props = {
|
||||||
|
children?: Snippet
|
||||||
}
|
}
|
||||||
|
|
||||||
const {children}: Props = $props()
|
const {children}: Props = $props()
|
||||||
|
|
||||||
const logout = () => pushModal(LogOut)
|
const logout = () => pushModal(LogOut)
|
||||||
|
|
||||||
|
const toggleTheme = () => theme.set($theme === "dark" ? "light" : "dark")
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SecondaryNav>
|
<SecondaryNav>
|
||||||
@@ -34,11 +39,16 @@
|
|||||||
</SecondaryNavItem>
|
</SecondaryNavItem>
|
||||||
</div>
|
</div>
|
||||||
<div in:fly|local={{delay: 150}}>
|
<div in:fly|local={{delay: 150}}>
|
||||||
|
<SecondaryNavItem onclick={toggleTheme}>
|
||||||
|
<Icon icon="moon" /> Theme
|
||||||
|
</SecondaryNavItem>
|
||||||
|
</div>
|
||||||
|
<div in:fly|local={{delay: 200}}>
|
||||||
<SecondaryNavItem href="/settings/about">
|
<SecondaryNavItem href="/settings/about">
|
||||||
<Icon icon="info-square" /> About
|
<Icon icon="info-square" /> About
|
||||||
</SecondaryNavItem>
|
</SecondaryNavItem>
|
||||||
</div>
|
</div>
|
||||||
<div in:fly|local={{delay: 200}}>
|
<div in:fly|local={{delay: 250}}>
|
||||||
<SecondaryNavItem class="text-error hover:text-error" onclick={logout}>
|
<SecondaryNavItem class="text-error hover:text-error" onclick={logout}>
|
||||||
<Icon icon="exit" /> Log Out
|
<Icon icon="exit" /> Log Out
|
||||||
</SecondaryNavItem>
|
</SecondaryNavItem>
|
||||||
|
|||||||
+10
-1
@@ -2,8 +2,8 @@ import {config} from "dotenv"
|
|||||||
import daisyui from "daisyui"
|
import daisyui from "daisyui"
|
||||||
import themes from "daisyui/src/theming/themes"
|
import themes from "daisyui/src/theming/themes"
|
||||||
|
|
||||||
config({path: ".env.local"})
|
|
||||||
config({path: ".env"})
|
config({path: ".env"})
|
||||||
|
config({path: ".env.template"})
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
@@ -30,6 +30,15 @@ export default {
|
|||||||
...themes["dark"],
|
...themes["dark"],
|
||||||
primary: process.env.VITE_PLATFORM_ACCENT,
|
primary: process.env.VITE_PLATFORM_ACCENT,
|
||||||
"primary-content": "#EAE7FF",
|
"primary-content": "#EAE7FF",
|
||||||
|
secondary: process.env.VITE_PLATFORM_SECONDARY,
|
||||||
|
"secondary-content": "#EAE7FF",
|
||||||
|
},
|
||||||
|
light: {
|
||||||
|
...themes["winter"],
|
||||||
|
primary: process.env.VITE_PLATFORM_ACCENT,
|
||||||
|
"primary-content": "#EAE7FF",
|
||||||
|
secondary: process.env.VITE_PLATFORM_SECONDARY,
|
||||||
|
"secondary-content": "#EAE7FF",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
+1
-1
@@ -4,8 +4,8 @@ import {SvelteKitPWA} from "@vite-pwa/sveltekit"
|
|||||||
import {sveltekit} from "@sveltejs/kit/vite"
|
import {sveltekit} from "@sveltejs/kit/vite"
|
||||||
import svg from "@poppanator/sveltekit-svg"
|
import svg from "@poppanator/sveltekit-svg"
|
||||||
|
|
||||||
config({path: ".env.local"})
|
|
||||||
config({path: ".env"})
|
config({path: ".env"})
|
||||||
|
config({path: ".env.template"})
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
|
|||||||
Reference in New Issue
Block a user