Compare commits

...
Sign in to create a new pull request.

4 commits
dev ... dev

Author SHA1 Message Date
7b3494cc32 feature: remember if someone has logged in and default to a login screen
fix: some includes needed updating
feature: some more overrides
2026-01-30 18:39:03 -08:00
ebf0e4428e feature: allow overriding signup pitch 2026-01-27 00:18:55 -08:00
6637927e20 fix: load a chat channel on login/load 2026-01-21 15:40:13 -08:00
32ed2dfd33 feature: add a map 2026-01-15 20:53:57 -08:00
38 changed files with 29968 additions and 199 deletions

View file

@ -1,6 +1,6 @@
# autonomous.contact # autonomous.contact
Bringing the BBS back. A hub for communities as a single service with no required external dependencies.
## TODO ## TODO

View file

@ -11,11 +11,15 @@
"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": "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": { "test": {
"exclude": ["tests/data/"] "exclude": [
"tests/data/"
]
}, },
"compilerOptions": {}, "compilerOptions": {},
"fmt": { "fmt": {
"include": ["**/*.ts"], "include": [
"**/*.ts"
],
"options": { "options": {
"useTabs": true, "useTabs": true,
"lineWidth": 180, "lineWidth": 180,
@ -25,22 +29,28 @@
} }
}, },
"lint": { "lint": {
"include": ["**/*.ts"], "include": [
"**/*.ts"
],
"rules": { "rules": {
"tags": ["recommended"], "tags": [
"exclude": ["no-explicit-any"] "recommended"
],
"exclude": [
"no-explicit-any"
]
} }
}, },
"imports": { "imports": {
"@andyburke/fsdb": "jsr:@andyburke/fsdb@^1.2.4", "@andyburke/fsdb": "jsr:@andyburke/fsdb@^1.2.4",
"@andyburke/lurid": "jsr:@andyburke/lurid@^0.2.0", "@andyburke/lurid": "jsr:@andyburke/lurid@^0.2.0",
"@andyburke/serverus": "jsr:@andyburke/serverus@^0.13.0", "@andyburke/serverus": "jsr:@andyburke/serverus@^0.16.0",
"@da/bcrypt": "jsr:@da/bcrypt@^1.0.1", "@da/bcrypt": "jsr:@da/bcrypt@^1.0.1",
"@std/assert": "jsr:@std/assert@^1.0.15", "@std/assert": "jsr:@std/assert@^1.0.17",
"@std/encoding": "jsr:@std/encoding@^1.0.10", "@std/encoding": "jsr:@std/encoding@^1.0.10",
"@std/fs": "jsr:@std/fs@^1.0.19", "@std/fs": "jsr:@std/fs@^1.0.22",
"@std/http": "jsr:@std/http@^1.0.21", "@std/http": "jsr:@std/http@^1.0.23",
"@std/media-types": "jsr:@std/media-types@^1.1.0", "@std/media-types": "jsr:@std/media-types@^1.1.0",
"@std/path": "jsr:@std/path@^1.1.2" "@std/path": "jsr:@std/path@^1.1.4"
} }
} }

84
deno.lock generated
View file

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

0
public/.spa Normal file
View file

View file

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

View file

@ -169,6 +169,15 @@ body {
/* fixed height? */ /* fixed height? */
} }
#background-container {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: -1;
}
main { main {
position: relative; position: relative;
width: 100%; width: 100%;
@ -1682,6 +1691,32 @@ body[data-perms*="files.write.own"] [data-requires-permission="files.write.own"]
left: 11px; 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 */ /* AUDIO PLAYER ICONS */
.icon.skip-back { .icon.skip-back {
box-sizing: border-box; box-sizing: border-box;

6
public/foo/index.html Normal file
View file

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

View file

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

View file

@ -5,10 +5,13 @@ const api = {
...__options, ...__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( const session_id = (document.cookie.match(
/^(?:.*;)?\s*session_id\s*=\s*([^;]+)(?:.*)?$/, /^(?:.*;)?\s*session_id\s*=\s*([^;]+)(?:.*)?$/,
) || [, null])[1]; ) || [, null])[1];
// FIXME: this will break with different server settings
// TODO: this wasn't really intended to be persisted in a cookie // TODO: this wasn't really intended to be persisted in a cookie
const session_secret = (document.cookie.match( const session_secret = (document.cookie.match(
/^(?:.*;)?\s*session_secret\s*=\s*([^;]+)(?:.*)?$/, /^(?:.*;)?\s*session_secret\s*=\s*([^;]+)(?:.*)?$/,

View file

@ -68,13 +68,13 @@ const APP = {
this._emit( 'view_changed', { this._emit( 'view_changed', {
previous, previous,
view view,
channel_id
}); });
} }
if (!document.body.dataset.channel || document.body.dataset.channel !== channel_id) { if (!document.body.dataset.channel || document.body.dataset.channel !== channel_id) {
const previous = typeof document.body.dataset.channel === 'string' ? document.body.dataset.channel : undefined; const previous = typeof document.body.dataset.channel === 'string' ? document.body.dataset.channel : undefined;
if ( channel_id ) { if ( channel_id ) {
document.body.dataset.channel = channel_id; document.body.dataset.channel = channel_id;
} }
@ -82,21 +82,10 @@ const APP = {
delete document.body.dataset.channel; delete document.body.dataset.channel;
} }
const target_channel_id = channel_id ?? this.CHANNELS.CHANNEL_LIST[0]?.id; this._emit( 'channel_changed', {
previous,
// TODO: allow a different default than chat channel_id
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: target_channel_id
});
}
window.location.hash = hash_target;
}
} }
}, },
@ -236,22 +225,11 @@ const APP = {
const channels_response = await api.fetch("/api/channels"); const channels_response = await api.fetch("/api/channels");
if (channels_response.ok) { if (channels_response.ok) {
const new_channels = await channels_response.json(); const new_channels = await channels_response.json();
const has_differences = APP.CHANNELS.CHANNEL_LIST = [...new_channels];
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._emit( 'channels_updated', {
APP.CHANNELS.CHANNEL_LIST = [...new_channels]; channels: APP.CHANNELS.CHANNEL_LIST
});
APP._emit( 'channels_updated', {
channels: APP.CHANNELS.CHANNEL_LIST
});
}
APP.CHANNELS._last_channel_update = now; APP.CHANNELS._last_channel_update = now;
} }
@ -271,3 +249,8 @@ const APP = {
}; };
document.addEventListener("DOMContentLoaded", APP.load.bind( APP )); document.addEventListener("DOMContentLoaded", APP.load.bind( APP ));
APP.on( 'view_changed', ( { view } ) => {
if ( !view ) {
window.location.hash = '/chat';
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

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

14512
public/js/external/leaflet/leaflet-src.js vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

661
public/js/external/leaflet/leaflet.css vendored Normal file
View file

@ -0,0 +1,661 @@
/* 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;
}
}

6
public/js/external/leaflet/leaflet.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -87,6 +87,11 @@
border-right: 1px solid var(--border-subtle); border-right: 1px solid var(--border-subtle);
} }
#sidebar .profile-container {
width: 100%;
max-width: 100%;
}
#sidebar #sidebar-context-menu { #sidebar #sidebar-context-menu {
display: none; display: none;
visibility: hidden; visibility: hidden;

View file

@ -14,11 +14,24 @@
background: var(--bg); background: var(--bg);
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
transition: all 0.33s; transition: all 0.33s ease;
animation: slideIn 0.4s ease;
}
@keyframes slideIn {
from {
opacity: 0;
transform: scale(1.2);
}
to {
opacity: 1;
transform: scale(1.0);
}
} }
#login-tab .tab-content { #login-tab .tab-content {
min-height: 17rem; min-height: 17rem;
overflow: hidden;
} }
#signup-tab .tab-content { #signup-tab .tab-content {
@ -48,52 +61,17 @@
} }
</style> </style>
<!-- #include file="./signup_pitch.md" --> <!-- #include "./files/settings/signup_pitch.html" or "./files/settings/signup_pitch.md" or "./signup_pitch.default.md" -->
<div class="limiter"> <div class="limiter">
<div class="tabs"> <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"> <div id="signup-tab" class="tab">
<input <input
type="radio" type="radio"
name="signup-login-tabs" name="signup-login-tabs"
id="signup-tab-input" id="signup-tab-input"
class="tab-switch" class="tab-switch"
checked="checked"
/> />
<label for="signup-tab-input" class="tab-label"> <label for="signup-tab-input" class="tab-label">
<div class="label">Sign Up</div> <div class="label">Sign Up</div>
@ -135,6 +113,7 @@
id="signup-invite-code" id="signup-invite-code"
type="text" type="text"
name="invite_code" name="invite_code"
required
/> />
<label class="placeholder" for="signup-invite-code">invite code</label> <label class="placeholder" for="signup-invite-code">invite code</label>
</div> </div>
@ -142,6 +121,53 @@
</form> </form>
</div> </div>
</div> </div>
<div id="login-tab" class="tab">
<input
type="radio"
name="signup-login-tabs"
id="login-tab-input"
class="tab-switch"
/>
<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> </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> </div>

View file

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

View file

@ -141,9 +141,9 @@
</label> </label>
<div class="tab-content"> <div class="tab-content">
<div id="blurbs-container" class="container"> <div id="blurbs-container" class="container">
<!-- #include file="./README.md" --> <!-- #include "./README.md" -->
<!-- #include file="./new_blurb.html" --> <!-- #include "./new_blurb.html" -->
<div <div
id="blurbs-list" id="blurbs-list"
@ -227,7 +227,7 @@
<div class="content-container">${htmlify(md_to_html(context.blurb.data.blurb))}</div> <div class="content-container">${htmlify(md_to_html(context.blurb.data.blurb))}</div>
<div class="reactions-container"></div> <div class="reactions-container"></div>
<button class="icon more" commandfor="eventactionspopover"></button> <button class="icon more" commandfor="eventactionspopover"></button>
<!-- #include file="./new_blurb.html" --> <!-- #include "./new_blurb.html" -->
<div class="replies-container"></div> <div class="replies-container"></div>
</div> </div>
</template> </template>

View file

@ -11,6 +11,6 @@
<div class="label">Calendar</div></label <div class="label">Calendar</div></label
> >
<div class="tab-content"> <div class="tab-content">
<!-- #include file="./README.md" --> <!-- #include "./README.md" -->
</div> </div>
</div> </div>

View file

@ -1,13 +1,53 @@
<script> <script>
APP.on("channels_updated", ({ channels }) => { function on_channels_updated({ channels }) {
const channel_list = document.getElementById("channel-list"); const channel_list = document.getElementById("channel-list");
if ( !channel_list ) {
setTimeout( () => {
on_channels_updated( { channels } );
}, 100 );
return;
}
channel_list.innerHTML = ""; channel_list.innerHTML = "";
for (const channel of channels.sort((lhs, rhs) => lhs.name.localeCompare(rhs.name))) { 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( channel_list.insertAdjacentHTML(
"beforeend", "beforeend",
`<li id="channel-selector-${channel.id}" class="channel" data-channel-selector-for="${channel.id}"><a href="#/channel/${channel.id}/chat">${channel.name}</a></li>`, `<li id="channel-selector-${channel.id}" class="channel" data-channel-selector-for="${channel.id}"><a href="#/chat/channel/${channel.id}">${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) { function update_channel_indicators(event) {
@ -77,20 +117,25 @@
} }
}); });
APP.on( 'view_changed', ( {view} ) => { APP.on( 'view_changed', ( {previous, view} ) => {
if ( !view === 'chat' ) {
return;
}
const sidebar_dynamic_container = document.getElementById( 'sidebar-dynamic-container'); const sidebar_dynamic_container = document.getElementById( 'sidebar-dynamic-container');
if ( !sidebar_dynamic_container ) { if ( !sidebar_dynamic_container ) {
console.error( 'could not get #sidebar-dynamic-container' ); console.error( 'could not get #sidebar-dynamic-container' );
return; 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'); const template = document.getElementById( 'channel-list-template');
sidebar_dynamic_container.innerHTML = template.innerHTML.trim(); sidebar_dynamic_container.innerHTML = template.innerHTML.trim();
APP.CHANNELS.update(); APP.CHANNELS.update(true);
}); });
</script> </script>
<style> <style>

View file

@ -12,7 +12,7 @@
</label> </label>
<div class="tab-content"> <div class="tab-content">
<style> <style>
<!-- #include file="./chat.css" --> <!-- #include "./chat.css" -->
</style> </style>
<script src="/js/external/mimetypes.js" type="text/javascript"></script> <script src="/js/external/mimetypes.js" type="text/javascript"></script>
<script src="/js/external/punycode.js" type="text/javascript"></script> <script src="/js/external/punycode.js" type="text/javascript"></script>
@ -232,4 +232,4 @@
</div> </div>
</div> </div>
</div> </div>
<!-- #include file="./channel_sidebar.html" --> <!-- #include "./channel_sidebar.html" -->

View file

@ -109,8 +109,8 @@
</label> </label>
<div class="tab-content"> <div class="tab-content">
<div id="essays-container" class="container"> <div id="essays-container" class="container">
<!-- #include file="./README.md" --> <!-- #include "./README.md" -->
<!-- #include file="./new_essay.html" --> <!-- #include "./new_essay.html" -->
<div <div
id="essays-list" id="essays-list"

View file

@ -11,6 +11,6 @@
<div class="label">Exchange</div></label <div class="label">Exchange</div></label
> >
<div class="tab-content"> <div class="tab-content">
<!-- #include file="./README.md" --> <!-- #include "./README.md" -->
</div> </div>
</div> </div>

View file

@ -140,7 +140,7 @@
<div class="label">Forum</div></label <div class="label">Forum</div></label
> >
<div class="tab-content forum-container"> <div class="tab-content forum-container">
<!-- #include file="./README.md" --> <!-- #include "./README.md" -->
<div <div
id="posts-list" id="posts-list"
@ -234,7 +234,7 @@
</div> </div>
<div class="reactions-container"></div> <div class="reactions-container"></div>
<button class="icon more" commandfor="eventactionspopover"></button> <button class="icon more" commandfor="eventactionspopover"></button>
<!-- #include file="./new_post.html" --> <!-- #include "./new_post.html" -->
<div class="replies-container"></div> <div class="replies-container"></div>
</div> </div>
</template> </template>
@ -251,6 +251,6 @@
</template> </template>
</div> </div>
<!-- #include file="./new_post.html" --> <!-- #include "./new_post.html" -->
</div> </div>
</div> </div>

View file

@ -12,6 +12,6 @@
<div class="label">Home</div> <div class="label">Home</div>
</label> </label>
<div class="tab-content"> <div class="tab-content">
<!-- #include file="./README.md" --> <!-- #include "./README.md" -->
</div> </div>
</div> </div>

55
public/tabs/map/map.html Normal file
View file

@ -0,0 +1,55 @@
<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 .tab-content {
overflow: hidden;
}
#map {
position: absolute;
top: 4em;
left: 4em;
right: 4em;
bottom: 4em;
}
@media screen and (max-width: 640px) {
#map {
top: 1em;
left: 1em;
right: 1em;
bottom: 1em;
}
}
</style>
<div id="map-tab" class="tab">
<input
type="radio"
name="top-level-tabs"
id="map-tab-tab-input"
class="tab-switch"
data-view="map"
/>
<label for="map-tab-tab-input" class="tab-label"
><div class="icon map-pin"></div>
<div class="label">Map</div>
</label>
<div class="tab-content">
<div id="map"></div>
</div>
</div>

View file

@ -10,5 +10,5 @@
><div class="icon resources"></div> ><div class="icon resources"></div>
<div class="label">Resources</div></label <div class="label">Resources</div></label
> >
<div class="tab-content"><!-- #include file="./README.md" --></div> <div class="tab-content"><!-- #include "./README.md" --></div>
</div> </div>

View file

@ -13,6 +13,7 @@
tab_switch.addEventListener("input", (event) => { tab_switch.addEventListener("input", (event) => {
const tab_selector = event.target; const tab_selector = event.target;
const view = tab_selector.dataset.view; const view = tab_selector.dataset.view;
if (view) { if (view) {
window.location.hash = `/${view}${ document.body.dataset.channel ? `/channel/${ document.body.dataset.channel }` : '' }`; window.location.hash = `/${view}${ document.body.dataset.channel ? `/channel/${ document.body.dataset.channel }` : '' }`;
} }
@ -110,28 +111,21 @@
@media screen and (max-width: 800px) { @media screen and (max-width: 800px) {
.tab-label { .tab-label {
width: 3rem; width: 4rem;
}
.tab-label .label {
font-size: small;
} }
} }
@media screen and (max-width: 400px) { @media screen and (max-width: 400px) {
.tab-label { .tab-label {
width: 2.5rem; width: 3rem;
}
.tab-label .label {
font-size: 8px;
} }
} }
</style> </style>
<div class="tabs"> <div class="tabs">
<!-- #include file="./chat/chat.html" --> <!-- #include "./chat/chat.html" -->
<!-- #include file="./blurbs/blurbs.html" --> <!-- #include "./blurbs/blurbs.html" -->
<!-- #include file="./forum/forum.html" --> <!-- #include "./forum/forum.html" -->
<!-- #include file="./essays/essays.html" --> <!-- #include "./essays/essays.html" -->
<!-- #include "./map/map.html" -->
</div> </div>

View file

@ -10,5 +10,5 @@
><div class="icon work"></div> ><div class="icon work"></div>
<div class="label">Work</div> <div class="label">Work</div>
</label> </label>
<div class="tab-content"><!-- #include file="./README.md" --></div> <div class="tab-content"><!-- #include "./README.md" --></div>
</div> </div>

View file

@ -8,6 +8,7 @@ import { EVENT } from '../models/event.ts';
export type PRECHECK = (req: Request, meta: Record<string, any>) => Promise<Response | undefined> | Response | undefined; export type PRECHECK = (req: Request, meta: Record<string, any>) => Promise<Response | undefined> | Response | undefined;
export type PRECHECK_TABLE = Record<string, PRECHECK[]>; 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_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 SESSION_SECRET_TOKEN: string = Deno.env.get('SESSION_SECRET_TOKEN') ?? 'session_secret';
export const TOTP_TOKEN: string = Deno.env.get('TOTP_TOKEN') ?? 'totp'; export const TOTP_TOKEN: string = Deno.env.get('TOTP_TOKEN') ?? 'totp';