tangled
alpha
login
or
join now
mackuba.eu
/
skythread
14
fork
atom
Thread viewer for Bluesky
14
fork
atom
overview
issues
pulls
pipelines
moved all settings to a separate settings module
mackuba.eu
3 months ago
0973bf0e
155b8c40
+75
-46
11 changed files
expand all
collapse all
unified
split
src
components
AccountMenu.svelte
BiohazardDialog.svelte
posts
BlockedPostView.svelte
HiddenRepliesLink.svelte
PostComponent.svelte
models
account.svelte.ts
settings.svelte.ts
pages
LycanSearchPage.svelte
skythread.js
types.d.ts
utils
post_presenter.ts
+5
-8
src/components/AccountMenu.svelte
···
1
1
<script lang="ts">
2
2
import { showLoginDialog } from '../skythread.js';
3
3
import { account } from '../models/account.svelte.js';
4
4
+
import { settings } from '../models/settings.svelte.js';
4
5
import { getBaseLocation } from '../router.js';
5
6
import AccountMenuButton from './AccountMenuButton.svelte';
6
7
import LoadableImage from './LoadableImage.svelte';
···
28
29
function toggleBiohazard(e: Event) {
29
30
e.preventDefault();
30
31
31
31
-
let hazards = document.querySelectorAll('p.hidden-replies, .content > .post.blocked, .blocked > .load-post');
32
32
-
33
33
-
if (account.biohazardEnabled === false) {
34
34
-
account.biohazardEnabled = true;
35
35
-
Array.from(hazards).forEach(p => { (p as HTMLElement).style.display = 'block' });
32
32
+
if (settings.biohazardsEnabled === false) {
33
33
+
settings.biohazardsEnabled = true;
36
34
} else {
37
37
-
account.biohazardEnabled = false;
38
38
-
Array.from(hazards).forEach(p => { (p as HTMLElement).style.display = 'none' });
35
35
+
settings.biohazardsEnabled = false;
39
36
}
40
37
}
41
38
···
94
91
onclick={toggleBiohazard}
95
92
label="Show infohazards"
96
93
title="Show links to blocked and hidden comments"
97
97
-
showCheckmark={account.biohazardEnabled !== false}
94
94
+
showCheckmark={settings.biohazardsEnabled !== false}
98
95
/>
99
96
100
97
{#if !account.loggedIn}
+3
-3
src/components/BiohazardDialog.svelte
···
1
1
<script lang="ts">
2
2
-
import { account } from '../models/account.svelte.js';
2
2
+
import { settings } from '../models/settings.svelte.js';
3
3
4
4
type Props = { onConfirm?: (() => void) | undefined, onClose?: (() => void) | undefined };
5
5
let { onConfirm = undefined, onClose = undefined }: Props = $props();
6
6
7
7
function showBiohazard(e: Event) {
8
8
e.preventDefault();
9
9
-
account.biohazardEnabled = true;
9
9
+
settings.biohazardsEnabled = true;
10
10
11
11
onConfirm?.()
12
12
onClose?.();
···
14
14
15
15
function hideBiohazard(e: Event) {
16
16
e.preventDefault();
17
17
-
account.biohazardEnabled = false;
17
17
+
settings.biohazardsEnabled = false;
18
18
19
19
for (let p of document.querySelectorAll('p.hidden-replies, .content > .post.blocked, .blocked > .load-post')) {
20
20
(p as HTMLElement).style.display = 'none';
+2
-2
src/components/posts/BlockedPostView.svelte
···
1
1
<script lang="ts">
2
2
-
import { account } from '../../models/account.svelte.js';
3
2
import { BlockedPost, DetachedQuotePost, MissingPost, Post } from '../../models/posts.js';
3
3
+
import { settings } from '../../models/settings.svelte.js';
4
4
5
5
import BlockedPostContent from './BlockedPostContent.svelte';
6
6
import MissingPostView from './MissingPostView.svelte';
···
15
15
16
16
let { reason, post, placement }: Props = $props();
17
17
18
18
-
let biohazardEnabled = $derived(account.biohazardEnabled !== false);
18
18
+
let biohazardEnabled = $derived(settings.biohazardsEnabled !== false);
19
19
let loading = $state(false);
20
20
let postNotFound = $state(false);
21
21
let reloadedPost: Post | undefined = $state();
+2
-2
src/components/posts/HiddenRepliesLink.svelte
···
1
1
<script lang="ts">
2
2
import { showBiohazardDialog } from '../../skythread.js';
3
3
-
import { account } from '../../models/account.svelte.js';
3
3
+
import { settings } from '../../models/settings.svelte.js';
4
4
import { parseThreadPost } from '../../models/posts.js';
5
5
import { linkToPostThread } from '../../router.js';
6
6
import { getPostContext } from './PostComponent.svelte';
···
17
17
function onLinkClick(e: Event) {
18
18
e.preventDefault();
19
19
20
20
-
if (account.biohazardEnabled === true) {
20
20
+
if (settings.biohazardsEnabled === true) {
21
21
loadHiddenReplies();
22
22
} else {
23
23
showBiohazardDialog(() => {
+3
-3
src/components/posts/PostComponent.svelte
···
5
5
<script lang="ts">
6
6
import { createContext } from 'svelte';
7
7
import { HiddenRepliesError } from '../../api/bluesky_api.js';
8
8
-
import { account } from '../../models/account.svelte.js';
8
8
+
import { settings } from '../../models/settings.svelte.js';
9
9
import { Post, BlockedPost } from '../../models/posts.js';
10
10
import { Embed, InlineLinkEmbed } from '../../models/embeds.js';
11
11
import { isValidURL, showError } from '../../utils.js';
···
60
60
if (reply instanceof Post) {
61
61
return true;
62
62
} else if (reply instanceof BlockedPost) {
63
63
-
return (account.biohazardEnabled !== false);
63
63
+
return (settings.biohazardsEnabled !== false);
64
64
} else {
65
65
return false;
66
66
}
···
157
157
{#if placement == 'thread' && !repliesLoaded}
158
158
{#if post.hasMoreReplies}
159
159
<LoadMoreLink onLoad={onMoreRepliesLoaded} onError={onRepliesLoadingError} />
160
160
-
{:else if post.hasHiddenReplies && account.biohazardEnabled !== false}
160
160
+
{:else if post.hasHiddenReplies && settings.biohazardsEnabled !== false}
161
161
<HiddenRepliesLink onLoad={onHiddenRepliesLoaded} onError={onRepliesLoadingError} />
162
162
{/if}
163
163
{/if}
+4
-24
src/models/account.svelte.ts
···
1
1
import { AuthenticatedAPI } from '../api/authenticated_api.js';
2
2
import { pdsEndpointForIdentifier } from '../api/identity.js';
3
3
+
import { settings } from './settings.svelte.js';
3
4
4
5
class Account {
5
5
-
#isIncognito: boolean;
6
6
-
#biohazardEnabled: boolean | undefined;
7
6
#loggedIn: boolean;
8
7
#avatarURL: string | undefined;
9
8
#avatarIsLoading: boolean;
10
9
11
10
constructor() {
12
12
-
let incognito = localStorage.getItem('incognito');
13
13
-
let biohazard = localStorage.getItem('biohazard');
14
14
-
let biohazardEnabled = biohazard ? !!JSON.parse(biohazard) : undefined;
15
11
let accountAPI = new AuthenticatedAPI();
16
12
17
17
-
this.#isIncognito = $state(accountAPI.isLoggedIn && !!incognito);
18
18
-
this.#biohazardEnabled = $state(biohazardEnabled);
19
13
this.#loggedIn = $state(accountAPI.isLoggedIn);
20
14
this.#avatarURL = $state(accountAPI.isLoggedIn ? accountAPI.user.avatar : undefined);
21
15
this.#avatarIsLoading = $state(false);
22
16
}
23
17
24
18
get isIncognito(): boolean {
25
25
-
return this.#isIncognito;
19
19
+
return !!settings.incognitoMode;
26
20
}
27
21
28
22
toggleIncognitoMode() {
29
29
-
if (!this.#isIncognito) {
30
30
-
localStorage.setItem('incognito', '1');
31
31
-
} else {
32
32
-
localStorage.removeItem('incognito');
33
33
-
}
34
34
-
23
23
+
settings.incognitoMode = !this.isIncognito;
35
24
location.reload();
36
36
-
}
37
37
-
38
38
-
get biohazardEnabled(): boolean | undefined {
39
39
-
return this.#biohazardEnabled;
40
40
-
}
41
41
-
42
42
-
set biohazardEnabled(value: boolean) {
43
43
-
this.#biohazardEnabled = value;
44
44
-
localStorage.setItem('biohazard', JSON.stringify(value));
45
25
}
46
26
47
27
get loggedIn(): boolean {
···
78
58
79
59
logOut() {
80
60
window.accountAPI.resetTokens();
81
81
-
localStorage.removeItem('incognito');
61
61
+
settings.logOut();
82
62
location.reload();
83
63
}
84
64
}
+52
src/models/settings.svelte.ts
···
1
1
+
interface SettingsData {
2
2
+
dateLocale?: string;
3
3
+
incognito?: boolean;
4
4
+
biohazard?: boolean;
5
5
+
}
6
6
+
7
7
+
class Settings {
8
8
+
data: SettingsData;
9
9
+
10
10
+
constructor() {
11
11
+
let savedData = localStorage.getItem('settings');
12
12
+
this.data = $state(savedData ? JSON.parse(savedData) : {});
13
13
+
}
14
14
+
15
15
+
save() {
16
16
+
localStorage.setItem('settings', JSON.stringify(this.data));
17
17
+
}
18
18
+
19
19
+
logOut() {
20
20
+
delete this.data.incognito;
21
21
+
this.save();
22
22
+
}
23
23
+
24
24
+
get dateLocale(): string | undefined {
25
25
+
return this.data.dateLocale;
26
26
+
}
27
27
+
28
28
+
set dateLocale(value: string) {
29
29
+
this.data.dateLocale = value;
30
30
+
this.save();
31
31
+
}
32
32
+
33
33
+
get incognitoMode(): boolean | undefined {
34
34
+
return this.data.incognito;
35
35
+
}
36
36
+
37
37
+
set incognitoMode(value: boolean) {
38
38
+
this.data.incognito = value;
39
39
+
this.save();
40
40
+
}
41
41
+
42
42
+
get biohazardsEnabled(): boolean | undefined {
43
43
+
return this.data.biohazard;
44
44
+
}
45
45
+
46
46
+
set biohazardsEnabled(value: boolean) {
47
47
+
this.data.biohazard = value;
48
48
+
this.save();
49
49
+
}
50
50
+
}
51
51
+
52
52
+
export const settings = new Settings();
+2
-1
src/pages/LycanSearchPage.svelte
···
1
1
<script lang="ts">
2
2
import { Post } from '../models/posts';
3
3
+
import { settings } from '../models/settings.svelte';
3
4
import { Lycan } from '../services/lycan';
4
5
import PostComponent from '../components/posts/PostComponent.svelte';
5
6
···
119
120
if (info.progress == 1.0) {
120
121
importStatusLabel = `Import complete ✓`;
121
122
} else if (info.position) {
122
122
-
let date = new Date(info.position).toLocaleString(window.dateLocale, { day: 'numeric', month: 'short', year: 'numeric' });
123
123
+
let date = new Date(info.position).toLocaleString(settings.dateLocale, { day: 'numeric', month: 'short', year: 'numeric' });
123
124
importStatusLabel = `Downloaded data until: ${date}`;
124
125
} else if (info.status == 'requested') {
125
126
importStatusLabel = 'Requesting import…';
-1
src/skythread.js
···
26
26
27
27
28
28
function init() {
29
29
-
window.dateLocale = localStorage.getItem('locale') || undefined;
30
29
window.avatarPreloader = buildAvatarPreloader();
31
30
32
31
svelte.mount(AccountMenu, { target: $id('account_menu_wrap') });
-1
src/types.d.ts
···
1
1
interface Window {
2
2
-
dateLocale: string | undefined;
3
2
root: AnyPost;
4
3
subtreeRoot: AnyPost;
5
4
init: () => void;
+2
-1
src/utils/post_presenter.ts
···
1
1
import { sameDay } from '../utils.js';
2
2
import { Post } from '../models/posts.js';
3
3
+
import { settings } from '../models/settings.svelte.js';
3
4
4
5
export class PostPresenter {
5
6
···
34
35
35
36
get formattedTimestamp() {
36
37
let timeFormat = this.timeFormatForTimestamp;
37
37
-
return this.post.createdAt.toLocaleString(window.dateLocale, timeFormat);
38
38
+
return this.post.createdAt.toLocaleString(settings.dateLocale, timeFormat);
38
39
}
39
40
}