Compare commits

..

No commits in common. "2d121048757106aca833f6e8a8775cb54612d68e" and "19afb7f9faf4f91aea26f5c527edea513751dc95" have entirely different histories.

46 changed files with 1139 additions and 30964 deletions

View file

@ -1,10 +1,6 @@
# autonomous.contact
A hub for communities as a single service with no required external dependencies.
## Screenshots
![chat](/screenshots/screenshot-chat.png)
Bringing the BBS back.
## TODO
@ -17,13 +13,13 @@ feature discussions.
- [X] check for logged in user session
- [X] log in
- [X] refactor login/sessions/totp
- [/] media uploads
- [ ] media uploads
- [X] local upload support (keep it simple for small instances)
- [ ] S3 support (then self-host with your friends: https://garagehq.deuxfleurs.fr/)
- [ ] test mounting an s3 volume under /files and having no s3 support in the codebase
- [X] user profile page
- [X] logout button
- [/] profile editing
- [ ] profile editing
- [X] avatar uploads
- [X] chat channels
- [X] chat messages
@ -39,17 +35,17 @@ feature discussions.
- [X] '...' button to show actions
- [ ] delete message
- [ ] reply to message
- [X] copy message link
- [ ] copy message link
- [ ] @-prefixing of users for notifications/highlighting
- [ ] chat input box should auto-complete users for you
- [ ] messages to you should be highlighted somehow
- [/] chat message reactions
- [ ] chat message reactions
- [X] sidebar on mobile needs to show/hide via a button
- [/] clean up after initial implementation
- [ ] clean up after initial implementation
- [X] split the monolithic talk.html up
- [X] rename talk => chat
- [ ] way too much spaghetti has accumulated in the frontend - take a cleanup pass
- [/] chat message processing
- [ ] chat message processing
- [X] auto-link urls
- [X] use this regex: `(?:(?<protocol>[a-zA-Z]+):)?(?:\/\/)?(?:(?<auth>(?<username>\S.+)\:(?<password>.+))\@)?(?<host>(?:(?<hostname>[-a-zA-Z0-9\.]+)\.)?(?<domain>(?:[-a-zA-Z0-9]+?\.(?<tld>[-a-zA-Z0-9]{2,64}))|localhost)(?:\:(?<port>[0-9]{1,6}))?)\b(?<path>[-a-zA-Z0-9@:%_{}\[\]<>\(\)\+.~&\/="]*?(?<extension>\.[^\.?/#"\n<>]+)?)(?:\?(?<query>[a-zA-Z0-9!$%&<>()*+,-\.\/\:\;\=\?\@_~"]+))?(?:#(?<hash>[a-zA-Z0-9!$&'()*+,-\.\/\:\;\=\?\@_~"]*?))?(?:$|\s)`
- [ ] :emoji: replacements
@ -57,11 +53,11 @@ feature discussions.
- [ ] add a button to allow for loading a preview
- we want to do this loading client-side, and so should only do it if they ask us to
- [ ] parse head meta, eg: https://github.com/AndrejGajdos/link-preview-generator/blob/master/index.js
- [/] embedded video for
- [ ] embedded video for
- [X] youtube
- [X] vimeo
- [ ] tiktok
- [/] embedded audio or audio cards for:
- [ ] embedded audio or audio cards for:
- [X] tidal
- [X] spotify
- [ ] youtube (any way to differentiate for yt music?)
@ -72,12 +68,12 @@ feature discussions.
- [ ] copy original link (hopefully just a button with some onclick we can slap next to the iframe and style?)
- [ ] toggle embed (toggle between showing the embed and the original link)
- [X] punycode urls before url extraction? (see: https://stackoverflow.com/a/26618995)
- [/] gif support
- [ ] gif support
- [X] gif embeds
- [X] mp4 embeds
- [ ] start/stop gif control
- [ ] hide control
- [/] inline image support
- [ ] inline image support
- [X] inline images
- [ ] fullscreen images on click to show largest version
- [ ] hide control
@ -87,8 +83,8 @@ feature discussions.
- [ ] emoji picker
- [ ] custom emoji support
- [ ] upload custom gif emoji
- [/] Notifications
- [X] if web notifications are not enabled, show a button to turn them on
- [ ] Notifications
- [ ] if web notifications are not enabled, show a button to turn them on
- [ ] if web notifications are enabled, emit on events
- [ ] ability to mute
- [ ] users

View file

@ -11,15 +11,11 @@
"test": "DENO_ENV=test FSDB_ROOT=$PWD/tests/data/$(date --iso-8601=seconds) SERVERUS_ROOT=$PWD/public SERVERUS_PUT_PATHS_ALLOWED=./files SERVERUS_DELETE_PATHS_ALLOWED=./files deno test --allow-env --allow-read --allow-write --allow-net --allow-import --trace-leaks --fail-fast tests/"
},
"test": {
"exclude": [
"tests/data/"
]
"exclude": ["tests/data/"]
},
"compilerOptions": {},
"fmt": {
"include": [
"**/*.ts"
],
"include": ["**/*.ts"],
"options": {
"useTabs": true,
"lineWidth": 180,
@ -29,28 +25,22 @@
}
},
"lint": {
"include": [
"**/*.ts"
],
"include": ["**/*.ts"],
"rules": {
"tags": [
"recommended"
],
"exclude": [
"no-explicit-any"
]
"tags": ["recommended"],
"exclude": ["no-explicit-any"]
}
},
"imports": {
"@andyburke/fsdb": "jsr:@andyburke/fsdb@^1.2.4",
"@andyburke/lurid": "jsr:@andyburke/lurid@^0.2.0",
"@andyburke/serverus": "jsr:@andyburke/serverus@^0.16.0",
"@andyburke/serverus": "jsr:@andyburke/serverus@^0.13.0",
"@da/bcrypt": "jsr:@da/bcrypt@^1.0.1",
"@std/assert": "jsr:@std/assert@^1.0.17",
"@std/assert": "jsr:@std/assert@^1.0.15",
"@std/encoding": "jsr:@std/encoding@^1.0.10",
"@std/fs": "jsr:@std/fs@^1.0.22",
"@std/http": "jsr:@std/http@^1.0.23",
"@std/fs": "jsr:@std/fs@^1.0.19",
"@std/http": "jsr:@std/http@^1.0.21",
"@std/media-types": "jsr:@std/media-types@^1.1.0",
"@std/path": "jsr:@std/path@^1.1.4"
"@std/path": "jsr:@std/path@^1.1.2"
}
}

84
deno.lock generated
View file

@ -3,31 +3,31 @@
"specifiers": {
"jsr:@andyburke/fsdb@^1.2.4": "1.2.4",
"jsr:@andyburke/lurid@0.2": "0.2.0",
"jsr:@andyburke/serverus@0.16": "0.16.0",
"jsr:@andyburke/serverus@0.13": "0.13.0",
"jsr:@da/bcrypt@*": "1.0.1",
"jsr:@da/bcrypt@^1.0.1": "1.0.1",
"jsr:@std/assert@^1.0.17": "1.0.17",
"jsr:@std/cli@^1.0.19": "1.0.25",
"jsr:@std/cli@^1.0.20": "1.0.25",
"jsr:@std/cli@^1.0.21": "1.0.25",
"jsr:@std/cli@^1.0.25": "1.0.25",
"jsr:@std/assert@^1.0.15": "1.0.15",
"jsr:@std/cli@^1.0.19": "1.0.23",
"jsr:@std/cli@^1.0.20": "1.0.23",
"jsr:@std/cli@^1.0.21": "1.0.23",
"jsr:@std/cli@^1.0.23": "1.0.23",
"jsr:@std/encoding@^1.0.10": "1.0.10",
"jsr:@std/fmt@^1.0.6": "1.0.8",
"jsr:@std/fmt@^1.0.8": "1.0.8",
"jsr:@std/fs@^1.0.18": "1.0.22",
"jsr:@std/fs@^1.0.19": "1.0.22",
"jsr:@std/fs@^1.0.21": "1.0.22",
"jsr:@std/fs@^1.0.22": "1.0.22",
"jsr:@std/fs@^1.0.18": "1.0.19",
"jsr:@std/fs@^1.0.19": "1.0.19",
"jsr:@std/html@^1.0.5": "1.0.5",
"jsr:@std/http@^1.0.20": "1.0.23",
"jsr:@std/http@^1.0.23": "1.0.23",
"jsr:@std/http@^1.0.20": "1.0.21",
"jsr:@std/http@^1.0.21": "1.0.21",
"jsr:@std/internal@^1.0.10": "1.0.12",
"jsr:@std/internal@^1.0.12": "1.0.12",
"jsr:@std/internal@^1.0.9": "1.0.12",
"jsr:@std/media-types@^1.1.0": "1.1.0",
"jsr:@std/net@^1.0.6": "1.0.6",
"jsr:@std/path@^1.1.0": "1.1.4",
"jsr:@std/path@^1.1.1": "1.1.4",
"jsr:@std/path@^1.1.4": "1.1.4",
"jsr:@std/streams@^1.0.16": "1.0.16",
"jsr:@std/path@^1.1.0": "1.1.2",
"jsr:@std/path@^1.1.1": "1.1.2",
"jsr:@std/path@^1.1.2": "1.1.2",
"jsr:@std/streams@^1.0.13": "1.0.13",
"npm:@types/node@*": "22.15.15"
},
"jsr": {
@ -45,8 +45,8 @@
"jsr:@std/cli@^1.0.19"
]
},
"@andyburke/serverus@0.16.0": {
"integrity": "625fc3f08ddc377beb86b282d603ca6154cf38e136d916ec19a87ae4c4ed86d5",
"@andyburke/serverus@0.13.0": {
"integrity": "73f451e1b68cd9be3938333b06290bfeab275361453559f40dfeab19dc4ad6d7",
"dependencies": [
"jsr:@std/cli@^1.0.21",
"jsr:@std/fmt@^1.0.6",
@ -59,14 +59,14 @@
"@da/bcrypt@1.0.1": {
"integrity": "d2172d3acbcff52e0465557a1a48b1ff1c92df08c90712dae5372255a8c45eb3"
},
"@std/assert@1.0.17": {
"integrity": "df5ebfffe77c03b3fa1401e11c762cc8f603d51021c56c4d15a8c7ab45e90dbe",
"@std/assert@1.0.15": {
"integrity": "d64018e951dbdfab9777335ecdb000c0b4e3df036984083be219ce5941e4703b",
"dependencies": [
"jsr:@std/internal"
"jsr:@std/internal@^1.0.12"
]
},
"@std/cli@1.0.25": {
"integrity": "1f85051b370c97a7a9dfc6ba626e7ed57a91bea8c081597276d1e78d929d8c91"
"@std/cli@1.0.23": {
"integrity": "bf95b7a9425ba2af1ae5a6359daf58c508f2decf711a76ed2993cd352498ccca"
},
"@std/encoding@1.0.10": {
"integrity": "8783c6384a2d13abd5e9e87a7ae0520a30e9f56aeeaa3bdf910a3eaaf5c811a1"
@ -74,27 +74,27 @@
"@std/fmt@1.0.8": {
"integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7"
},
"@std/fs@1.0.22": {
"integrity": "de0f277a58a867147a8a01bc1b181d0dfa80bfddba8c9cf2bacd6747bcec9308",
"@std/fs@1.0.19": {
"integrity": "051968c2b1eae4d2ea9f79a08a3845740ef6af10356aff43d3e2ef11ed09fb06",
"dependencies": [
"jsr:@std/internal",
"jsr:@std/path@^1.1.4"
"jsr:@std/internal@^1.0.9",
"jsr:@std/path@^1.1.1"
]
},
"@std/html@1.0.5": {
"integrity": "4e2d693f474cae8c16a920fa5e15a3b72267b94b84667f11a50c6dd1cb18d35e"
},
"@std/http@1.0.23": {
"integrity": "6634e9e034c589bf35101c1b5ee5bbf052a5987abca20f903e58bdba85c80dee",
"@std/http@1.0.21": {
"integrity": "abb5c747651ee6e3ea6139858fd9b1810d2c97f53a5e6722f3b6d27a6d263edc",
"dependencies": [
"jsr:@std/cli@^1.0.25",
"jsr:@std/cli@^1.0.23",
"jsr:@std/encoding",
"jsr:@std/fmt@^1.0.8",
"jsr:@std/fs@^1.0.21",
"jsr:@std/fs@^1.0.19",
"jsr:@std/html",
"jsr:@std/media-types",
"jsr:@std/net",
"jsr:@std/path@^1.1.4",
"jsr:@std/path@^1.1.2",
"jsr:@std/streams"
]
},
@ -107,14 +107,14 @@
"@std/net@1.0.6": {
"integrity": "110735f93e95bb9feb95790a8b1d1bf69ec0dc74f3f97a00a76ea5efea25500c"
},
"@std/path@1.1.4": {
"integrity": "1d2d43f39efb1b42f0b1882a25486647cb851481862dc7313390b2bb044314b5",
"@std/path@1.1.2": {
"integrity": "c0b13b97dfe06546d5e16bf3966b1cadf92e1cc83e56ba5476ad8b498d9e3038",
"dependencies": [
"jsr:@std/internal"
"jsr:@std/internal@^1.0.10"
]
},
"@std/streams@1.0.16": {
"integrity": "85030627befb1767c60d4f65cb30fa2f94af1d6ee6e5b2515b76157a542e89c4"
"@std/streams@1.0.13": {
"integrity": "772d208cd0d3e5dac7c1d9e6cdb25842846d136eea4a41a62e44ed4ab0c8dd9e"
}
},
"npm": {
@ -135,14 +135,14 @@
"dependencies": [
"jsr:@andyburke/fsdb@^1.2.4",
"jsr:@andyburke/lurid@0.2",
"jsr:@andyburke/serverus@0.16",
"jsr:@andyburke/serverus@0.13",
"jsr:@da/bcrypt@^1.0.1",
"jsr:@std/assert@^1.0.17",
"jsr:@std/assert@^1.0.15",
"jsr:@std/encoding@^1.0.10",
"jsr:@std/fs@^1.0.22",
"jsr:@std/http@^1.0.23",
"jsr:@std/fs@^1.0.19",
"jsr:@std/http@^1.0.21",
"jsr:@std/media-types@^1.1.0",
"jsr:@std/path@^1.1.4"
"jsr:@std/path@^1.1.2"
]
}
}

View file

View file

@ -6,11 +6,10 @@ import { SESSION, SESSIONS } from '../../../models/session.ts';
import { TOTP_ENTRIES } from '../../../models/totp_entry.ts';
import { encodeBase64 } from '@std/encoding/base64';
import parse_body from '../../../utils/bodyparser.ts';
import { AUTHED_BEFORE_COOKIE_ID, get_session, get_user, PRECHECK_TABLE, require_user, SESSION_ID_TOKEN, SESSION_SECRET_TOKEN } from '../../../utils/prechecks.ts';
import { get_session, get_user, PRECHECK_TABLE, require_user, SESSION_ID_TOKEN, SESSION_SECRET_TOKEN } from '../../../utils/prechecks.ts';
import * as bcrypt from '@da/bcrypt';
import { verifyTotp } from '../../../utils/totp.ts';
const AUTHED_BEFORE_EXPIRATION: number = 399 * (24 * (60 * (60 * 1_000))); // 399 days
const DEFAULT_SESSION_TIME: number = 365 * (24 * (60 * (60 * 1_000))); // 365 days
export const PRECHECKS: PRECHECK_TABLE = {};
@ -207,7 +206,6 @@ export async function create_new_session(session_settings: SESSION_INFO): Promis
const headers = new Headers();
const expires_in_utc = new Date(session.timestamps.expires).toUTCString();
headers.append('Set-Cookie', `${AUTHED_BEFORE_COOKIE_ID}=1; Path=/; Secure; Expires=${new Date(new Date(now).valueOf() + AUTHED_BEFORE_EXPIRATION).toUTCString()}`);
headers.append('Set-Cookie', `${SESSION_ID_TOKEN}=${session.id}; Path=/; Secure; Expires=${expires_in_utc}`);
headers.append(`x-${SESSION_ID_TOKEN}`, session.id);

View file

@ -1,5 +1,5 @@
import lurid from '@andyburke/lurid';
import { get_session, get_user, PRECHECK_TABLE, require_user, user_has_create_permission_for_event } from '../../../utils/prechecks.ts';
import { get_session, get_user, PRECHECK_TABLE, require_user, user_has_write_permission_for_event } from '../../../utils/prechecks.ts';
import * as CANNED_RESPONSES from '../../../utils/canned_responses.ts';
import { EVENT, EVENTS, VALIDATE_EVENT } from '../../../models/event.ts';
import parse_body from '../../../utils/bodyparser.ts';
@ -39,14 +39,7 @@ export async function GET(request: Request, meta: Record<string, any>): Promise<
const {
event_type,
event_id
} = /^.*\/events\/.*\/(?<event_type>.*?)\:(?<event_id>[A-Za-z-]+)\.json$/.exec(entry.path)?.groups ?? {};
console.dir({
entry,
event_type,
event_id,
query: meta.query
});
} = /^.*\/events\/(?<event_type>.*?)\/.*\/(?<event_id>[A-Za-z-]+)\.json$/.exec(entry.path)?.groups ?? {};
if (meta.query.after_id && event_id <= meta.query.after_id) {
return false;
@ -217,7 +210,7 @@ export async function POST(req: Request, meta: Record<string, any>): Promise<Res
});
}
if (!user_has_create_permission_for_event(meta.user, event)) {
if (!user_has_write_permission_for_event(meta.user, event)) {
return CANNED_RESPONSES.permission_denied();
}

View file

@ -21,9 +21,7 @@
@media (prefers-color-scheme: light) {
:root {
--bg: hsl(from var(--base-color) h 20% 93%);
--bg-darker: hsl(from var(--base-color) h 20% 88%);
--bg-lighter: hsl(from var(--base-color) h 20% 98%);
--bg: hsl(from var(--base-color) h 20% 95%);
--text: hsl(from var(--base-color) h 5% 5%);
--accent: hsl(from var(--base-color) h calc(s + 10) calc(l + 20));
@ -171,15 +169,6 @@ body {
/* fixed height? */
}
#background-container {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: -1;
}
main {
position: relative;
width: 100%;
@ -1693,32 +1682,6 @@ body[data-perms*="files.write.own"] [data-requires-permission="files.write.own"]
left: 11px;
}
.icon.map-pin {
box-sizing: border-box;
position: relative;
display: block;
transform: rotate(45deg) scale(var(--icon-scale, 1));
width: 18px;
height: 18px;
border-radius: 100% 100% 0 100%;
border: 2px solid;
margin-top: -4px;
}
.icon.map-pin::before {
content: "";
display: block;
box-sizing: border-box;
position: absolute;
width: 8px;
height: 8px;
border: 2px solid;
top: 3px;
left: 3px;
border-radius: 40px;
}
/* AUDIO PLAYER ICONS */
.icon.skip-back {
box-sizing: border-box;

View file

@ -1,6 +0,0 @@
<!DOCTYPE html>
<html>
<body>
<h1>Hello World - foo</h1>
</body>
</html>

View file

@ -3,57 +3,54 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><!-- #include "./files/settings/title.txt" or "./title.txt" --></title>
<title>autonomous.contact</title>
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png" ></link>
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png" ></link>
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png" ></link>
<link rel="apple-touch-icon" sizes="180x180" href="./icons/apple-touch-icon.png" ></link>
<link rel="icon" type="image/png" sizes="32x32" href="./icons/favicon-32x32.png" ></link>
<link rel="icon" type="image/png" sizes="16x16" href="./icons/favicon-16x16.png" ></link>
<link rel="stylesheet" href="/base.css"></link>
<link rel="stylesheet" href="/files/custom.css"></link>
<link rel="stylesheet" href="./base.css"></link>
<!-- inlining these to force them to be scoped for everything else -->
<script type="text/javascript"><!-- #include "./js/_utils.js" --></script>
<script type="text/javascript"><!-- #include "./js/api.js" --></script>
<script type="text/javascript"><!-- #include "./js/app.js" --></script>
<script type="text/javascript"><!-- #include file="./js/_utils.js" --></script>
<script type="text/javascript"><!-- #include file="./js/api.js" --></script>
<script type="text/javascript"><!-- #include file="./js/app.js" --></script>
<!-- everything else -->
<script src="/js/audioplayer.js" type="text/javascript"></script>
<script src="/js/datetimeutils.js" type="text/javascript"></script>
<script src="/js/debounce.js" type="text/javascript"></script>
<script src="/js/external/md_to_html.js" type="text/javascript"></script>
<script src="/js/embeds/audio.js" type="text/javascript"></script>
<script src="/js/embeds/gif.js" type="text/javascript"></script>
<script src="/js/embeds/image.js" type="text/javascript"></script>
<script src="/js/embeds/link.js" type="text/javascript"></script>
<script src="/js/embeds/mkv.js" type="text/javascript"></script>
<script src="/js/embeds/mov.js" type="text/javascript"></script>
<script src="/js/embeds/mp4.js" type="text/javascript"></script>
<script src="/js/embeds/spotify.js" type="text/javascript"></script>
<script src="/js/embeds/tidal.js" type="text/javascript"></script>
<script src="/js/embeds/vimeo.js" type="text/javascript"></script>
<script src="/js/embeds/youtube.js" type="text/javascript"></script>
<script src="/js/emojis/en.js" type="text/javascript"></script>
<script src="/js/eventactions.js" type="text/javascript"></script>
<script src="/js/htmlify.js" type="text/javascript"></script>
<script src="/js/locationchange.js" type="text/javascript"></script>
<script src="/js/notifications.js" type="text/javascript"></script>
<script src="/js/reactions.js" type="text/javascript"></script>
<script src="/js/smartfeeds.js" type="text/javascript"></script>
<script src="/js/smartforms.js" type="text/javascript"></script>
<script src="/js/textareaenhancements.js" type="text/javascript"></script>
<script src="/js/totp.js" type="text/javascript"></script>
<script src="./js/audioplayer.js" type="text/javascript"></script>
<script src="./js/datetimeutils.js" type="text/javascript"></script>
<script src="./js/debounce.js" type="text/javascript"></script>
<script src="./js/external/md_to_html.js" type="text/javascript"></script>
<script src="./js/embeds/audio.js" type="text/javascript"></script>
<script src="./js/embeds/gif.js" type="text/javascript"></script>
<script src="./js/embeds/image.js" type="text/javascript"></script>
<script src="./js/embeds/link.js" type="text/javascript"></script>
<script src="./js/embeds/mkv.js" type="text/javascript"></script>
<script src="./js/embeds/mov.js" type="text/javascript"></script>
<script src="./js/embeds/mp4.js" type="text/javascript"></script>
<script src="./js/embeds/spotify.js" type="text/javascript"></script>
<script src="./js/embeds/tidal.js" type="text/javascript"></script>
<script src="./js/embeds/vimeo.js" type="text/javascript"></script>
<script src="./js/embeds/youtube.js" type="text/javascript"></script>
<script src="./js/emojis/en.js" type="text/javascript"></script>
<script src="./js/eventactions.js" type="text/javascript"></script>
<script src="./js/htmlify.js" type="text/javascript"></script>
<script src="./js/locationchange.js" type="text/javascript"></script>
<script src="./js/notifications.js" type="text/javascript"></script>
<script src="./js/reactions.js" type="text/javascript"></script>
<script src="./js/smartfeeds.js" type="text/javascript"></script>
<script src="./js/smartforms.js" type="text/javascript"></script>
<script src="./js/textareaenhancements.js" type="text/javascript"></script>
<script src="./js/totp.js" type="text/javascript"></script>
</head>
<body>
<div id="background-container"></div>
<!-- #include "./signup_login_wall.html" -->
<!-- #include file="./signup_login_wall.html" -->
<main>
<!-- #include "./sidebar/sidebar.html" -->
<!-- #include file="./sidebar/sidebar.html" -->
<!-- #include "./tabs/tabs.html" -->
<!-- #include file="./tabs/tabs.html" -->
</main>
</body>
</html>

View file

@ -5,13 +5,10 @@ const api = {
...__options,
};
// FIXME: this will break with different server settings
// TODO: we need the cookie names here to match any configured on the server
const session_id = (document.cookie.match(
/^(?:.*;)?\s*session_id\s*=\s*([^;]+)(?:.*)?$/,
) || [, null])[1];
// FIXME: this will break with different server settings
// TODO: this wasn't really intended to be persisted in a cookie
const session_secret = (document.cookie.match(
/^(?:.*;)?\s*session_secret\s*=\s*([^;]+)(?:.*)?$/,

View file

@ -11,11 +11,6 @@ const APP = {
on: function( event_name, callback ) {
this._event_callbacks[ event_name ] = this._event_callbacks[ event_name ] ?? new Set();
this._event_callbacks[event_name ].add( callback );
if ( event_name === 'load' && this._loaded ) {
setTimeout( callback, 0 );
}
return true;
},
@ -73,13 +68,13 @@ const APP = {
this._emit( 'view_changed', {
previous,
view,
channel_id
view
});
}
if (!document.body.dataset.channel || document.body.dataset.channel !== channel_id) {
const previous = typeof document.body.dataset.channel === 'string' ? document.body.dataset.channel : undefined;
if ( channel_id ) {
document.body.dataset.channel = channel_id;
}
@ -87,15 +82,25 @@ const APP = {
delete document.body.dataset.channel;
}
const target_channel_id = channel_id ?? this.CHANNELS.CHANNEL_LIST[0]?.id;
// TODO: allow a different default than chat
const hash_target = `/${ view ? view : 'chat' }` + ( target_channel_id ? `/channel/${ target_channel_id }` : '' );
if ( window.location.hash?.slice( 1 ) !== hash_target ) {
if ( previous !== target_channel_id ) {
this._emit( 'channel_changed', {
previous,
channel_id
channel_id: target_channel_id
});
}
window.location.hash = hash_target;
}
}
},
load: async function() {
this._loaded = false;
this.server = {
name: document.title,
url: window.location.origin ?? window.location.href,
@ -132,7 +137,6 @@ const APP = {
this.check_if_logged_in();
this.extract_url_hash_info();
this._emit( 'load', this );
this._loaded = true;
},
update_user: async function( user ) {
@ -232,11 +236,22 @@ const APP = {
const channels_response = await api.fetch("/api/channels");
if (channels_response.ok) {
const new_channels = await channels_response.json();
const has_differences =
APP.CHANNELS.CHANNEL_LIST.length !== new_channels.length ||
new_channels.some((channel, index) => {
return (
APP.CHANNELS.CHANNEL_LIST[index]?.id !== channel.id ||
APP.CHANNELS.CHANNEL_LIST[index]?.name !== channel.name
);
});
if (has_differences) {
APP.CHANNELS.CHANNEL_LIST = [...new_channels];
APP._emit( 'channels_updated', {
channels: APP.CHANNELS.CHANNEL_LIST
});
}
APP.CHANNELS._last_channel_update = now;
}
@ -256,8 +271,3 @@ const APP = {
};
document.addEventListener("DOMContentLoaded", APP.load.bind( APP ));
APP.on( 'view_changed', ( { view } ) => {
if ( !view ) {
window.location.hash = '/chat';
}
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 B

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,661 +0,0 @@
/* required styles */
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
/* Prevents IE11 from highlighting tiles in blue */
.leaflet-tile::selection {
background: transparent;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
image-rendering: -webkit-optimize-contrast;
}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
width: 1600px;
height: 1600px;
-webkit-transform-origin: 0 0;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg {
max-width: none !important;
max-height: none !important;
}
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer,
.leaflet-container .leaflet-tile {
max-width: none !important;
max-height: none !important;
width: auto;
padding: 0;
}
.leaflet-container img.leaflet-tile {
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
mix-blend-mode: plus-lighter;
}
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
}
.leaflet-container.leaflet-touch-drag {
-ms-touch-action: pinch-zoom;
/* Fallback for FF which doesn't support pinch-zoom */
touch-action: none;
touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
-ms-touch-action: none;
touch-action: none;
}
.leaflet-container {
-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 800;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-pane { z-index: 400; }
.leaflet-tile-pane { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane { z-index: 500; }
.leaflet-marker-pane { z-index: 600; }
.leaflet-tooltip-pane { z-index: 650; }
.leaflet-popup-pane { z-index: 700; }
.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg { z-index: 200; }
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 800;
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-animated {
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
svg.leaflet-zoom-animated {
will-change: transform;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile {
-webkit-transition: none;
-moz-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-interactive {
cursor: pointer;
}
.leaflet-grab {
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
cursor: crosshair;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}
/* marker & overlays interactivity */
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-image-layer,
.leaflet-pane > svg path,
.leaflet-tile-container {
pointer-events: none;
}
.leaflet-marker-icon.leaflet-interactive,
.leaflet-image-layer.leaflet-interactive,
.leaflet-pane > svg path.leaflet-interactive,
svg.leaflet-image-layer.leaflet-interactive path {
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline-offset: 1px;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255,255,255,0.5);
}
/* general typography */
.leaflet-container {
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
font-size: 12px;
font-size: 0.75rem;
line-height: 1.5;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
}
.leaflet-bar a {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover,
.leaflet-bar a:focus {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
line-height: 30px;
}
.leaflet-touch .leaflet-bar a:first-child {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.leaflet-touch .leaflet-bar a:last-child {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
/* zoom control */
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
font-size: 22px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background: #fff;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-scrollbar {
overflow-y: scroll;
overflow-x: hidden;
padding-right: 5px;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
font-size: 13px;
font-size: 1.08333em;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* Default icon URLs */
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
background-image: url(images/marker-icon.png);
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.8);
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
line-height: 1.4;
}
.leaflet-control-attribution a {
text-decoration: none;
}
.leaflet-control-attribution a:hover,
.leaflet-control-attribution a:focus {
text-decoration: underline;
}
.leaflet-attribution-flag {
display: inline !important;
vertical-align: baseline !important;
width: 1em;
height: 0.6669em;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
white-space: nowrap;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: rgba(255, 255, 255, 0.8);
text-shadow: 1px 1px #fff;
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
margin-bottom: 20px;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 24px 13px 20px;
line-height: 1.3;
font-size: 13px;
font-size: 1.08333em;
min-height: 1px;
}
.leaflet-popup-content p {
margin: 17px 0;
margin: 1.3em 0;
}
.leaflet-popup-tip-container {
width: 40px;
height: 20px;
position: absolute;
left: 50%;
margin-top: -1px;
margin-left: -20px;
overflow: hidden;
pointer-events: none;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
pointer-events: auto;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
border: none;
text-align: center;
width: 24px;
height: 24px;
font: 16px/24px Tahoma, Verdana, sans-serif;
color: #757575;
text-decoration: none;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover,
.leaflet-container a.leaflet-popup-close-button:focus {
color: #585858;
}
.leaflet-popup-scrolled {
overflow: auto;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
-ms-zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
}
.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
border: 1px solid #999;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}
/* Tooltip */
/* Base styles for the element that has a tooltip */
.leaflet-tooltip {
position: absolute;
padding: 6px;
background-color: #fff;
border: 1px solid #fff;
border-radius: 3px;
color: #222;
white-space: nowrap;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.leaflet-tooltip.leaflet-interactive {
cursor: pointer;
pointer-events: auto;
}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
position: absolute;
pointer-events: none;
border: 6px solid transparent;
background: transparent;
content: "";
}
/* Directions */
.leaflet-tooltip-bottom {
margin-top: 6px;
}
.leaflet-tooltip-top {
margin-top: -6px;
}
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
left: 50%;
margin-left: -6px;
}
.leaflet-tooltip-top:before {
bottom: 0;
margin-bottom: -12px;
border-top-color: #fff;
}
.leaflet-tooltip-bottom:before {
top: 0;
margin-top: -12px;
margin-left: -6px;
border-bottom-color: #fff;
}
.leaflet-tooltip-left {
margin-left: -6px;
}
.leaflet-tooltip-right {
margin-left: 6px;
}
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
top: 50%;
margin-top: -6px;
}
.leaflet-tooltip-left:before {
right: 0;
margin-right: -12px;
border-left-color: #fff;
}
.leaflet-tooltip-right:before {
left: 0;
margin-left: -12px;
border-right-color: #fff;
}
/* Printing */
@media print {
/* Prevent printers from removing background-images of controls. */
.leaflet-control {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,28 +0,0 @@
/*
jquery-qrcode v0.14.0 - https://larsjung.de/jquery-qrcode/ */
'use strict';let G=null;class H{}H.render=function(w,B){G(w,B)};self.QrCreator=H;
(function(w){function B(t,c,a,e){var b={},h=w(a,c);h.u(t);h.J();e=e||0;var r=h.h(),d=h.h()+2*e;b.text=t;b.level=c;b.version=a;b.O=d;b.a=function(b,a){b-=e;a-=e;return 0>b||b>=r||0>a||a>=r?!1:h.a(b,a)};return b}function C(t,c,a,e,b,h,r,d,g,x){function u(b,a,f,c,d,r,g){b?(t.lineTo(a+r,f+g),t.arcTo(a,f,c,d,h)):t.lineTo(a,f)}r?t.moveTo(c+h,a):t.moveTo(c,a);u(d,e,a,e,b,-h,0);u(g,e,b,c,b,0,-h);u(x,c,b,c,a,h,0);u(r,c,a,e,a,0,h)}function z(t,c,a,e,b,h,r,d,g,x){function u(b,a,c,d){t.moveTo(b+c,a);t.lineTo(b,
a);t.lineTo(b,a+d);t.arcTo(b,a,b+c,a,h)}r&&u(c,a,h,h);d&&u(e,a,-h,h);g&&u(e,b,-h,-h);x&&u(c,b,h,-h)}function A(t,c){var a=c.fill;if("string"===typeof a)t.fillStyle=a;else{var e=a.type,b=a.colorStops;a=a.position.map((b)=>Math.round(b*c.size));if("linear-gradient"===e)var h=t.createLinearGradient.apply(t,a);else if("radial-gradient"===e)h=t.createRadialGradient.apply(t,a);else throw Error("Unsupported fill");b.forEach(([b,a])=>{h.addColorStop(b,a)});t.fillStyle=h}}function y(t,c){a:{var a=c.text,e=
c.v,b=c.N,h=c.K,r=c.P;b=Math.max(1,b||1);for(h=Math.min(40,h||40);b<=h;b+=1)try{var d=B(a,e,b,r);break a}catch(J){}d=void 0}if(!d)return null;a=t.getContext("2d");c.background&&(a.fillStyle=c.background,a.fillRect(c.left,c.top,c.size,c.size));e=d.O;h=c.size/e;a.beginPath();for(r=0;r<e;r+=1)for(b=0;b<e;b+=1){var g=a,x=c.left+b*h,u=c.top+r*h,p=r,q=b,f=d.a,k=x+h,m=u+h,D=p-1,E=p+1,n=q-1,l=q+1,y=Math.floor(Math.min(.5,Math.max(0,c.R))*h),v=f(p,q),I=f(D,n),w=f(D,q);D=f(D,l);var F=f(p,l);l=f(E,l);q=f(E,
q);E=f(E,n);p=f(p,n);x=Math.round(x);u=Math.round(u);k=Math.round(k);m=Math.round(m);v?C(g,x,u,k,m,y,!w&&!p,!w&&!F,!q&&!F,!q&&!p):z(g,x,u,k,m,y,w&&p&&I,w&&F&&D,q&&F&&l,q&&p&&E)}A(a,c);a.fill();return t}var v={minVersion:1,maxVersion:40,ecLevel:"L",left:0,top:0,size:200,fill:"#000",background:null,text:"no text",radius:.5,quiet:0};G=function(t,c){var a={};Object.assign(a,v,t);a.N=a.minVersion;a.K=a.maxVersion;a.v=a.ecLevel;a.left=a.left;a.top=a.top;a.size=a.size;a.fill=a.fill;a.background=a.background;
a.text=a.text;a.R=a.radius;a.P=a.quiet;if(c instanceof HTMLCanvasElement){if(c.width!==a.size||c.height!==a.size)c.width=a.size,c.height=a.size;c.getContext("2d").clearRect(0,0,c.width,c.height);y(c,a)}else t=document.createElement("canvas"),t.width=a.size,t.height=a.size,a=y(t,a),c.appendChild(a)}})(function(){function w(c){var a=C.s(c);return{S:function(){return 4},b:function(){return a.length},write:function(c){for(var b=0;b<a.length;b+=1)c.put(a[b],8)}}}function B(){var c=[],a=0,e={B:function(){return c},
c:function(b){return 1==(c[Math.floor(b/8)]>>>7-b%8&1)},put:function(b,h){for(var a=0;a<h;a+=1)e.m(1==(b>>>h-a-1&1))},f:function(){return a},m:function(b){var h=Math.floor(a/8);c.length<=h&&c.push(0);b&&(c[h]|=128>>>a%8);a+=1}};return e}function C(c,a){function e(b,h){for(var a=-1;7>=a;a+=1)if(!(-1>=b+a||d<=b+a))for(var c=-1;7>=c;c+=1)-1>=h+c||d<=h+c||(r[b+a][h+c]=0<=a&&6>=a&&(0==c||6==c)||0<=c&&6>=c&&(0==a||6==a)||2<=a&&4>=a&&2<=c&&4>=c?!0:!1)}function b(b,a){for(var f=d=4*c+17,k=Array(f),m=0;m<
f;m+=1){k[m]=Array(f);for(var p=0;p<f;p+=1)k[m][p]=null}r=k;e(0,0);e(d-7,0);e(0,d-7);f=y.G(c);for(k=0;k<f.length;k+=1)for(m=0;m<f.length;m+=1){p=f[k];var q=f[m];if(null==r[p][q])for(var n=-2;2>=n;n+=1)for(var l=-2;2>=l;l+=1)r[p+n][q+l]=-2==n||2==n||-2==l||2==l||0==n&&0==l}for(f=8;f<d-8;f+=1)null==r[f][6]&&(r[f][6]=0==f%2);for(f=8;f<d-8;f+=1)null==r[6][f]&&(r[6][f]=0==f%2);f=y.w(h<<3|a);for(k=0;15>k;k+=1)m=!b&&1==(f>>k&1),r[6>k?k:8>k?k+1:d-15+k][8]=m,r[8][8>k?d-k-1:9>k?15-k:14-k]=m;r[d-8][8]=!b;if(7<=
c){f=y.A(c);for(k=0;18>k;k+=1)m=!b&&1==(f>>k&1),r[Math.floor(k/3)][k%3+d-8-3]=m;for(k=0;18>k;k+=1)m=!b&&1==(f>>k&1),r[k%3+d-8-3][Math.floor(k/3)]=m}if(null==g){b=t.I(c,h);f=B();for(k=0;k<x.length;k+=1)m=x[k],f.put(4,4),f.put(m.b(),y.f(4,c)),m.write(f);for(k=m=0;k<b.length;k+=1)m+=b[k].j;if(f.f()>8*m)throw Error("code length overflow. ("+f.f()+">"+8*m+")");for(f.f()+4<=8*m&&f.put(0,4);0!=f.f()%8;)f.m(!1);for(;!(f.f()>=8*m);){f.put(236,8);if(f.f()>=8*m)break;f.put(17,8)}var u=0;m=k=0;p=Array(b.length);
q=Array(b.length);for(n=0;n<b.length;n+=1){var v=b[n].j,w=b[n].o-v;k=Math.max(k,v);m=Math.max(m,w);p[n]=Array(v);for(l=0;l<p[n].length;l+=1)p[n][l]=255&f.B()[l+u];u+=v;l=y.C(w);v=z(p[n],l.b()-1).l(l);q[n]=Array(l.b()-1);for(l=0;l<q[n].length;l+=1)w=l+v.b()-q[n].length,q[n][l]=0<=w?v.c(w):0}for(l=f=0;l<b.length;l+=1)f+=b[l].o;f=Array(f);for(l=u=0;l<k;l+=1)for(n=0;n<b.length;n+=1)l<p[n].length&&(f[u]=p[n][l],u+=1);for(l=0;l<m;l+=1)for(n=0;n<b.length;n+=1)l<q[n].length&&(f[u]=q[n][l],u+=1);g=f}b=g;f=
-1;k=d-1;m=7;p=0;a=y.F(a);for(q=d-1;0<q;q-=2)for(6==q&&--q;;){for(n=0;2>n;n+=1)null==r[k][q-n]&&(l=!1,p<b.length&&(l=1==(b[p]>>>m&1)),a(k,q-n)&&(l=!l),r[k][q-n]=l,--m,-1==m&&(p+=1,m=7));k+=f;if(0>k||d<=k){k-=f;f=-f;break}}}var h=A[a],r=null,d=0,g=null,x=[],u={u:function(b){b=w(b);x.push(b);g=null},a:function(b,a){if(0>b||d<=b||0>a||d<=a)throw Error(b+","+a);return r[b][a]},h:function(){return d},J:function(){for(var a=0,h=0,c=0;8>c;c+=1){b(!0,c);var d=y.D(u);if(0==c||a>d)a=d,h=c}b(!1,h)}};return u}
function z(c,a){if("undefined"==typeof c.length)throw Error(c.length+"/"+a);var e=function(){for(var b=0;b<c.length&&0==c[b];)b+=1;for(var r=Array(c.length-b+a),d=0;d<c.length-b;d+=1)r[d]=c[d+b];return r}(),b={c:function(b){return e[b]},b:function(){return e.length},multiply:function(a){for(var h=Array(b.b()+a.b()-1),c=0;c<b.b();c+=1)for(var g=0;g<a.b();g+=1)h[c+g]^=v.i(v.g(b.c(c))+v.g(a.c(g)));return z(h,0)},l:function(a){if(0>b.b()-a.b())return b;for(var c=v.g(b.c(0))-v.g(a.c(0)),h=Array(b.b()),
g=0;g<b.b();g+=1)h[g]=b.c(g);for(g=0;g<a.b();g+=1)h[g]^=v.i(v.g(a.c(g))+c);return z(h,0).l(a)}};return b}C.s=function(c){for(var a=[],e=0;e<c.length;e++){var b=c.charCodeAt(e);128>b?a.push(b):2048>b?a.push(192|b>>6,128|b&63):55296>b||57344<=b?a.push(224|b>>12,128|b>>6&63,128|b&63):(e++,b=65536+((b&1023)<<10|c.charCodeAt(e)&1023),a.push(240|b>>18,128|b>>12&63,128|b>>6&63,128|b&63))}return a};var A={L:1,M:0,Q:3,H:2},y=function(){function c(b){for(var a=0;0!=b;)a+=1,b>>>=1;return a}var a=[[],[6,18],
[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],
[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],e={w:function(b){for(var a=b<<10;0<=c(a)-c(1335);)a^=1335<<c(a)-c(1335);return(b<<10|a)^21522},A:function(b){for(var a=b<<12;0<=c(a)-c(7973);)a^=7973<<c(a)-c(7973);return b<<12|a},G:function(b){return a[b-1]},F:function(b){switch(b){case 0:return function(b,a){return 0==(b+a)%2};case 1:return function(b){return 0==b%2};case 2:return function(b,a){return 0==a%3};case 3:return function(b,a){return 0==
(b+a)%3};case 4:return function(b,a){return 0==(Math.floor(b/2)+Math.floor(a/3))%2};case 5:return function(b,a){return 0==b*a%2+b*a%3};case 6:return function(b,a){return 0==(b*a%2+b*a%3)%2};case 7:return function(b,a){return 0==(b*a%3+(b+a)%2)%2};default:throw Error("bad maskPattern:"+b);}},C:function(b){for(var a=z([1],0),c=0;c<b;c+=1)a=a.multiply(z([1,v.i(c)],0));return a},f:function(b,a){if(4!=b||1>a||40<a)throw Error("mode: "+b+"; type: "+a);return 10>a?8:16},D:function(b){for(var a=b.h(),c=0,
d=0;d<a;d+=1)for(var g=0;g<a;g+=1){for(var e=0,t=b.a(d,g),p=-1;1>=p;p+=1)if(!(0>d+p||a<=d+p))for(var q=-1;1>=q;q+=1)0>g+q||a<=g+q||(0!=p||0!=q)&&t==b.a(d+p,g+q)&&(e+=1);5<e&&(c+=3+e-5)}for(d=0;d<a-1;d+=1)for(g=0;g<a-1;g+=1)if(e=0,b.a(d,g)&&(e+=1),b.a(d+1,g)&&(e+=1),b.a(d,g+1)&&(e+=1),b.a(d+1,g+1)&&(e+=1),0==e||4==e)c+=3;for(d=0;d<a;d+=1)for(g=0;g<a-6;g+=1)b.a(d,g)&&!b.a(d,g+1)&&b.a(d,g+2)&&b.a(d,g+3)&&b.a(d,g+4)&&!b.a(d,g+5)&&b.a(d,g+6)&&(c+=40);for(g=0;g<a;g+=1)for(d=0;d<a-6;d+=1)b.a(d,g)&&!b.a(d+
1,g)&&b.a(d+2,g)&&b.a(d+3,g)&&b.a(d+4,g)&&!b.a(d+5,g)&&b.a(d+6,g)&&(c+=40);for(g=e=0;g<a;g+=1)for(d=0;d<a;d+=1)b.a(d,g)&&(e+=1);return c+=Math.abs(100*e/a/a-50)/5*10}};return e}(),v=function(){for(var c=Array(256),a=Array(256),e=0;8>e;e+=1)c[e]=1<<e;for(e=8;256>e;e+=1)c[e]=c[e-4]^c[e-5]^c[e-6]^c[e-8];for(e=0;255>e;e+=1)a[c[e]]=e;return{g:function(b){if(1>b)throw Error("glog("+b+")");return a[b]},i:function(b){for(;0>b;)b+=255;for(;256<=b;)b-=255;return c[b]}}}(),t=function(){function c(b,c){switch(c){case A.L:return a[4*
(b-1)];case A.M:return a[4*(b-1)+1];case A.Q:return a[4*(b-1)+2];case A.H:return a[4*(b-1)+3]}}var a=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,
2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12,7,37,13],[5,122,98,1,123,99],[7,73,
45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,
151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],
[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],
[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],e={I:function(b,a){var e=c(b,a);if("undefined"==
typeof e)throw Error("bad rs block @ typeNumber:"+b+"/errorCorrectLevel:"+a);b=e.length/3;a=[];for(var d=0;d<b;d+=1)for(var g=e[3*d],h=e[3*d+1],t=e[3*d+2],p=0;p<g;p+=1){var q=t,f={};f.o=h;f.j=q;a.push(f)}return a}};return e}();return C}());
//# sourceMappingURL=/js/external/qr-creator.min.js.map

File diff suppressed because one or more lines are too long

View file

@ -1,25 +1,14 @@
const NOTIFICATIONS = {
_notifications: [],
send: function (message, options = {}) {
if (!Notification || Notification.permission !== "granted") {
return;
}
const notification = new Notification(message, options);
this._notifications.push( notification );
return notification;
},
close: function() {
for ( const notification of this._notifications ) {
notification?.close();
}
this._notifications.length = 0;
},
request_permission: function () {
request_permissions: function () {
if (Notification && Notification.permission === "granted") {
return;
}

View file

@ -1,4 +1,3 @@
<script src="/js/external/qr-creator.min.js"></script>
<script>
function clear_invite_popup() {
document.body.querySelectorAll(".invitepopover").forEach((element) => element.remove());
@ -20,9 +19,8 @@
</form>`;
document.body.appendChild(invite_div);
const document_width = document.body.getBoundingClientRect().width;
invite_div.style.left = document_width > 800 ? button.getBoundingClientRect().left + "px" : '1rem';
invite_div.style.top = document_width > 800 ? button.getBoundingClientRect().top + "px" : '1rem';
invite_div.style.left = button.getBoundingClientRect().left + "px";
invite_div.style.top = button.getBoundingClientRect().top + "px";
}
async function create_invite(click_event) {
@ -57,8 +55,6 @@
}
const invite_code = await invite_code_response.json();
const invite_url = `${window.location.protocol}//${window.location.host}/?invite_code=${ encodeURIComponent(invite_code.code) }`;
const qr_div_id = 'qr-div-' + Math.random().toString(36).substring(2, 8);
invite_popover.innerHTML = `
<div>
@ -70,24 +66,11 @@
</div>
<div class="share-option">
<span class="name">Link</span>
<input readonly type="text" name="code" value="${ invite_url }" />
<button onclick="navigator.clipboard.writeText('${ invite_url }');" />Copy</button>
</div>
<div class="share-option">
<span class="name">Scan</span>
<div id="${ qr_div_id }" class="qr-invite-container"></div>
<input readonly type="text" name="code" value="${window.location.protocol + "//" + window.location.host + "/?invite_code=" + encodeURIComponent(invite_code.code)}" />
<button onclick="navigator.clipboard.writeText('${window.location.protocol + "//" + window.location.host + "/?invite_code=" + encodeURIComponent(invite_code.code)}');" />Copy</button>
</div>
<button onclick="( () => document.querySelectorAll( '.invitepopover' ).forEach( (element) => element.remove() ) )()">Done</button>
</div>`;
QrCreator.render({
text: invite_url,
radius: 0.5, // 0.0 to 0.5
ecLevel: 'H', // L, M, Q, H
fill: '#999999', // foreground color
background: null, // color or null for transparent
size: 256 // in pixels
}, document.querySelector(`#${ qr_div_id }`));
}
</script>
@ -104,11 +87,6 @@
border-right: 1px solid var(--border-subtle);
}
#sidebar .profile-container {
width: 100%;
max-width: 100%;
}
#sidebar #sidebar-context-menu {
display: none;
visibility: hidden;
@ -255,7 +233,7 @@
.invitepopover .share-option input {
padding: 0.75rem;
margin: 0 0.75rem 1rem 0;
margin: 0 1rem 1rem 0;
background: none;
color: var(--text);
border: 1px solid var(--border-highlight);
@ -274,12 +252,6 @@
margin-left: -0.75rem;
}
.invitepopover .qr-invite-container {
width: 256px;
height: 256px;
margin: 1rem auto;
}
@media screen and (max-width: 1200px) {
.invitepopover {
margin: 0;
@ -292,8 +264,9 @@
margin-bottom: 1rem;
}
.invitepopover .share-option input {
max-width: 14rem;
.invitepopover .share-option button {
display: block;
margin: 0 auto 1rem;
}
}
</style>
@ -620,7 +593,7 @@
</summary>
<div class="notifications-settings-container">
<button onclick="NOTIFICATIONS.request_permission()">
<button class="mockup" onclick="NOTIFICATIONS.request_permission()">
Enable Notifications
</button>
</div>

View file

@ -5,7 +5,7 @@
flex-direction: column;
align-items: center;
justify-content: center;
position: fixed;
position: absolute;
top: 0;
left: 0;
right: 0;
@ -14,53 +14,15 @@
background: var(--bg);
visibility: visible;
opacity: 1;
transition: all 0.33s;
}
#auth-container {
width: 95%;
max-height: calc(min(90vh,900px));
border-radius: 10px;
position: relative;
background: var(--bg-lighter);
max-width: 40em;
margin: 0 auto;
border-radius: calc( var(--border-radius) * 2.5);
padding: 2em 1em;
overflow-y: scroll;
transition: all 0.33s ease;
animation: zoomsettle 0.4s ease;
#login-tab .tab-content {
min-height: 17rem;
}
#signup-tab,
#login-tab {
padding: 0.5em;
height: auto;
}
@media screen and (max-width: 480px) {
#auth-container {
padding: 0.5em;
}
#auth-container h1 {
font-size: x-large;
}
#auth-container h3 {
font-size: medium;
}
}
@keyframes zoomsettle {
from {
opacity: 0;
transform: scale(1.2);
}
to {
opacity: 1;
transform: scale(1.0);
}
#signup-tab .tab-content {
min-height: 21rem;
}
body[data-user] #signup-login-wall {
@ -69,27 +31,74 @@
display: none;
}
#signup-login-wall .limiter {
width: 95%;
min-height: 24rem;
position: relative;
background: hsl(from var(--bg) h s calc(l/1.1));
max-width: 40em;
margin: 0 auto;
border-radius: var(--border-radius);
overflow: hidden;
}
#signup-login-wall form {
width: 100%;
padding: 0.5rem 1.5rem 0;
padding: 1.5rem 1.5rem 0 1.5rem;
}
</style>
<div id="auth-container" class="limiter">
<!-- #include "./files/settings/signup_pitch.html" or "./files/settings/signup_pitch.md" or "./signup_pitch.default.md" -->
<!-- #include file="./signup_pitch.md" -->
<div class="limiter">
<div class="tabs">
<div id="login-tab" class="tab">
<input
type="radio"
name="signup-login-tabs"
id="login-tab-input"
class="tab-switch"
checked="checked"
/>
<label for="login-tab-input" class="tab-label">
<div class="label">Log In</div>
</label>
<div class="tab-content">
<form data-smart="true" data-method="POST" id="login-form" action="/api/auth">
<script>
{
const form = document.currentScript.closest("form");
form.on_reply = (response) => {
const user = response.user;
APP.login( user );
};
}
</script>
<div>
<input id="login-username" type="text" name="username" required />
<label class="placeholder" for="login-username">username</label>
</div>
<div>
<input id="login-password" type="password" name="password" required />
<label class="placeholder" for="login-password">password</label>
</div>
<div>
<button id="login-submit" type="submit" class="primary">Log In</button>
</div>
</form>
</div>
</div>
<div id="signup-tab" class="tab">
<input
type="radio"
name="signup-login-tabs"
id="signup-tab-input"
class="tabswitch"
checked="checked"
class="tab-switch"
/>
<label for="signup-tab-input" class="tablabel">
<label for="signup-tab-input" class="tab-label">
<div class="label">Sign Up</div>
</label>
<div id="signup-tab" class="panel">
<div class="tab-content">
<form data-smart="true" data-method="POST" id="signup-form" action="/api/users">
<script>
{
@ -126,59 +135,13 @@
id="signup-invite-code"
type="text"
name="invite_code"
required
/>
<label class="placeholder" for="signup-invite-code">invite code</label>
</div>
<button id="signup-submit" type="submit" class="primary">Sign Up</button>
</form>
</div>
<input
type="radio"
name="signup-login-tabs"
id="login-tab-input"
class="tabswitch"
/>
<label for="login-tab-input" class="tablabel">
<div class="label">Log In</div>
</label>
<div id="login-tab" class="panel">
<form data-smart="true" data-method="POST" id="login-form" action="/api/auth">
<script>
{
const form = document.currentScript.closest("form");
form.on_reply = (response) => {
const user = response.user;
APP.login( user );
};
}
</script>
<div>
<input id="login-username" type="text" name="username" required />
<label class="placeholder" for="login-username">username</label>
</div>
<div>
<input id="login-password" type="password" name="password" required />
<label class="placeholder" for="login-password">password</label>
</div>
<div>
<button id="login-submit" type="submit" class="primary">Log In</button>
</div>
</form>
</div>
</div>
</div>
<script>
document.addEventListener( 'DOMContentLoaded', () => {
const authed_before = (document.cookie.match(
/^(?:.*;)?\s*authed_before\s*=\s*([^;]+)(?:.*)?$/,
) || [, null])[1];
if ( authed_before ) {
document.getElementById("login-tab-input").checked = true;
}
});
</script>
</div>

View file

@ -1,6 +1,6 @@
# Welcome!
# verifiedhuman.network
## You're here because someone else thought you needed an invite.
## You're here because someone else said you were a cool human.
### Use your invite code to gain access.

View file

@ -127,22 +127,23 @@
}
</style>
<input
<div id="blurbs" class="tab">
<input
type="radio"
name="top-level-tabs"
id="blurb-tab-input"
class="tabswitch"
class="tab-switch"
data-view="blurbs"
/>
<label for="blurb-tab-input" class="tablabel"
/>
<label for="blurb-tab-input" class="tab-label"
><div class="icon blurb"></div>
<div class="label">Blurbs</div>
</label>
<div class="panel">
</label>
<div class="tab-content">
<div id="blurbs-container" class="container">
<!-- #include "./README.md" -->
<!-- #include file="./README.md" -->
<!-- #include "./new_blurb.html" -->
<!-- #include file="./new_blurb.html" -->
<div
id="blurbs-list"
@ -226,7 +227,7 @@
<div class="content-container">${htmlify(md_to_html(context.blurb.data.blurb))}</div>
<div class="reactions-container"></div>
<button class="icon more" commandfor="eventactionspopover"></button>
<!-- #include "./new_blurb.html" -->
<!-- #include file="./new_blurb.html" -->
<div class="replies-container"></div>
</div>
</template>
@ -243,4 +244,5 @@
</template>
</div>
</div>
</div>
</div>

View file

@ -1,14 +1,16 @@
<input
<div id="calendar" class="tab">
<input
type="radio"
name="top-level-tabs"
id="calendar-tab-input"
class="tabswitch"
class="tab-switch"
data-view="calendar"
/>
<label for="calendar-tab-input" class="tablabel mockup"
/>
<label for="calendar-tab-input" class="tab-label mockup"
><div class="icon calendar"></div>
<div class="label">Calendar</div></label
>
<div class="panel">
<!-- #include "./README.md" -->
>
<div class="tab-content">
<!-- #include file="./README.md" -->
</div>
</div>

View file

@ -1,53 +1,13 @@
<script>
function on_channels_updated({ channels }) {
APP.on("channels_updated", ({ channels }) => {
const channel_list = document.getElementById("channel-list");
if ( !channel_list ) {
setTimeout( () => {
on_channels_updated( { channels } );
}, 100 );
return;
}
channel_list.innerHTML = "";
for (const channel of channels.sort((lhs, rhs) => lhs.name.localeCompare(rhs.name))) {
if ( !document.body.dataset.channel ) {
document.body.dataset.channel = APP.user?.meta?.chat?.last_channel ?? channel.id;
if ( APP.view === 'chat' ) {
window.location.hash = '/chat/channel/' + document.body.dataset.channel;
}
}
channel_list.insertAdjacentHTML(
"beforeend",
`<li id="channel-selector-${channel.id}" class="channel" data-channel-selector-for="${channel.id}"><a href="#/chat/channel/${channel.id}">${channel.name}</a></li>`,
`<li id="channel-selector-${channel.id}" class="channel" data-channel-selector-for="${channel.id}"><a href="#/channel/${channel.id}/chat">${channel.name}</a></li>`,
);
}
}
APP.on("channels_updated", on_channels_updated );
APP.on( 'view_changed', async ( { previous, view, channel_id } ) => {
if ( view !== 'chat' ) {
return;
}
const previous_channel = typeof document.body.dataset.channel === 'string' ? document.body.dataset.channel : undefined;
if ( channel_id ) {
document.body.dataset.channel = channel_id;
}
else {
delete document.body.dataset.channel;
}
await APP.CHANNELS.update(); // don't force, but ensure we have channels
const target_channel_id = channel_id ?? APP.CHANNELS.CHANNEL_LIST[0]?.id;
const hash_target = `/chat` + ( target_channel_id ? `/channel/${ target_channel_id }` : '' );
if ( window.location.hash?.slice( 1 ) !== hash_target ) {
window.location.hash = hash_target;
}
});
function update_channel_indicators(event) {
@ -117,25 +77,20 @@
}
});
APP.on( 'view_changed', ( {previous, view} ) => {
APP.on( 'view_changed', ( {view} ) => {
if ( !view === 'chat' ) {
return;
}
const sidebar_dynamic_container = document.getElementById( 'sidebar-dynamic-container');
if ( !sidebar_dynamic_container ) {
console.error( 'could not get #sidebar-dynamic-container' );
return;
}
if ( view !== 'chat' && previous === 'chat' ) {
sidebar_dynamic_container.innerHTML = '';
delete document.body.dataset.channel;
return;
}
else if ( view !== 'chat' ) {
return;
}
const template = document.getElementById( 'channel-list-template');
sidebar_dynamic_container.innerHTML = template.innerHTML.trim();
APP.CHANNELS.update(true);
APP.CHANNELS.update();
});
</script>
<style>

View file

@ -1,10 +1,10 @@
#chat-container {
#chat #chat-container {
position: relative;
width: 100%;
height: 100%;
}
#chat-container #chat-content {
#chat #chat-content {
overflow-y: scroll;
scroll-behavior: smooth;
position: absolute;
@ -14,14 +14,13 @@
bottom: 5rem;
padding: 1.5rem 1.5rem 0.75rem 1.5rem;
}
@media screen and (max-width: 1200px) {
#chat-container #chat-content {
#chat #chat-content {
padding: 0.75rem;
}
}
#chat-container #chat-entry-container {
#chat #chat-entry-container {
position: absolute;
bottom: 0;
left: 0;
@ -30,18 +29,18 @@
padding: 1rem;
}
#chat-container #chat-entry-container form {
#chat #chat-entry-container form {
display: flex;
flex-direction: row;
}
#chat-container #chat-entry-container form input[type="file"] {
#chat #chat-entry-container form input[type="file"] {
opacity: 0;
display: none;
}
#chat-container #chat-entry-container form button,
#chat-container #chat-entry-container form label {
#chat #chat-entry-container form button,
#chat #chat-entry-container form label {
position: relative;
top: inherit;
font-size: inherit;
@ -55,12 +54,12 @@
border: 1px solid rgba(128, 128, 128, 0.2);
}
#chat-container #chat-entry-container form textarea {
#chat #chat-entry-container form textarea {
flex-grow: 1;
resize: none;
}
#chat-container .message-container {
#chat .message-container {
position: relative;
transition: all 0.33s;
background: var(--bg-lighter);
@ -69,22 +68,22 @@
border-radius: var(--border-radius);
}
#chat-container .message-container .html-from-markdown {
#chat .message-container .html-from-markdown {
padding: 0;
}
#chat-container .user-tock.time-tock:has(+ .user-tock.time-tick),
#chat-container .user-tock.time-tick:has(+ .user-tock.time-tock),
#chat-container .user-tick.time-tock:has(+ .user-tick.time-tick),
#chat-container .user-tick.time-tick:has(+ .user-tick.time-tock),
#chat-container .user-tock.time-tock:has(+ .user-tick.time-tick),
#chat-container .user-tock.time-tick:has(+ .user-tick.time-tock),
#chat-container .user-tick.time-tock:has(+ .user-tock.time-tick),
#chat-container .user-tick.time-tick:has(+ .user-tock.time-tock),
#chat-container .user-tock.time-tock:last-of-type,
#chat-container .user-tock.time-tick:last-of-type,
#chat-container .user-tick.time-tock:last-of-type,
#chat-container .user-tick.time-tick:last-of-type {
.user-tock.time-tock:has(+ .user-tock.time-tick),
.user-tock.time-tick:has(+ .user-tock.time-tock),
.user-tick.time-tock:has(+ .user-tick.time-tick),
.user-tick.time-tick:has(+ .user-tick.time-tock),
.user-tock.time-tock:has(+ .user-tick.time-tick),
.user-tock.time-tick:has(+ .user-tick.time-tock),
.user-tick.time-tock:has(+ .user-tock.time-tick),
.user-tick.time-tick:has(+ .user-tock.time-tock),
.user-tock.time-tock:last-of-type,
.user-tock.time-tick:last-of-type,
.user-tick.time-tock:last-of-type,
.user-tick.time-tick:last-of-type {
/* border: 1px dotted red; */
margin-bottom: 1rem !important;
padding-bottom: 0.25rem !important;
@ -92,42 +91,54 @@
margin-top: 0.5rem !important;
}
#chat-container .user-tock.time-tock:has(+ .user-tock.time-tock),
#chat-container .user-tock.time-tick:has(+ .user-tock.time-tick),
#chat-container .user-tick.time-tock:has(+ .user-tick.time-tock),
#chat-container .user-tick.time-tick:has(+ .user-tick.time-tick) {
.user-tock.time-tock:has(+ .user-tock.time-tock),
.user-tock.time-tick:has(+ .user-tock.time-tick),
.user-tick.time-tock:has(+ .user-tick.time-tock),
.user-tick.time-tick:has(+ .user-tick.time-tick) {
/* border: 1px dotted blue; */
margin-bottom: -0.75rem !important;
padding-top: 0 !important;
}
#chat-container .user-tock.time-tock+.user-tock.time-tick:has(+ .user-tock.time-tick),
#chat-container .user-tock.time-tick+.user-tock.time-tock:has(+ .user-tock.time-tock),
#chat-container .user-tick.time-tock+.user-tick.time-tick:has(+ .user-tick.time-tick),
#chat-container .user-tick.time-tick+.user-tick.time-tock:has(+ .user-tick.time-tock),
#chat-container .user-tock.time-tock+.user-tick.time-tick:has(+ .user-tick.time-tick),
#chat-container .user-tock.time-tick+.user-tick.time-tock:has(+ .user-tick.time-tock),
#chat-container .user-tick.time-tock+.user-tock.time-tick:has(+ .user-tock.time-tick),
#chat-container .user-tick.time-tick+.user-tock.time-tock:has(+ .user-tock.time-tock) {
.user-tock.time-tock + .user-tock.time-tick:has(+ .user-tock.time-tick),
.user-tock.time-tick + .user-tock.time-tock:has(+ .user-tock.time-tock),
.user-tick.time-tock + .user-tick.time-tick:has(+ .user-tick.time-tick),
.user-tick.time-tick + .user-tick.time-tock:has(+ .user-tick.time-tock),
.user-tock.time-tock + .user-tick.time-tick:has(+ .user-tick.time-tick),
.user-tock.time-tick + .user-tick.time-tock:has(+ .user-tick.time-tock),
.user-tick.time-tock + .user-tock.time-tick:has(+ .user-tock.time-tick),
.user-tick.time-tick + .user-tock.time-tock:has(+ .user-tock.time-tock) {
/* border: 1px dotted green; */
margin-bottom: -0.75rem !important;
}
#chat-container .message-container.user-tick.time-tick+.message-container.user-tick.time-tick .info-container,
#chat-container .message-container.user-tick.time-tock+.message-container.user-tick.time-tock .info-container,
#chat-container .message-container.user-tock.time-tick+.message-container.user-tock.time-tick .info-container,
#chat-container .message-container.user-tock.time-tock+.message-container.user-tock.time-tock .info-container {
#chat
.message-container.user-tick.time-tick
+ .message-container.user-tick.time-tick
.info-container,
#chat
.message-container.user-tick.time-tock
+ .message-container.user-tick.time-tock
.info-container,
#chat
.message-container.user-tock.time-tick
+ .message-container.user-tock.time-tick
.info-container,
#chat
.message-container.user-tock.time-tock
+ .message-container.user-tock.time-tock
.info-container {
opacity: 0;
visibility: hidden;
height: 0;
margin: 0;
}
#chat-container .message-container.sending {
#chat .message-container.sending {
opacity: 0.75;
}
#chat-container .message-container button[commandfor] {
#chat .message-container button[commandfor] {
position: absolute;
top: 0.1rem;
right: 0.1rem;
@ -136,13 +147,13 @@
z-index: 10;
}
#chat-container .message-container .message-actions-container:has(input[type="checkbox"]:checked) {
#chat .message-container .message-actions-container:has(input[type="checkbox"]:checked) {
background: rgb(from var(--bg) r g b / 0.9);
border-color: var(--border-subtle);
z-index: 11;
}
#chat-container .message-container .message-actions-container .message-action {
#chat .message-container .message-actions-container .message-action {
border: none;
opacity: 0;
transition: 0.2s ease-in-out;
@ -158,75 +169,79 @@
background: none;
}
#chat-container .message-container .message-actions-container label {
#chat .message-container .message-actions-container label {
cursor: pointer;
width: 2rem;
text-align: right;
padding-top: 0.5rem;
}
#chat-container .message-container .message-actions-container input[type="checkbox"] {
#chat .message-container .message-actions-container input[type="checkbox"] {
opacity: 0;
display: none;
}
#chat-container .message-container .message-actions-container input[type="checkbox"]:checked~.message-action {
#chat
.message-container
.message-actions-container
input[type="checkbox"]:checked
~ .message-action {
opacity: 1;
width: 3.25rem;
margin-right: 1.25rem;
}
#chat-container .message-container .message-actions-container .message-action .action-name {
#chat .message-container .message-actions-container .message-action .action-name {
font-size: x-small;
}
#chat-container .message-container .info-container {
#chat .message-container .info-container {
display: flex;
margin-bottom: -1.75rem;
height: 3.75rem;
}
#chat-container .message-container .info-container .username-container {
#chat .message-container .info-container .username-container {
margin: 0 4px;
font-weight: bold;
padding-top: 0.5rem;
}
#chat-container .message-container .info-container .datetime-container {
#chat .message-container .info-container .datetime-container {
margin: 0 4px;
padding-top: 0.4rem;
}
#chat-container .message-container .info-container .datetime-container .long {
#chat .message-container .info-container .datetime-container .long {
font-size: x-small;
text-transform: uppercase;
}
#chat-container .message-container .info-container .datetime-container .short {
#chat .message-container .info-container .datetime-container .short {
font-size: xx-small;
visibility: hidden;
display: none;
}
#chat-container .message-container .message-content-container,
#chat-container .message-container .message-media-container,
#chat-container .message-container .reactions-container {
#chat .message-container .message-content-container,
#chat .message-container .message-media-container,
#chat .message-container .reactions-container {
padding-left: 8rem;
overflow-x: auto;
}
#chat-container .message-container .reactions-container:has(> .reaction-container) {
#chat .message-container .reactions-container:has(> .reaction-container) {
margin-top: 0.5rem;
margin-bottom: 0.25rem;
}
#chat-container .embed-container {
#chat .embed-container {
position: relative;
width: 100%;
max-width: 640px;
}
#chat-container .embed-container .embed-actions-container {
#chat .embed-container .embed-actions-container {
position: absolute;
z-index: 100;
top: 0.25rem;
@ -238,31 +253,31 @@
opacity: 0;
}
#chat-container .embed-container audio {
#chat .embed-container audio {
width: 100%;
}
#chat-container .embed-container.rounded {
#chat .embed-container.rounded {
border-radius: 6px;
}
#chat-container .embed-container.short {
#chat .embed-container.short {
height: 120px;
overflow: hidden;
overflow-y: auto;
}
#chat-container .embed-container.tidal {
#chat .embed-container.tidal {
border-radius: 12px;
}
#chat-container .embed-container.vertical {
#chat .embed-container.vertical {
max-width: 320px;
overflow: hidden;
aspect-ratio: 9 / 16 !important;
}
#chat-container .embed-container.letterbox {
#chat .embed-container.letterbox {
/* height: 0; */
overflow: hidden;
overflow-y: auto;
@ -270,14 +285,14 @@
aspect-ratio: 16 / 9 !important;
}
#chat-container .embed-container.square {
#chat .embed-container.square {
overflow: hidden;
overflow-y: auto;
aspect-ratio: 1 / 1 !important;
}
#chat-container .embed-container iframe,
#chat-container .embed-container video {
#chat .embed-container iframe,
#chat .embed-container video {
position: absolute;
top: 0;
left: 0;
@ -290,7 +305,7 @@
}
@media screen and (max-width: 1200px) {
#chat-container #chat-container {
#chat #chat-container {
position: absolute;
top: 0;
left: 0;
@ -298,28 +313,28 @@
bottom: 0;
}
#chat-container #chat-container #chat-entry-container,
#chat-container #chat-container #chat-entry-container form {
#chat #chat-container #chat-entry-container,
#chat #chat-container #chat-entry-container form {
padding: 0.25rem;
}
#chat-container #chat-container #chat-entry-container form button,
#chat-container #chat-container #chat-entry-container form label {
#chat #chat-container #chat-entry-container form button,
#chat #chat-container #chat-entry-container form label {
margin: 0 0.5rem;
}
#chat-container .message-container .message-content-container,
#chat-container .message-container .message-media-container,
#chat-container .message-container .reactions-container {
#chat .message-container .message-content-container,
#chat .message-container .message-media-container,
#chat .message-container .reactions-container {
padding-left: 4rem;
}
#chat-container .message-container .info-container .datetime-container .long {
#chat .message-container .info-container .datetime-container .long {
display: none;
visibility: hidden;
}
#chat-container .message-container .info-container .datetime-container .short {
#chat .message-container .info-container .datetime-container .short {
display: inline-block;
visibility: visible;
}

View file

@ -1,17 +1,18 @@
<input
<div id="chat" class="tab">
<input
type="radio"
name="top-level-tabs"
id="chat-tab-input"
class="tabswitch"
class="tab-switch"
data-view="chat"
/>
<label for="chat-tab-input" class="tablabel"
/>
<label for="chat-tab-input" class="tab-label"
><div class="icon chat"></div>
<div class="label">Chat</div>
</label>
<div class="panel">
</label>
<div class="tab-content">
<style>
<!-- #include "./chat.css" -->
<!-- #include file="./chat.css" -->
</style>
<script src="/js/external/mimetypes.js" type="text/javascript"></script>
<script src="/js/external/punycode.js" type="text/javascript"></script>
@ -229,5 +230,6 @@
</form>
</div>
</div>
</div>
</div>
<!-- #include "./channel_sidebar.html" -->
<!-- #include file="./channel_sidebar.html" -->

View file

@ -14,7 +14,6 @@
"preview"
"title"
"info"
"reactions"
"content"
"newessay"
"replies";
@ -72,10 +71,6 @@
font-size: xx-large;
}
.essay-container .reactions-container {
grid-area: reactions;
}
.essay-container .content-container {
grid-area: content;
padding-left: 0.25rem;
@ -83,10 +78,6 @@
font-size: large;
}
.essay-container .content-container .html-from-markdown {
padding: 1em;
}
.essay-container button[commandfor="eventactionspopover"] {
position: absolute;
bottom: 0.25rem;
@ -104,21 +95,22 @@
}
</style>
<input
<div id="essays" class="tab">
<input
type="radio"
name="top-level-tabs"
id="essay-tab-input"
class="tabswitch"
class="tab-switch"
data-view="essays"
/>
<label for="essay-tab-input" class="tablabel"
/>
<label for="essay-tab-input" class="tab-label"
><div class="icon essay"></div>
<div class="label">Essays</div>
</label>
<div class="panel">
</label>
<div class="tab-content">
<div id="essays-container" class="container">
<!-- #include "./README.md" -->
<!-- #include "./new_essay.html" -->
<!-- #include file="./README.md" -->
<!-- #include file="./new_essay.html" -->
<div
id="essays-list"
@ -198,11 +190,11 @@
<span class="short">${context.essay_datetime.short}</span>
</div>
</div>
<div class="reactions-container"></div>
<div class="title-container">${context.essay.data.title}</div>
<div class="content-container">
${htmlify(md_to_html(context.essay.data.essay))}
</div>
<div class="reactions-container"></div>
<button class="icon more" commandfor="eventactionspopover"></button>
</div>
</template>
@ -219,4 +211,5 @@
</template>
</div>
</div>
</div>
</div>

View file

@ -1,14 +1,16 @@
<input
<div id="exchange" class="tab">
<input
type="radio"
name="top-level-tabs"
id="exchange-tab-input"
class="tabswitch"
class="tab-switch"
data-view="exchange"
/>
<label for="exchange-tab-input" class="tablabel"
/>
<label for="exchange-tab-input" class="tab-label"
><div class="icon exchange"></div>
<div class="label">Exchange</div></label
>
<div class="panel">
<!-- #include "./README.md" -->
>
<div class="tab-content">
<!-- #include file="./README.md" -->
</div>
</div>

View file

@ -11,7 +11,6 @@
grid-template-areas:
"expander preview info"
"expander preview subject"
". . reactions"
". . content"
". . newpost"
". . replies";
@ -112,10 +111,6 @@
margin-top: 2rem;
}
.post-container .reactions-container {
grid-area: reactions;
}
.post-container button[commandfor="eventactionspopover"] {
position: absolute;
bottom: 0.25rem;
@ -130,50 +125,22 @@
.post-container .replies-container {
grid-area: replies;
}
@media screen and (max-width: 480px) {
.post-container {
grid-template-rows: 120px auto auto auto 1fr;
grid-template-columns: auto 1fr;
grid-template-areas:
". preview"
"expander info"
"expander subject"
". reactions"
". content"
". newpost"
". replies";
max-height: 16rem;
}
.post-container .media-preview-container {
overflow: hidden;
display: grid;
align-content: center;
width: 100%;
height: 7rem;
}
.post-container .media-preview-container img {
object-fit: cover;
}
}
</style>
<input
<div id="forum" class="tab">
<input
type="radio"
name="top-level-tabs"
id="forum-tab-input"
class="tabswitch"
class="tab-switch"
data-view="forum"
/>
<label for="forum-tab-input" class="tablabel"
/>
<label for="forum-tab-input" class="tab-label"
><div class="icon forum"></div>
<div class="label">Forum</div></label
>
<div class="panel forum-container">
<!-- #include "./README.md" -->
>
<div class="tab-content forum-container">
<!-- #include file="./README.md" -->
<div
id="posts-list"
@ -267,7 +234,7 @@
</div>
<div class="reactions-container"></div>
<button class="icon more" commandfor="eventactionspopover"></button>
<!-- #include "./new_post.html" -->
<!-- #include file="./new_post.html" -->
<div class="replies-container"></div>
</div>
</template>
@ -284,5 +251,6 @@
</template>
</div>
<!-- #include "./new_post.html" -->
<!-- #include file="./new_post.html" -->
</div>
</div>

View file

@ -1,15 +1,17 @@
<input
<div id="home" class="tab">
<input
type="radio"
name="top-level-tabs"
id="home-tab-input"
checked="checked"
class="tabswitch"
class="tab-switch"
data-view="home"
/>
<label for="home-tab-input" class="tablabel">
/>
<label for="home-tab-input" class="tab-label">
<div class="icon home"></div>
<div class="label">Home</div>
</label>
<div class="panel">
<!-- #include "./README.md" -->
</label>
<div class="tab-content">
<!-- #include file="./README.md" -->
</div>
</div>

View file

@ -1,17 +1,18 @@
<script></script>
<style></style>
<input
<div id="live-tab" class="tab">
<input
type="radio"
name="top-level-tabs"
id="live-tab-input"
class="tabswitch"
class="tab-switch"
data-view="profile"
/>
<label for="live-tab-input" class="tablabel mockup"
/>
<label for="live-tab-input" class="tab-label mockup"
><div class="icon live"></div>
<div class="label">Live</div></label
>
<div class="panel">
>
<div class="tab-content">
<div
class="live-container"
style="
@ -68,4 +69,5 @@
</label>
</div>
</div>
</div>
</div>

View file

@ -1,54 +0,0 @@
<link rel="stylesheet" href="/js/external/leaflet/leaflet.css"/>
<script src="/js/external/leaflet/leaflet.js"></script>
<script type="module">
var map = L.map('map').setView([33.88,-118.13], 13);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
APP.on( "view_changed", ({ view }) => {
if ( view === 'map' ) {
map.invalidateSize();
}
});
</script>
<style>
#map-tab .panel {
overflow: hidden;
}
#map {
position: absolute;
top: 4em;
left: 4em;
right: 4em;
bottom: 4em;
z-index: 0;
}
@media screen and (max-width: 640px) {
#map {
top: 1em;
left: 1em;
right: 1em;
bottom: 1em;
}
}
</style>
<input
type="radio"
name="top-level-tabs"
id="map-tab-tab-input"
class="tabswitch"
data-view="map"
/>
<label for="map-tab-tab-input" class="tablabel"
><div class="icon map-pin"></div>
<div class="label">Map</div>
</label>
<div class="panel">
<div id="map"></div>
</div>

View file

@ -1,12 +1,14 @@
<input
<div id="resources" class="tab">
<input
type="radio"
name="top-level-tabs"
id="resources-tab-input"
class="tabswitch"
class="tab-switch"
data-view="resources"
/>
<label for="resources-tab-input" class="tablabel mockup"
/>
<label for="resources-tab-input" class="tab-label mockup"
><div class="icon resources"></div>
<div class="label">Resources</div></label
>
<div class="panel"><!-- #include "./README.md" --></div>
>
<div class="tab-content"><!-- #include file="./README.md" --></div>
</div>

View file

@ -1,6 +1,6 @@
<script>
APP.on( "view_changed", ({ view }) => {
const target_tab = document.querySelector(`.tabswitch[data-view="${view}"]`);
const target_tab = document.querySelector(`.tab-switch[data-view="${view}"]`);
if (target_tab) {
target_tab.click();
@ -8,67 +8,130 @@
});
APP.on( 'load', () => {
console.log( 'loaded' );
const tab_switchers = document.querySelectorAll(".tabswitch");
const tab_switchers = document.querySelectorAll(".tab-switch");
for (const tab_switch of tab_switchers) {
tab_switch.addEventListener("input", (event) => {
const tab_selector = event.target;
const view = tab_selector.dataset.view;
if (view) {
window.location.hash = `/${view}`;
window.location.hash = `/${view}${ document.body.dataset.channel ? `/channel/${ document.body.dataset.channel }` : '' }`;
}
});
}
});
</script>
<style>
:root {
--tab-label-height: 4em;
}
.tabs {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
}
.tabs .tabswitch {
position: absolute;
opacity: 0;
}
.tabs .tablabel {
cursor: pointer;
border: none;
text-align: center;
padding: 1em;
height: var(--tab-label-height);
line-height: 2em;
}
.tabs .tabswitch:checked + .tablabel {
border-bottom: 1px solid var(--accent);
}
.tabs .panel {
display: none;
.tabs-container {
position: relative;
padding: 1em;
width: 100%;
height: calc( 100vh - var(--tab-label-height) );
order: 99;
}
height: 100%;
}
.tabs .tabswitch:checked + .tablabel + .panel {
.tabs {
position: relative;
height: 100%;
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: flex-start;
min-height: inherit;
background: inherit;
}
.tabs::before,
.tabs::after {
content: "";
display: table;
}
.tabs::after {
clear: both;
}
.tab {
background: inherit;
}
.tab-switch {
display: none;
}
.tab-label {
position: relative;
width: 6rem;
height: 5rem;
cursor: pointer;
transition: all 0.25s;
display: flex;
align-items: center;
justify-content: end;
flex-direction: column;
}
.tab-label .label {
margin: 0.75rem 0;
}
.tab-content {
position: absolute;
z-index: 1;
top: 5rem;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
transition: all 0.35s;
border-top: 1px solid var(--border-subtle);
margin-top: 1px;
overflow-y: scroll;
visibility: hidden;
display: none;
background: inherit;
}
.tab-switch,
.tab-switch + .tab-label {
transition: none;
}
.tab-switch:checked + .tab-label {
margin-top: 1px;
border-bottom: 1px solid var(--accent);
z-index: 1;
}
.tab-switch:checked + label + .tab-content {
z-index: 2;
opacity: 1;
visibility: visible;
display: block;
}
}
@media screen and (max-width: 800px) {
.tab-label {
width: 3rem;
}
.tab-label .label {
font-size: small;
}
}
@media screen and (max-width: 400px) {
.tab-label {
width: 2.5rem;
}
.tab-label .label {
font-size: 8px;
}
}
</style>
<div class="tabs">
<!-- #include "./chat/chat.html" -->
<!-- #include "./blurbs/blurbs.html" -->
<!-- #include "./forum/forum.html" -->
<!-- #include "./essays/essays.html" -->
<!-- #include "./map/map.html" -->
<!-- #include file="./chat/chat.html" -->
<!-- #include file="./blurbs/blurbs.html" -->
<!-- #include file="./forum/forum.html" -->
<!-- #include file="./essays/essays.html" -->
</div>

View file

@ -1,14 +1,14 @@
<input
<div id="work" class="tab">
<input
type="radio"
name="top-level-tabs"
id="work-tab-input"
class="tabswitch"
class="tab-switch"
data-view="work"
/>
<label for="work-tab-input" class="tablabel"
/>
<label for="work-tab-input" class="tab-label"
><div class="icon work"></div>
<div class="label">Work</div>
</label>
<div class="panel">
<!-- #include "./README.md" -->
</label>
<div class="tab-content"><!-- #include file="./README.md" --></div>
</div>

View file

@ -1 +0,0 @@
autonomous.contact

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

View file

@ -8,7 +8,6 @@ import { EVENT } from '../models/event.ts';
export type PRECHECK = (req: Request, meta: Record<string, any>) => Promise<Response | undefined> | Response | undefined;
export type PRECHECK_TABLE = Record<string, PRECHECK[]>;
export const AUTHED_BEFORE_COOKIE_ID: string = Deno.env.get('AUTHED_BEFORE_COOKIE_ID') ?? 'authed_before';
export const SESSION_ID_TOKEN: string = Deno.env.get('SESSION_ID_TOKEN') ?? 'session_id';
export const SESSION_SECRET_TOKEN: string = Deno.env.get('SESSION_SECRET_TOKEN') ?? 'session_secret';
export const TOTP_TOKEN: string = Deno.env.get('TOTP_TOKEN') ?? 'totp';
@ -44,9 +43,6 @@ export function require_user(
}
}
export function user_has_create_permission_for_event(user: USER, event: EVENT) {
export function user_has_write_permission_for_event(user: USER, event: EVENT) {
return user.permissions.includes('events.create.' + event.type) || (Deno.env.get('DENO_ENV') === 'test' && event.type === 'test');
}
export function user_has_write_permission_for_event(user: USER, event: EVENT) {
return user.permissions.includes('events.write.' + event.type) || (Deno.env.get('DENO_ENV') === 'test' && event.type === 'test');
}