This commit is contained in:
ThatOneCalculator 2023-03-23 14:46:37 -07:00
commit 3daeeb45a4
24 changed files with 201 additions and 182 deletions

View File

@ -62,16 +62,6 @@ redis:
#prefix: example-prefix #prefix: example-prefix
#db: 1 #db: 1
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#elasticsearch:
# host: localhost
# port: 9200
# ssl: false
# user:
# pass:
# ┌─────────────────────┐ # ┌─────────────────────┐
#───┘ Sonic configuration └───────────────────────────────────── #───┘ Sonic configuration └─────────────────────────────────────
@ -82,6 +72,16 @@ redis:
# collection: notes # collection: notes
# bucket: default # bucket: default
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#elasticsearch:
# host: localhost
# port: 9200
# ssl: false
# user:
# pass:
# ┌───────────────┐ # ┌───────────────┐
#───┘ ID generation └─────────────────────────────────────────── #───┘ ID generation └───────────────────────────────────────────

View File

@ -8,7 +8,7 @@ services:
depends_on: depends_on:
- db - db
- redis - redis
# - es - sonic
ports: ports:
- "3000:3000" - "3000:3000"
networks: networks:
@ -40,16 +40,14 @@ services:
volumes: volumes:
- ./db:/var/lib/postgresql/data - ./db:/var/lib/postgresql/data
# es: sonic:
# restart: unless-stopped restart: unless-stopped
# image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2 image: docker.io/valeriansaliou/sonic:v1.4.0
# environment: networks:
# - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - calcnet
# - "TAKE_FILE_OWNERSHIP=111" volumes:
# networks: - ./sonic:/var/lib/sonic/store
# - calcnet - ./sonic/config.cfg:/etc/sonic.cfg
# volumes:
# - ./elasticsearch:/usr/share/elasticsearch/data
networks: networks:
calcnet: calcnet:

View File

@ -14,7 +14,7 @@ There is a `docker-compose.yml` in the root of the project that you can use to b
Rename the files: Rename the files:
`cp .config/default_example.yml .config/default.yml` `cp .config/example.yml .config/default.yml`
`cp .config/example.env .config/docker.env` `cp .config/example.env .config/docker.env`
@ -23,6 +23,7 @@ You can configure `docker.env` with anything you like, but you will have to pay
- `url` should be set to the URL you will be hosting the web interface for the instance at. - `url` should be set to the URL you will be hosting the web interface for the instance at.
- `host`, `db`, `user`, `pass` will have to be configured in the `PostgreSQL configuration` section - `host` is the name of the postgres container (eg: *calckey_db_1*), and the others should match your `docker.env`. - `host`, `db`, `user`, `pass` will have to be configured in the `PostgreSQL configuration` section - `host` is the name of the postgres container (eg: *calckey_db_1*), and the others should match your `docker.env`.
- `host`will need to be configured in the *Redis configuration* section - it is the name of the redis container (eg: *calckey_redis_1*) - `host`will need to be configured in the *Redis configuration* section - it is the name of the redis container (eg: *calckey_redis_1*)
- `auth` will need to be configured in the *Sonic* section - cannot be the default `SecretPassword`
Everything else can be left as-is. Everything else can be left as-is.

View File

@ -1027,27 +1027,6 @@ _time:
minute: "د" minute: "د"
hour: "سا" hour: "سا"
day: "ي" day: "ي"
_tutorial:
title: "How to use Calckey"
step1_1: "Welcome!"
step1_2: "Let's get you set up. You'll be up and running in no time!"
step2_1: "First, please fill out your profile."
step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you."
step3_1: "Now time to follow some people!"
step3_2: "Your home and social timelines are based off of who you follow, so try following a couple accounts to get started.\nClick the plus circle on the top right of a profile to follow them."
step4_1: "Let's get you out there."
step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\""
step5_1: "Timelines, timelines everywhere!"
step5_2: "Your instance has {timelines} different timelines enabled."
step5_3: "The Home {icon} timeline is where you can see posts from your followers."
step5_4: "The Local {icon} timeline is where you can see posts from everyone else on this instance."
step5_5: "The Recommended {icon} timeline is where you can see posts from instances the admins recommend."
step5_6: "The Social {icon} timeline is where you can see posts from friends of your followers."
step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance."
step6_1: "So, what is this place?"
step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"."
step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time."
step6_4: "Now go, explore, and have fun!"
_2fa: _2fa:
alreadyRegistered: "سجلت سلفًا جهازًا للاستيثاق بعاملين." alreadyRegistered: "سجلت سلفًا جهازًا للاستيثاق بعاملين."
registerDevice: "سجّل جهازًا جديدًا" registerDevice: "سجّل جهازًا جديدًا"

View File

@ -1108,27 +1108,6 @@ _time:
minute: "মিনিট" minute: "মিনিট"
hour: "ঘণ্টা" hour: "ঘণ্টা"
day: "দিন" day: "দিন"
_tutorial:
title: "How to use Calckey"
step1_1: "Welcome!"
step1_2: "Let's get you set up. You'll be up and running in no time!"
step2_1: "First, please fill out your profile."
step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you."
step3_1: "Now time to follow some people!"
step3_2: "Your home and social timelines are based off of who you follow, so try following a couple accounts to get started.\nClick the plus circle on the top right of a profile to follow them."
step4_1: "Let's get you out there."
step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\""
step5_1: "Timelines, timelines everywhere!"
step5_2: "Your instance has {timelines} different timelines enabled."
step5_3: "The Home {icon} timeline is where you can see posts from your followers."
step5_4: "The Local {icon} timeline is where you can see posts from everyone else on this instance."
step5_5: "The Recommended {icon} timeline is where you can see posts from instances the admins recommend."
step5_6: "The Social {icon} timeline is where you can see posts from friends of your followers."
step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance."
step6_1: "So, what is this place?"
step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"."
step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time."
step6_4: "Now go, explore, and have fun!"
_2fa: _2fa:
alreadyRegistered: "আপনি ইতিমধ্যে একটি 2-ফ্যাক্টর অথেনটিকেশন ডিভাইস নিবন্ধন করেছেন৷" alreadyRegistered: "আপনি ইতিমধ্যে একটি 2-ফ্যাক্টর অথেনটিকেশন ডিভাইস নিবন্ধন করেছেন৷"
registerDevice: "নতুন ডিভাইস নিবন্ধন করুন" registerDevice: "নতুন ডিভাইস নিবন্ধন করুন"

View File

@ -849,6 +849,9 @@ overridedDeviceKind: "Device type"
smartphone: "Smartphone" smartphone: "Smartphone"
tablet: "Tablet" tablet: "Tablet"
auto: "Auto" auto: "Auto"
showLocalPosts: "Show local posts in:"
homeTimeline: "Home Timeline"
socialTimeline: "Social Timeline"
themeColor: "Instance Ticker Color" themeColor: "Instance Ticker Color"
size: "Size" size: "Size"
numberOfColumn: "Number of columns" numberOfColumn: "Number of columns"
@ -1240,10 +1243,10 @@ _tutorial:
step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\"" step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\""
step5_1: "Timelines, timelines everywhere!" step5_1: "Timelines, timelines everywhere!"
step5_2: "Your instance has {timelines} different timelines enabled." step5_2: "Your instance has {timelines} different timelines enabled."
step5_3: "The Home {icon} timeline is where you can see posts from your followers." step5_3: "The Home {icon} timeline is where you can see posts from the accounts you follow and from everyone else on this instance. If you prefer your Home timeline to only display posts from accounts you follow, you can easily change this in Settings!"
step5_4: "The Local {icon} timeline is where you can see posts from everyone else on this instance." step5_4: "The Local {icon} timeline is where you can see posts from everyone else on this instance."
step5_5: "The Recommended {icon} timeline is where you can see posts from instances the admins recommend." step5_5: "The Social {icon} timeline is where you can see posts only from the accounts you follow."
step5_6: "The Social {icon} timeline is your home + local." step5_6: "The Recommended {icon} timeline is where you can see posts from instances the admins recommend."
step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance." step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance."
step6_1: "So, what is this place?" step6_1: "So, what is this place?"
step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"." step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"."

View File

@ -1174,27 +1174,6 @@ _time:
minute: "min" minute: "min"
hour: "hod" hour: "hod"
day: "dní" day: "dní"
_tutorial:
title: "How to use Calckey"
step1_1: "Welcome!"
step1_2: "Let's get you set up. You'll be up and running in no time!"
step2_1: "First, please fill out your profile."
step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you."
step3_1: "Now time to follow some people!"
step3_2: "Your home and social timelines are based off of who you follow, so try following a couple accounts to get started.\nClick the plus circle on the top right of a profile to follow them."
step4_1: "Let's get you out there."
step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\""
step5_1: "Timelines, timelines everywhere!"
step5_2: "Your instance has {timelines} different timelines enabled."
step5_3: "The Home {icon} timeline is where you can see posts from your followers."
step5_4: "The Local {icon} timeline is where you can see posts from everyone else on this instance."
step5_5: "The Recommended {icon} timeline is where you can see posts from instances the admins recommend."
step5_6: "The Social {icon} timeline is where you can see posts from friends of your followers."
step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance."
step6_1: "So, what is this place?"
step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"."
step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time."
step6_4: "Now go, explore, and have fun!"
_2fa: _2fa:
alreadyRegistered: "Už ste zaregistrovali 2-faktorové autentifikačné zariadenie." alreadyRegistered: "Už ste zaregistrovali 2-faktorové autentifikačné zariadenie."
registerDevice: "Registrovať nové zariadenie" registerDevice: "Registrovať nové zariadenie"

View File

@ -1179,27 +1179,6 @@ _time:
minute: "phút" minute: "phút"
hour: "giờ" hour: "giờ"
day: "ngày" day: "ngày"
_tutorial:
title: "How to use Calckey"
step1_1: "Welcome!"
step1_2: "Let's get you set up. You'll be up and running in no time!"
step2_1: "First, please fill out your profile."
step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you."
step3_1: "Now time to follow some people!"
step3_2: "Your home and social timelines are based off of who you follow, so try following a couple accounts to get started.\nClick the plus circle on the top right of a profile to follow them."
step4_1: "Let's get you out there."
step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\""
step5_1: "Timelines, timelines everywhere!"
step5_2: "Your instance has {timelines} different timelines enabled."
step5_3: "The Home {icon} timeline is where you can see posts from your followers."
step5_4: "The Local {icon} timeline is where you can see posts from everyone else on this instance."
step5_5: "The Recommended {icon} timeline is where you can see posts from instances the admins recommend."
step5_6: "The Social {icon} timeline is where you can see posts from friends of your followers."
step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance."
step6_1: "So, what is this place?"
step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"."
step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time."
step6_4: "Now go, explore, and have fun!"
_2fa: _2fa:
alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước." alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước."
registerDevice: "Đăng ký một thiết bị" registerDevice: "Đăng ký một thiết bị"

View File

@ -1,12 +1,12 @@
{ {
"name": "calckey", "name": "calckey",
"version": "13.2.0-beta2", "version": "13.2.0-beta3",
"codename": "aqua", "codename": "aqua",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://codeberg.org/calckey/calckey.git" "url": "https://codeberg.org/calckey/calckey.git"
}, },
"packageManager": "pnpm@7.29.3", "packageManager": "pnpm@7.30.1",
"private": true, "private": true,
"scripts": { "scripts": {
"rebuild": "pnpm run clean && pnpm -r run build && pnpm run gulp", "rebuild": "pnpm run clean && pnpm -r run build && pnpm run gulp",

View File

@ -12,6 +12,7 @@ import {
Channels, Channels,
} from "../index.js"; } from "../index.js";
import type { Packed } from "@/misc/schema.js"; import type { Packed } from "@/misc/schema.js";
import { nyaize } from "@/misc/nyaize.js";
import { awaitAll } from "@/prelude/await-all.js"; import { awaitAll } from "@/prelude/await-all.js";
import { import {
convertLegacyReaction, convertLegacyReaction,
@ -262,7 +263,7 @@ export const NoteRepository = db.getRepository(Note).extend({
: {}), : {}),
}); });
/* if (packed.user.isCat && packed.text) { if (packed.user.isCat && packed.text) {
const tokens = packed.text ? mfm.parse(packed.text) : []; const tokens = packed.text ? mfm.parse(packed.text) : [];
mfm.inspect(tokens, (node) => { mfm.inspect(tokens, (node) => {
if (node.type === "text") { if (node.type === "text") {
@ -271,7 +272,7 @@ export const NoteRepository = db.getRepository(Note).extend({
} }
}); });
packed.text = mfm.toString(tokens); packed.text = mfm.toString(tokens);
} */ }
return packed; return packed;
}, },

View File

@ -66,7 +66,7 @@ const nameSchema = { type: "string", minLength: 1, maxLength: 50 } as const;
const descriptionSchema = { const descriptionSchema = {
type: "string", type: "string",
minLength: 1, minLength: 1,
maxLength: 500, maxLength: 2048,
} as const; } as const;
const locationSchema = { type: "string", minLength: 1, maxLength: 50 } as const; const locationSchema = { type: "string", minLength: 1, maxLength: 50 } as const;
const birthdaySchema = { const birthdaySchema = {

View File

@ -70,13 +70,13 @@ export class LdSignature {
...options, ...options,
"@context": "https://w3id.org/identity/v1", "@context": "https://w3id.org/identity/v1",
}; };
transformedOptions["type"] = undefined; delete transformedOptions["type"];
transformedOptions["id"] = undefined; delete transformedOptions["id"];
transformedOptions["signatureValue"] = undefined; delete transformedOptions["signatureValue"];
const canonizedOptions = await this.normalize(transformedOptions); const canonizedOptions = await this.normalize(transformedOptions);
const optionsHash = this.sha256(canonizedOptions); const optionsHash = this.sha256(canonizedOptions);
const transformedData = { ...data }; const transformedData = { ...data };
transformedData["signature"] = undefined; delete transformedData["signature"];
const cannonidedData = await this.normalize(transformedData); const cannonidedData = await this.normalize(transformedData);
if (this.debug) console.debug(`cannonidedData: ${cannonidedData}`); if (this.debug) console.debug(`cannonidedData: ${cannonidedData}`);
const documentHash = this.sha256(cannonidedData); const documentHash = this.sha256(cannonidedData);

View File

@ -61,4 +61,49 @@ export function apiMastodonCompatible(router: Router): void {
ctx.body = e.response.data; ctx.body = e.response.data;
} }
}); });
router.get("/v1/filters", async (ctx) => {
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
const accessTokens = ctx.request.headers.authorization;
const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt
// displayed without being logged in
try {
const data = await client.getFilters();
ctx.body = data.data;
} catch (e: any) {
console.error(e);
ctx.status = 401;
ctx.body = e.response.data;
}
});
router.get("/v1/trends", async (ctx) => {
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
const accessTokens = ctx.request.headers.authorization;
const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt
// displayed without being logged in
try {
const data = await client.getInstanceTrends();
ctx.body = data.data;
} catch (e: any) {
console.error(e);
ctx.status = 401;
ctx.body = e.response.data;
}
});
router.get("/v1/preferences", async (ctx) => {
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
const accessTokens = ctx.request.headers.authorization;
const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt
// displayed without being logged in
try {
const data = await client.getPreferences();
ctx.body = data.data;
} catch (e: any) {
console.error(e);
ctx.status = 401;
ctx.body = e.response.data;
}
});
} }

View File

@ -174,7 +174,7 @@ mastoRouter.post("/oauth/token", async (ctx) => {
const body: any = ctx.request.body || ctx.request.query; const body: any = ctx.request.body || ctx.request.query;
console.log('token-request', body); console.log('token-request', body);
console.log('token-query', ctx.request.query); console.log('token-query', ctx.request.query);
if (body.redirect_uri.startsWith('com.tapbots') && body.grant_type === 'client_credentials') { if (body.grant_type === 'client_credentials') {
const ret = { const ret = {
access_token: uuid(), access_token: uuid(),
token_type: "Bearer", token_type: "Bearer",

View File

@ -149,7 +149,7 @@ onBeforeUnmount(() => {
height: 31px; height: 31px;
font-size: 16px; font-size: 16px;
border-radius: 32px; border-radius: 32px;
background: var(--accentedBg); background: var(--bg);
&.full { &.full {
padding: 0 8px 0 12px; padding: 0 8px 0 12px;

View File

@ -304,6 +304,7 @@ os.api('notes/children', {
if (appearNote.replyId) { if (appearNote.replyId) {
os.api('notes/conversation', { os.api('notes/conversation', {
noteId: appearNote.replyId, noteId: appearNote.replyId,
limit: 30,
}).then(res => { }).then(res => {
conversation.value = res.reverse(); conversation.value = res.reverse();
focus(); focus();

View File

@ -91,7 +91,6 @@ import { instance } from '@/instance';
import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account'; import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account';
import { uploadFile } from '@/scripts/upload'; import { uploadFile } from '@/scripts/upload';
import { deepClone } from '@/scripts/clone'; import { deepClone } from '@/scripts/clone';
import { nyaize } from '@/scripts/nyaize';
import XCheatSheet from '@/components/MkCheatSheetDialog.vue'; import XCheatSheet from '@/components/MkCheatSheetDialog.vue';
const modal = inject('modal'); const modal = inject('modal');
@ -583,10 +582,6 @@ async function post() {
} }
} }
if ($i?.isCat) {
postData.text = nyaize(`${postData.text}`);
}
let token = undefined; let token = undefined;
if (postAccount) { if (postAccount) {

View File

@ -1,5 +1,5 @@
<template> <template>
<a :href="to" :class="active ? activeClass : null" @click.prevent="nav" @contextmenu.prevent.stop="onContextmenu" @click.stop> <a :href="to" :class="active ? activeClass : null" @click="nav" @contextmenu.prevent.stop="onContextmenu" @click.stop>
<slot></slot> <slot></slot>
</a> </a>
</template> </template>
@ -80,23 +80,27 @@ function popout() {
} }
function nav(ev: MouseEvent) { function nav(ev: MouseEvent) {
if (props.behavior === 'browser') { if (!ev.ctrlKey) {
location.href = props.to; ev.preventDefault();
return;
}
if (props.behavior) { if (props.behavior === 'browser') {
if (props.behavior === 'window') { location.href = props.to;
return openWindow(); return;
} else if (props.behavior === 'modalWindow') {
return modalWindow();
} }
}
if (ev.shiftKey) { if (props.behavior) {
return openWindow(); if (props.behavior === 'window') {
} return openWindow();
} else if (props.behavior === 'modalWindow') {
return modalWindow();
}
}
router.push(props.to, ev.ctrlKey ? 'forcePage' : null); if (ev.shiftKey) {
return openWindow();
}
router.push(props.to, ev.ctrlKey ? 'forcePage' : null);
}
} }
</script> </script>

View File

@ -1,21 +1,28 @@
<template> <template>
<div v-if="chosen && defaultStore.state.showAds" class="qiivuoyo"> <div v-if="chosen && chosen.length > 0 && defaultStore.state.showAds" v-for="chosenItem in chosen" class="qiivuoyo">
<div v-if="!showMenu" class="main" :class="chosen.place"> <div v-if="!showMenu" class="main" :class="chosenItem.place">
<a :href="chosen.url" target="_blank"> <a :href="chosenItem.url" target="_blank">
<img :src="chosen.imageUrl"> <img :src="chosenItem.imageUrl">
<button class="_button menu" @click.prevent.stop="toggleMenu"><span class="ph-info ph-bold ph-lg info-circle"></span></button> </a>
</a>
</div>
<div v-else class="menu">
<div class="body">
<div>Ads by {{ host }}</div>
<!--<MkButton class="button" primary>{{ i18n.ts._ad.like }}</MkButton>-->
<MkButton v-if="chosen.ratio !== 0" class="button" @click="reduceFrequency">{{ i18n.ts._ad.reduceFrequencyOfThisAd }}</MkButton>
<button class="_textButton" @click="toggleMenu">{{ i18n.ts._ad.back }}</button>
</div> </div>
</div> </div>
</div> <div v-else-if="chosen && defaultStore.state.showAds" class="qiivuoyo">
<div v-else></div> <div v-if="!showMenu" class="main" :class="chosen.place">
<a :href="chosen.url" target="_blank">
<img :src="chosen.imageUrl">
<button class="_button menu" @click.prevent.stop="toggleMenu"><span class="ph-info ph-bold ph-lg info-circle"></span></button>
</a>
</div>
<div v-else class="menu">
<div class="body">
<div>Ads by {{ host }}</div>
<!--<MkButton class="button" primary>{{ i18n.ts._ad.like }}</MkButton>-->
<MkButton v-if="chosen.ratio !== 0" class="button" @click="reduceFrequency">{{ i18n.ts._ad.reduceFrequencyOfThisAd }}</MkButton>
<button class="_textButton" @click="toggleMenu">{{ i18n.ts._ad.back }}</button>
</div>
</div>
</div>
<div v-else></div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@ -20,6 +20,12 @@
<option value="desktop"><i class="ph-desktop ph-bold ph-lg"/> {{ i18n.ts.desktop }}</option> <option value="desktop"><i class="ph-desktop ph-bold ph-lg"/> {{ i18n.ts.desktop }}</option>
</FormRadios> </FormRadios>
<FormRadios v-model="showLocalPostsInTimeline" class="_formBlock">
<template #label>{{ i18n.ts.showLocalPosts }}</template>
<option value="home"><i class="ph-house ph-bold ph-lg"/> {{ i18n.ts.homeTimeline }}</option>
<option value="social"><i class="ph-handshake ph-bold ph-lg"/> {{ i18n.ts.socialTimeline }}</option>
</FormRadios>
<FormSwitch v-model="showFixedPostForm" class="_formBlock">{{ i18n.ts.showFixedPostForm }}</FormSwitch> <FormSwitch v-model="showFixedPostForm" class="_formBlock">{{ i18n.ts.showFixedPostForm }}</FormSwitch>
<FormSection> <FormSection>
@ -127,6 +133,7 @@ async function reloadAsk() {
} }
const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind')); const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind'));
const showLocalPostsInTimeline = computed(defaultStore.makeGetterSetter('showLocalPostsInTimeline'));
const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior')); const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior'));
const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v)); const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v));
const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal')); const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal'));
@ -183,6 +190,7 @@ watch([
showGapBetweenNotesInTimeline, showGapBetweenNotesInTimeline,
instanceTicker, instanceTicker,
overridedDeviceKind, overridedDeviceKind,
showLocalPostsInTimeline,
showAds, showAds,
showUpdates, showUpdates,
swipeOnDesktop, swipeOnDesktop,

View File

@ -55,6 +55,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
'widgets', 'widgets',
'tl', 'tl',
'overridedDeviceKind', 'overridedDeviceKind',
'showLocalPostsInTimeline',
'serverDisconnectedBehavior', 'serverDisconnectedBehavior',
'nsfw', 'nsfw',
'showAds', 'showAds',

View File

@ -94,17 +94,27 @@ const keymap = {
t: focus, t: focus,
}; };
let timelines = ['home']; let timelines = [];
if (isLocalTimelineAvailable && defaultStore.state.showLocalPostsInTimeline === 'home') {
timelines.push('social');
} else {
timelines.push('home');
}
if (isLocalTimelineAvailable) { if (isLocalTimelineAvailable) {
timelines.push('local'); timelines.push('local');
} }
if (isLocalTimelineAvailable && defaultStore.state.showLocalPostsInTimeline === 'home') {
timelines.push('home');
} else if (isLocalTimelineAvailable) {
timelines.push('social');
}
if (isRecommendedTimelineAvailable) { if (isRecommendedTimelineAvailable) {
timelines.push('recommended'); timelines.push('recommended');
} }
if (isLocalTimelineAvailable) {
timelines.push('social');
}
if (isGlobalTimelineAvailable) { if (isGlobalTimelineAvailable) {
timelines.push('global'); timelines.push('global');
} }
@ -220,13 +230,26 @@ const headerActions = $computed(() => [
}*/, }*/,
]); ]);
// Swap home timeline with social's functionality
const headerTabs = $computed(() => [ const headerTabs = $computed(() => [
{ ...(isLocalTimelineAvailable && defaultStore.state.showLocalPostsInTimeline === 'home'
key: 'home', ? [
title: i18n.ts._timelines.home, {
icon: 'ph-house ph-bold ph-lg', key: 'social',
iconOnly: true, title: i18n.ts._timelines.home,
}, icon: 'ph-house ph-bold ph-lg',
iconOnly: true,
},
]
: [
{
key: 'home',
title: i18n.ts._timelines.home,
icon: 'ph-house ph-bold ph-lg',
iconOnly: true,
}
]),
...(isLocalTimelineAvailable ...(isLocalTimelineAvailable
? [ ? [
{ {
@ -237,6 +260,24 @@ const headerTabs = $computed(() => [
}, },
] ]
: []), : []),
...(isLocalTimelineAvailable && defaultStore.state.showLocalPostsInTimeline === 'home'
? [
{
key: 'home',
title: i18n.ts._timelines.social,
icon: 'ph-handshake ph-bold ph-lg',
iconOnly: true,
},
]
: isLocalTimelineAvailable ? [
{
key: 'social',
title: i18n.ts._timelines.social,
icon: 'ph-handshake ph-bold ph-lg',
iconOnly: true,
},
]
: []),
...(isRecommendedTimelineAvailable ...(isRecommendedTimelineAvailable
? [ ? [
{ {
@ -247,16 +288,6 @@ const headerTabs = $computed(() => [
}, },
] ]
: []), : []),
...(isLocalTimelineAvailable
? [
{
key: 'social',
title: i18n.ts._timelines.social,
icon: 'ph-handshake ph-bold ph-lg',
iconOnly: true,
},
]
: []),
...(isGlobalTimelineAvailable ...(isGlobalTimelineAvailable
? [ ? [
{ {
@ -275,13 +306,17 @@ definePageMetadata(
icon: icon:
src === 'local' src === 'local'
? 'ph-users ph-bold ph-lg' ? 'ph-users ph-bold ph-lg'
: src === 'social' : src === 'social' && defaultStore.state.showLocalPostsInTimeline === 'home'
? 'ph-handshake ph-bold ph-lg' ? 'ph-house ph-bold ph-lg'
: src === 'recommended' : src === 'social'
? 'ph-thumbs-up ph-bold ph-lg' ? 'ph-handshake ph-bold ph-lg'
: src === 'global' : src === 'recommended'
? 'ph-planet ph-bold ph-lg' ? 'ph-thumbs-up ph-bold ph-lg'
: 'ph-house ph-bold ph-lg', : src === 'global'
? 'ph-planet ph-bold ph-lg'
: src === 'home' && defaultStore.state.showLocalPostsInTimeline === 'home'
? 'ph-handshake ph-bold ph-lg'
: 'ph-house ph-bold ph-lg',
})), })),
); );

View File

@ -133,6 +133,10 @@ export const defaultStore = markRaw(
where: "device", where: "device",
default: null as null | "smartphone" | "tablet" | "desktop", default: null as null | "smartphone" | "tablet" | "desktop",
}, },
showLocalPostsInTimeline: {
where: "device",
default: "home" as "home" | "social",
},
serverDisconnectedBehavior: { serverDisconnectedBehavior: {
where: "device", where: "device",
default: "nothing" as "nothing" | "quiet" | "reload" | "dialog", default: "nothing" as "nothing" | "quiet" | "reload" | "dialog",