Merge branch 'develop' of https://firefish.dev/firefish/firefish into refactor/types

This commit is contained in:
Lhcfl 2024-04-23 10:28:11 +08:00
commit 07d39cb5ac
40 changed files with 225 additions and 105 deletions

23
Cargo.lock generated
View file

@ -222,6 +222,7 @@ dependencies = [
"rand", "rand",
"redis", "redis",
"regex", "regex",
"rmp-serde",
"schemars", "schemars",
"sea-orm", "sea-orm",
"serde", "serde",
@ -2012,6 +2013,28 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "rmp"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
dependencies = [
"byteorder",
"num-traits",
"paste",
]
[[package]]
name = "rmp-serde"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "938a142ab806f18b88a97b0dea523d39e0fd730a064b035726adcfc58a8a5188"
dependencies = [
"byteorder",
"rmp",
"serde",
]
[[package]] [[package]]
name = "rsa" name = "rsa"
version = "0.9.6" version = "0.9.6"

View file

@ -28,6 +28,7 @@ quote = "1.0.36"
rand = "0.8.5" rand = "0.8.5"
redis = "0.25.3" redis = "0.25.3"
regex = "1.10.4" regex = "1.10.4"
rmp-serde = "1.2.0"
schemars = "0.8.16" schemars = "0.8.16"
sea-orm = "0.12.15" sea-orm = "0.12.15"
serde = "1.0.197" serde = "1.0.197"

View file

@ -69,7 +69,7 @@ renameFile: Преименуване на файла
_widgets: _widgets:
activity: Дейност activity: Дейност
notifications: Известия notifications: Известия
timeline: Инфопоток timeline: Хронология
clock: Часовник clock: Часовник
trends: Актуални trends: Актуални
photos: Снимки photos: Снимки
@ -187,7 +187,7 @@ notesAndReplies: Публикации и отговори
noSuchUser: Потребителят не е намерен noSuchUser: Потребителят не е намерен
pinnedPages: Закачени страници pinnedPages: Закачени страници
pinLimitExceeded: Не може да закачаш повече публикации pinLimitExceeded: Не може да закачаш повече публикации
flagShowTimelineReplies: Показване на отговори в инфопотока flagShowTimelineReplies: Показване на отговори в хронологията
followersCount: Брой последователи followersCount: Брой последователи
receivedReactionsCount: Брой получени реакции receivedReactionsCount: Брой получени реакции
federation: Федерация federation: Федерация
@ -340,7 +340,7 @@ _deck:
_columns: _columns:
notifications: Известия notifications: Известия
mentions: Споменавания mentions: Споменавания
tl: Инфопоток tl: Хронология
direct: Директни съобщения direct: Директни съобщения
list: Списък list: Списък
antenna: Антена antenna: Антена
@ -375,7 +375,7 @@ basicSettings: Основни настройки
otherSettings: Други настройки otherSettings: Други настройки
openInWindow: Отваряне в прозорец openInWindow: Отваряне в прозорец
profile: Профил profile: Профил
timeline: Инфопоток timeline: Хронология
noAccountDescription: Този потребител все още не е написал своята биография. noAccountDescription: Този потребител все още не е написал своята биография.
login: Вход login: Вход
loggingIn: Вписване loggingIn: Вписване
@ -558,12 +558,12 @@ _visibility:
specified: Директна specified: Директна
localOnly: Само местни localOnly: Само местни
public: Общодостъпна public: Общодостъпна
publicDescription: Публикацията ще бъде видима във всички публични инфопотоци publicDescription: Публикацията ще бъде видима във всички публични хронологии
home: Скрита home: Скрита
localOnlyDescription: Не е видима за отдалечени потребители localOnlyDescription: Не е видима за отдалечени потребители
specifiedDescription: Видима само за определени потребители specifiedDescription: Видима само за определени потребители
followersDescription: Видима само за последователите ти и споменатите потребители followersDescription: Видима само за последователите ти и споменатите потребители
homeDescription: Публикуване само в началния инфопоток homeDescription: Публикуване само в началната хронология
explore: Разглеждане explore: Разглеждане
theme: Теми theme: Теми
wallpaper: Тапет wallpaper: Тапет
@ -594,21 +594,21 @@ _tutorial:
да разберат дали искат да видят вашите публикации или да ви следват. да разберат дали искат да видят вашите публикации или да ви следват.
title: Как се използва Firefish title: Как се използва Firefish
step1_1: Добре дошли! step1_1: Добре дошли!
step5_1: Инфопотоци, инфопотоци навсякъде! step5_1: Хронологии, хронологии навсякъде!
step3_1: Сега е време да последвате няколко хора! step3_1: Сега е време да последвате няколко хора!
step1_2: Нека да ви настроим. Ще бъдете готови за нула време! step1_2: Нека да ви настроим. Ще бъдете готови за нула време!
step5_3: Началният {icon} инфопоток е мястото, където можете да видите публикации step5_3: Началната {icon} хронология е мястото, където можете да видите публикации
от акаунтите, които следвате. от акаунтите, които следвате.
step6_1: И така, какво е това място? step6_1: И така, какво е това място?
step5_7: Глобалният {icon} инфопоток е мястото, където можете да видите публикации step5_7: Глобалната {icon} хронология е мястото, където можете да видите публикации
от всеки друг свързан сървър. от всеки друг свързан сървър.
step4_2: За първата си публикация някои хора обичат да правят публикация {introduction} step4_2: За първата си публикация някои хора обичат да правят публикация {introduction}
или просто „Здравей свят!“ или просто „Здравей свят!“
step5_2: Вашият сървър има активирани {timelines} различни инфопотоци. step5_2: Вашият сървър има активирани {timelines} различни хронологии.
step5_4: Местният {icon} инфопоток е мястото, където можете да видите публикации step5_4: Местната {icon} хронология е мястото, където можете да видите публикации
от всички останали на този сървър. от всички останали на този сървър.
step5_5: Социалният {icon} инфопоток е комбинация от Началния и Местния инфопоток. step5_5: Социалната {icon} хронология е комбинация от Началната и Местната хронология.
step5_6: Препоръчаният {icon} инфопоток е мястото, където можете да видите публикации step5_6: Препоръчаната {icon} хронология е мястото, където можете да видите публикации
от сървъри, препоръчани от администраторите. от сървъри, препоръчани от администраторите.
step6_4: Сега отидете, изследвайте и се забавлявайте! step6_4: Сега отидете, изследвайте и се забавлявайте!
step6_3: Всеки сървър работи по различни начини и не всички сървъри работят с Firefish. step6_3: Всеки сървър работи по различни начини и не всички сървъри работят с Firefish.
@ -754,7 +754,7 @@ _feeds:
general: Общи general: Общи
metadata: Метаданни metadata: Метаданни
disk: Диск disk: Диск
featured: Представени featured: Препоръчани
yearsOld: на {age} години yearsOld: на {age} години
reload: Опресняване reload: Опресняване
invites: Покани invites: Покани
@ -778,8 +778,8 @@ uploadFromUrl: Качване от URL адрес
instanceName: Име на сървъра instanceName: Име на сървъра
instanceDescription: Описание на сървъра instanceDescription: Описание на сървъра
accept: Приемане accept: Приемане
enableLocalTimeline: Включване на местния инфопоток enableLocalTimeline: Включване на местната хронология
enableGlobalTimeline: Включване на глобалния инфопоток enableGlobalTimeline: Включване на глобалната хронология
removeMember: Премахване на член removeMember: Премахване на член
isAdmin: Администратор isAdmin: Администратор
isModerator: Модератор isModerator: Модератор
@ -862,8 +862,8 @@ apply: Прилагане
selectAccount: Избор на акаунт selectAccount: Избор на акаунт
muteThread: Заглушаване на нишката muteThread: Заглушаване на нишката
ffVisibility: Видимост на Последвани/Последователи ffVisibility: Видимост на Последвани/Последователи
renoteMute: Заглушаване на подсилванията в инфопотоците renoteMute: Заглуш. на подсилванията в хронолог.
replyMute: Заглушаване на отговорите в инфопотоците replyMute: Заглуш. на отговорите в хронолог.
blockConfirm: Сигурни ли сте, че искате да блокирате този акаунт? blockConfirm: Сигурни ли сте, че искате да блокирате този акаунт?
appearance: Облик appearance: Облик
fontSize: Размер на шрифта fontSize: Размер на шрифта
@ -893,7 +893,7 @@ charts: Диаграми
disablePagesScript: Изключване на AiScript в Страниците disablePagesScript: Изключване на AiScript в Страниците
updatedAt: Обновено на updatedAt: Обновено на
privateDescription: Видима само за теб privateDescription: Видима само за теб
enableTimelineStreaming: Автоматично обновяване на инфопотоците enableTimelineStreaming: Автоматично обновяване на хронологиите
toEdit: Редактиране toEdit: Редактиране
showEmojisInReactionNotifications: Показване на емоджита в известията за реакции showEmojisInReactionNotifications: Показване на емоджита в известията за реакции
rememberNoteVisibility: Запомняне на настройките за видимост на публикациите rememberNoteVisibility: Запомняне на настройките за видимост на публикациите
@ -932,3 +932,11 @@ clientSettings: Настройки за устройството
behavior: Поведение behavior: Поведение
detectPostLanguage: Автоматично откриване на езика и показване на бутон за превеждане detectPostLanguage: Автоматично откриване на езика и показване на бутон за превеждане
за публикации на чужди езици за публикации на чужди езици
replyUnmute: Отмяна на заглушаването на отговорите
searchWords: Думи за търсене / ID или URL за поглеждане
reloadConfirm: Искате ли да опресните хронологията?
enableRecommendedTimeline: Включване на препоръчаната хронология
showGapBetweenNotesInTimeline: Показване на празнина между публикациите в хронологията
lookup: Поглеждане
media: Мултимедия
welcomeBackWithName: Добре дошли отново, {name}

View file

@ -32,6 +32,7 @@ parse-display = { workspace = true }
rand = { workspace = true } rand = { workspace = true }
redis = { workspace = true } redis = { workspace = true }
regex = { workspace = true } regex = { workspace = true }
rmp-serde = { workspace = true }
schemars = { workspace = true, features = ["chrono"] } schemars = { workspace = true, features = ["chrono"] }
sea-orm = { workspace = true, features = ["sqlx-postgres", "runtime-tokio-rustls"] } sea-orm = { workspace = true, features = ["sqlx-postgres", "runtime-tokio-rustls"] }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }

View file

@ -11,3 +11,4 @@ pub mod meta;
pub mod nyaify; pub mod nyaify;
pub mod password; pub mod password;
pub mod reaction; pub mod reaction;
pub mod redis_cache;

View file

@ -0,0 +1,84 @@
use crate::database::{redis_conn, redis_key};
use redis::{Commands, RedisError};
use serde::{Deserialize, Serialize};
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Redis error: {0}")]
RedisError(#[from] RedisError),
#[error("Data serialization error: {0}")]
SerializeError(#[from] rmp_serde::encode::Error),
#[error("Data deserialization error: {0}")]
DeserializeError(#[from] rmp_serde::decode::Error),
}
pub fn set_cache<V: for<'a> Deserialize<'a> + Serialize>(
key: &str,
value: &V,
expire_seconds: u64,
) -> Result<(), Error> {
redis_conn()?.set_ex(
redis_key(key),
rmp_serde::encode::to_vec(&value)?,
expire_seconds,
)?;
Ok(())
}
pub fn get_cache<V: for<'a> Deserialize<'a> + Serialize>(key: &str) -> Result<Option<V>, Error> {
let serialized_value: Option<Vec<u8>> = redis_conn()?.get(redis_key(key))?;
Ok(match serialized_value {
Some(v) => Some(rmp_serde::from_slice::<V>(v.as_ref())?),
None => None,
})
}
#[cfg(test)]
mod unit_test {
use super::{get_cache, set_cache};
use pretty_assertions::assert_eq;
#[test]
fn set_get_expire() {
#[derive(serde::Deserialize, serde::Serialize, PartialEq, Debug)]
struct Data {
id: u32,
kind: String,
}
let key_1 = "CARGO_TEST_CACHE_KEY_1";
let value_1: Vec<i32> = vec![1, 2, 3, 4, 5];
let key_2 = "CARGO_TEST_CACHE_KEY_2";
let value_2 = "Hello fedizens".to_string();
let key_3 = "CARGO_TEST_CACHE_KEY_3";
let value_3 = Data {
id: 1000000007,
kind: "prime number".to_string(),
};
set_cache(key_1, &value_1, 1).unwrap();
set_cache(key_2, &value_2, 1).unwrap();
set_cache(key_3, &value_3, 1).unwrap();
let cached_value_1: Vec<i32> = get_cache(key_1).unwrap().unwrap();
let cached_value_2: String = get_cache(key_2).unwrap().unwrap();
let cached_value_3: Data = get_cache(key_3).unwrap().unwrap();
assert_eq!(value_1, cached_value_1);
assert_eq!(value_2, cached_value_2);
assert_eq!(value_3, cached_value_3);
// wait for the cache to expire
std::thread::sleep(std::time::Duration::from_millis(1100));
let expired_value_1: Option<Vec<i32>> = get_cache(key_1).unwrap();
let expired_value_2: Option<Vec<i32>> = get_cache(key_2).unwrap();
let expired_value_3: Option<Vec<i32>> = get_cache(key_3).unwrap();
assert!(expired_value_1.is_none());
assert!(expired_value_2.is_none());
assert!(expired_value_3.is_none());
}
}

View file

@ -8,7 +8,7 @@
<i :class="icon('ph-dots-three-outline')"></i> <i :class="icon('ph-dots-three-outline')"></i>
</button> </button>
<button <button
v-if="!hideFollowButton && isSignedIn && me!.id != user.id" v-if="!hideFollowButton && isSignedIn(me) && me.id != user.id"
v-tooltip="full ? null : `${state} ${user.name || user.username}`" v-tooltip="full ? null : `${state} ${user.name || user.username}`"
class="kpoogebi _button follow-button" class="kpoogebi _button follow-button"
:class="{ :class="{

View file

@ -51,9 +51,9 @@ const canonical =
const url = `/${canonical}`; const url = `/${canonical}`;
const isMe = const isMe =
isSignedIn && isSignedIn(me) &&
`@${props.username}@${toUnicode(props.host)}`.toLowerCase() === `@${props.username}@${toUnicode(props.host)}`.toLowerCase() ===
`@${me!.username}@${toUnicode(localHost)}`.toLowerCase(); `@${me.username}@${toUnicode(localHost)}`.toLowerCase();
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -235,7 +235,7 @@
<XQuoteButton class="button" :note="appearNote" /> <XQuoteButton class="button" :note="appearNote" />
<button <button
v-if=" v-if="
isSignedIn && isSignedIn(me) &&
isForeignLanguage && isForeignLanguage &&
translation == null translation == null
" "
@ -370,7 +370,7 @@ const reactButton = ref<HTMLElement | null>(null);
const appearNote = computed(() => const appearNote = computed(() =>
isRenote ? (note.value.renote as NoteType) : note.value, isRenote ? (note.value.renote as NoteType) : note.value,
); );
const isMyRenote = isSignedIn && me!.id === note.value.userId; const isMyRenote = isSignedIn(me) && me.id === note.value.userId;
// const showContent = ref(false); // const showContent = ref(false);
const isDeleted = ref(false); const isDeleted = ref(false);
const muted = ref( const muted = ref(

View file

@ -127,7 +127,7 @@
<XQuoteButton class="button" :note="appearNote" /> <XQuoteButton class="button" :note="appearNote" />
<button <button
v-if=" v-if="
isSignedIn && isSignedIn(me) &&
isForeignLanguage && isForeignLanguage &&
translation == null translation == null
" "

View file

@ -16,7 +16,7 @@
<MkButton <MkButton
v-else-if=" v-else-if="
!showOnlyToRegister && !showOnlyToRegister &&
(isSignedIn ? pushRegistrationInServer : pushSubscription) (isSignedIn(me) ? pushRegistrationInServer : pushSubscription)
" "
type="button" type="button"
:primary="false" :primary="false"
@ -31,7 +31,7 @@
{{ i18n.ts.unsubscribePushNotification }} {{ i18n.ts.unsubscribePushNotification }}
</MkButton> </MkButton>
<MkButton <MkButton
v-else-if="isSignedIn && pushRegistrationInServer" v-else-if="isSignedIn(me) && pushRegistrationInServer"
disabled disabled
:rounded="rounded" :rounded="rounded"
:inline="inline" :inline="inline"
@ -142,7 +142,7 @@ async function unsubscribe() {
pushRegistrationInServer.value = undefined; pushRegistrationInServer.value = undefined;
if (isSignedIn && accounts.length >= 2) { if (isSignedIn(me) && accounts.length >= 2) {
apiWithDialog("sw/unregister", { apiWithDialog("sw/unregister", {
i: me.token, i: me.token,
endpoint, endpoint,
@ -189,7 +189,7 @@ if (navigator.serviceWorker == null) {
if ( if (
instance.swPublickey && instance.swPublickey &&
"PushManager" in window && "PushManager" in window &&
isSignedIn && isSignedIn(me) &&
me.token me.token
) { ) {
supported.value = true; supported.value = true;

View file

@ -23,12 +23,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import type { entitites } from "firefish-js"; import type { entities } from "firefish-js";
import XDetails from "@/components/MkReactionsViewer.details.vue"; import XDetails from "@/components/MkReactionsViewer.details.vue";
import XReactionIcon from "@/components/MkReactionIcon.vue"; import XReactionIcon from "@/components/MkReactionIcon.vue";
import * as os from "@/os"; import * as os from "@/os";
import { useTooltip } from "@/scripts/use-tooltip"; import { useTooltip } from "@/scripts/use-tooltip";
import { isSignedIn } from "@/me"; import { isSignedIn, me } from "@/me";
const props = defineProps<{ const props = defineProps<{
reaction: string; reaction: string;
@ -43,7 +43,9 @@ const emit = defineEmits<{
const buttonRef = ref<HTMLElement>(); const buttonRef = ref<HTMLElement>();
const canToggle = computed(() => isSignedIn && !props.reaction.match(/@\w/)); const canToggle = computed(
() => isSignedIn(me) && !props.reaction.match(/@\w/),
);
const toggleReaction = () => { const toggleReaction = () => {
if (!canToggle.value) return; if (!canToggle.value) return;

View file

@ -30,7 +30,7 @@ const reactionsEl = ref<HTMLElement>();
const initialReactions = new Set(Object.keys(props.note.reactions)); const initialReactions = new Set(Object.keys(props.note.reactions));
const isMe = computed(() => isSignedIn && me.id === props.note.userId); const isMe = computed(() => isSignedIn(me) && me.id === props.note.userId);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -74,10 +74,10 @@ useTooltip(buttonRef, async (showing) => {
const hasRenotedBefore = ref(false); const hasRenotedBefore = ref(false);
if (isSignedIn) { if (isSignedIn(me)) {
os.api("notes/renotes", { os.api("notes/renotes", {
noteId: props.note.id, noteId: props.note.id,
userId: me!.id, userId: me.id,
limit: 1, limit: 1,
}).then((res) => { }).then((res) => {
hasRenotedBefore.value = res.length > 0; hasRenotedBefore.value = res.length > 0;

View file

@ -1,6 +1,6 @@
<template> <template>
<MkInfo <MkInfo
v-if="tlHint && !tlHintClosed && isSignedIn" v-if="tlHint && !tlHintClosed && isSignedIn(me)"
:closeable="true" :closeable="true"
class="_gap" class="_gap"
@close="closeHint" @close="closeHint"
@ -120,7 +120,7 @@ const prepend = (note: entities.Note) => {
emit("note"); emit("note");
if (props.sound) { if (props.sound) {
sound.play(isSignedIn && note.userId === me?.id ? "noteMy" : "note"); sound.play(isSignedIn(me) && note.userId === me?.id ? "noteMy" : "note");
} }
}; };

View file

@ -8,7 +8,7 @@
:class="{ detailed }" :class="{ detailed }"
> >
<span <span
v-if="isSignedIn && me.id !== user.id && user.isFollowed" v-if="isSignedIn(me) && me.id !== user.id && user.isFollowed"
class="followed" class="followed"
>{{ i18n.ts.followsYou }}</span >{{ i18n.ts.followsYou }}</span
> >
@ -80,7 +80,7 @@
<div class="buttons"> <div class="buttons">
<slot> <slot>
<MkFollowButton <MkFollowButton
v-if="isSignedIn && user.id !== me.id" v-if="isSignedIn(me) && user.id !== me.id"
:user="user" :user="user"
/> />
</slot> </slot>

View file

@ -262,7 +262,7 @@ function checkForSplash() {
} }
if ( if (
isSignedIn && isSignedIn(me) &&
defaultStore.state.tutorial === -1 && defaultStore.state.tutorial === -1 &&
!["/announcements", "/announcements/"].includes(window.location.pathname) !["/announcements", "/announcements/"].includes(window.location.pathname)
) { ) {
@ -417,7 +417,7 @@ function checkForSplash() {
s: search, s: search,
}; };
if (isSignedIn) { if (isSignedIn(me)) {
// only add post shortcuts if logged in // only add post shortcuts if logged in
hotkeys["p|n"] = post; hotkeys["p|n"] = post;

View file

@ -8,7 +8,7 @@ export const me = accountData
? reactive(JSON.parse(accountData) as Account) ? reactive(JSON.parse(accountData) as Account)
: null; : null;
export const isSignedIn = me != null; export const isSignedIn = (i: typeof me): i is Account => i != null;
export const isModerator = me != null && (me.isModerator || me.isAdmin); export const isModerator = me != null && (me.isModerator || me.isAdmin);
export const isEmojiMod = isModerator || me?.emojiModPerm !== "unauthorized"; export const isEmojiMod = isModerator || me?.emojiModPerm !== "unauthorized";
export const isAdmin = me?.isAdmin; export const isAdmin = me?.isAdmin;

View file

@ -11,21 +11,21 @@ export const navbarItemDef = reactive({
notifications: { notifications: {
title: "notifications", title: "notifications",
icon: `${icon("ph-bell")}`, icon: `${icon("ph-bell")}`,
show: computed(() => isSignedIn), show: computed(() => isSignedIn(me)),
indicated: computed(() => me?.hasUnreadNotification), indicated: computed(() => me?.hasUnreadNotification),
to: "/my/notifications", to: "/my/notifications",
}, },
messaging: { messaging: {
title: "messaging", title: "messaging",
icon: `${icon("ph-chats-teardrop")}`, icon: `${icon("ph-chats-teardrop")}`,
show: computed(() => isSignedIn), show: computed(() => isSignedIn(me)),
indicated: computed(() => me?.hasUnreadMessagingMessage), indicated: computed(() => me?.hasUnreadMessagingMessage),
to: "/my/messaging", to: "/my/messaging",
}, },
drive: { drive: {
title: "drive", title: "drive",
icon: `${icon("ph-cloud")}`, icon: `${icon("ph-cloud")}`,
show: computed(() => isSignedIn), show: computed(() => isSignedIn(me)),
to: "/my/drive", to: "/my/drive",
}, },
followRequests: { followRequests: {
@ -54,19 +54,19 @@ export const navbarItemDef = reactive({
lists: { lists: {
title: "lists", title: "lists",
icon: `${icon("ph-list-bullets")}`, icon: `${icon("ph-list-bullets")}`,
show: computed(() => isSignedIn), show: computed(() => isSignedIn(me)),
to: "/my/lists", to: "/my/lists",
}, },
antennas: { antennas: {
title: "antennas", title: "antennas",
icon: `${icon("ph-flying-saucer")}`, icon: `${icon("ph-flying-saucer")}`,
show: computed(() => isSignedIn), show: computed(() => isSignedIn(me)),
to: "/my/antennas", to: "/my/antennas",
}, },
favorites: { favorites: {
title: "favorites", title: "favorites",
icon: `${icon("ph-bookmark-simple")}`, icon: `${icon("ph-bookmark-simple")}`,
show: computed(() => isSignedIn), show: computed(() => isSignedIn(me)),
to: "/my/favorites", to: "/my/favorites",
}, },
pages: { pages: {
@ -82,7 +82,7 @@ export const navbarItemDef = reactive({
clips: { clips: {
title: "clips", title: "clips",
icon: `${icon("ph-paperclip")}`, icon: `${icon("ph-paperclip")}`,
show: computed(() => isSignedIn), show: computed(() => isSignedIn(me)),
to: "/my/clips", to: "/my/clips",
}, },
channels: { channels: {

View file

@ -17,7 +17,7 @@
> >
<div class="_title"> <div class="_title">
<h3> <h3>
<span v-if="isSignedIn && !announcement.isRead"> <span v-if="isSignedIn(me) && !announcement.isRead">
🆕&nbsp; 🆕&nbsp;
</span> </span>
{{ announcement.title }} {{ announcement.title }}
@ -37,7 +37,7 @@
/> />
</div> </div>
<div <div
v-if="isSignedIn && !announcement.isRead" v-if="isSignedIn(me) && !announcement.isRead"
class="_footer" class="_footer"
> >
<MkButton primary @click="read(announcement.id)" <MkButton primary @click="read(announcement.id)"
@ -60,7 +60,7 @@ import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
import { isSignedIn } from "@/me"; import { isSignedIn, me } from "@/me";
const pagination = { const pagination = {
endpoint: "announcements" as const, endpoint: "announcements" as const,

View file

@ -1,8 +1,8 @@
<template> <template>
<div v-if="isSignedIn && fetching" class=""> <div v-if="isSignedIn(me) && fetching" class="">
<MkLoading /> <MkLoading />
</div> </div>
<div v-else-if="isSignedIn"> <div v-else-if="isSignedIn(me)">
<XForm <XForm
v-if="state == 'waiting'" v-if="state == 'waiting'"
ref="form" ref="form"
@ -52,7 +52,7 @@ import MkSignin from "@/components/MkSignin.vue";
import MkKeyValue from "@/components/MkKeyValue.vue"; import MkKeyValue from "@/components/MkKeyValue.vue";
import * as os from "@/os"; import * as os from "@/os";
import { signIn } from "@/account"; import { signIn } from "@/account";
import { isSignedIn } from "@/me"; import { isSignedIn, me } from "@/me";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
const props = defineProps<{ const props = defineProps<{
@ -64,7 +64,7 @@ const fetching = ref(true);
const auth_code = ref(""); const auth_code = ref("");
onMounted(() => { onMounted(() => {
if (!isSignedIn) return; if (!isSignedIn(me)) return;
os.api("auth/session/show", { token: props.token }) os.api("auth/session/show", { token: props.token })
.then((sess: any) => { .then((sess: any) => {

View file

@ -51,7 +51,7 @@ const pagination = {
}; };
const isOwned: boolean | null = computed<boolean | null>( const isOwned: boolean | null = computed<boolean | null>(
() => isSignedIn && clip.value && me.id === clip.value.userId, () => isSignedIn(me) && clip.value && me.id === clip.value.userId,
); );
watch( watch(

View file

@ -17,7 +17,7 @@
<XUserList :pagination="pinnedUsers" /> <XUserList :pagination="pinnedUsers" />
</MkFolder> </MkFolder>
<MkFolder <MkFolder
v-if="isSignedIn" v-if="isSignedIn(me)"
class="_gap" class="_gap"
persist-key="explore-popular-users" persist-key="explore-popular-users"
> >
@ -31,7 +31,7 @@
<XUserList :pagination="popularUsers" /> <XUserList :pagination="popularUsers" />
</MkFolder> </MkFolder>
<MkFolder <MkFolder
v-if="isSignedIn" v-if="isSignedIn(me)"
class="_gap" class="_gap"
persist-key="explore-recently-updated-users" persist-key="explore-recently-updated-users"
> >
@ -45,7 +45,7 @@
<XUserList :pagination="recentlyUpdatedUsers" /> <XUserList :pagination="recentlyUpdatedUsers" />
</MkFolder> </MkFolder>
<MkFolder <MkFolder
v-if="isSignedIn" v-if="isSignedIn(me)"
class="_gap" class="_gap"
persist-key="explore-recently-registered-users" persist-key="explore-recently-registered-users"
> >
@ -103,7 +103,7 @@
<XUserList :pagination="tagUsers" /> <XUserList :pagination="tagUsers" />
</MkFolder> </MkFolder>
<template v-if="tag == null && isSignedIn"> <template v-if="tag == null && isSignedIn(me)">
<MkFolder class="_gap"> <MkFolder class="_gap">
<template #header <template #header
><i ><i
@ -146,7 +146,7 @@ import MkFolder from "@/components/MkFolder.vue";
import MkTab from "@/components/MkTab.vue"; import MkTab from "@/components/MkTab.vue";
import * as os from "@/os"; import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { isSignedIn } from "@/me"; import { isSignedIn, me } from "@/me";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
const props = defineProps<{ const props = defineProps<{

View file

@ -59,7 +59,7 @@
<div class="other"> <div class="other">
<button <button
v-if=" v-if="
isSignedIn && me!.id === post.user.id isSignedIn(me) && me.id === post.user.id
" "
v-tooltip="i18n.ts.toEdit" v-tooltip="i18n.ts.toEdit"
v-click-anime v-click-anime
@ -105,7 +105,7 @@
<MkAcct :user="post.user" /> <MkAcct :user="post.user" />
</div> </div>
<MkFollowButton <MkFollowButton
v-if="!isSignedIn || me!.id != post.user.id" v-if="!isSignedIn(me) || me.id != post.user.id"
:user="post.user" :user="post.user"
:inline="true" :inline="true"
:transparent="false" :transparent="false"

View file

@ -51,7 +51,7 @@
/></MkA> /></MkA>
<template <template
v-if=" v-if="
isSignedIn && me.id === page.userId isSignedIn(me) && me.id === page.userId
" "
> >
<MkA <MkA
@ -159,7 +159,7 @@
</div> </div>
<!-- <div class="links"> <!-- <div class="links">
<MkA :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ i18n.ts._pages.viewSource }}</MkA> <MkA :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ i18n.ts._pages.viewSource }}</MkA>
<template v-if="isSignedIn && me.id === page.userId"> <template v-if="isSignedIn(me) && me.id === page.userId">
<MkA :to="`/pages/edit/${page.id}`" class="link">{{ i18n.ts._pages.editThisPage }}</MkA> <MkA :to="`/pages/edit/${page.id}`" class="link">{{ i18n.ts._pages.editThisPage }}</MkA>
<button v-if="me.pinnedPageId === page.id" class="link _textButton" @click="pin(false)">{{ i18n.ts.unpin }}</button> <button v-if="me.pinnedPageId === page.id" class="link _textButton" @click="pin(false)">{{ i18n.ts.unpin }}</button>
<button v-else class="link _textButton" @click="pin(true)">{{ i18n.ts.pin }}</button> <button v-else class="link _textButton" @click="pin(true)">{{ i18n.ts.pin }}</button>

View file

@ -64,7 +64,7 @@ const fetching = ref(true);
const usage = ref<any>(null); const usage = ref<any>(null);
const capacity = ref<any>(null); const capacity = ref<any>(null);
const uploadFolder = ref<any>(null); const uploadFolder = ref<any>(null);
const alwaysMarkNsfw = ref<boolean>(isSignedIn && me.alwaysMarkNsfw); const alwaysMarkNsfw = ref<boolean>(isSignedIn(me) && me.alwaysMarkNsfw);
const meterStyle = computed(() => { const meterStyle = computed(() => {
return { return {

View file

@ -65,7 +65,7 @@ import * as os from "@/os";
import { ColdDeviceStorage, defaultStore } from "@/store"; import { ColdDeviceStorage, defaultStore } from "@/store";
import { unisonReload } from "@/scripts/unison-reload"; import { unisonReload } from "@/scripts/unison-reload";
import { useStream } from "@/stream"; import { useStream } from "@/stream";
import { isSignedIn } from "@/me"; import { isSignedIn, me } from "@/me";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { host, version } from "@/config"; import { host, version } from "@/config";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
@ -170,7 +170,7 @@ interface Profile {
}; };
} }
const connection = isSignedIn && stream.useChannel("main"); const connection = isSignedIn(me) && stream.useChannel("main");
const profiles = ref<Record<string, Profile> | null>(null); const profiles = ref<Record<string, Profile> | null>(null);

View file

@ -75,28 +75,28 @@ import * as os from "@/os";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { instance } from "@/instance"; import { instance } from "@/instance";
import { isModerator, isSignedIn } from "@/me"; import { isModerator, isSignedIn, me } from "@/me";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
import { deviceKind } from "@/scripts/device-kind"; import { deviceKind } from "@/scripts/device-kind";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
import "swiper/scss"; import "swiper/scss";
import "swiper/scss/virtual"; import "swiper/scss/virtual";
if (isSignedIn && defaultStore.reactiveState.tutorial.value !== -1) { if (isSignedIn(me) && defaultStore.reactiveState.tutorial.value !== -1) {
os.popup(XTutorial, {}, {}, "closed"); os.popup(XTutorial, {}, {}, "closed");
} }
const isHomeTimelineAvailable = isSignedIn; const isHomeTimelineAvailable = isSignedIn(me);
const isLocalTimelineAvailable = const isLocalTimelineAvailable =
(!instance.disableLocalTimeline && (!instance.disableLocalTimeline &&
(isSignedIn || instance.enableGuestTimeline)) || (isSignedIn(me) || instance.enableGuestTimeline)) ||
isModerator; isModerator;
const isSocialTimelineAvailable = isLocalTimelineAvailable && isSignedIn; const isSocialTimelineAvailable = isLocalTimelineAvailable && isSignedIn(me);
const isRecommendedTimelineAvailable = const isRecommendedTimelineAvailable =
!instance.disableRecommendedTimeline && isSignedIn; !instance.disableRecommendedTimeline && isSignedIn(me);
const isGlobalTimelineAvailable = const isGlobalTimelineAvailable =
(!instance.disableGlobalTimeline && (!instance.disableGlobalTimeline &&
(isSignedIn || instance.enableGuestTimeline)) || (isSignedIn(me) || instance.enableGuestTimeline)) ||
isModerator; isModerator;
const keymap = { const keymap = {
t: focus, t: focus,
@ -205,7 +205,7 @@ function focus(): void {
} }
const headerActions = computed(() => const headerActions = computed(() =>
isSignedIn isSignedIn(me)
? [ ? [
{ {
icon: `${icon("ph-list-bullets")}`, icon: `${icon("ph-list-bullets")}`,

View file

@ -67,7 +67,7 @@
</div> </div>
<span <span
v-if=" v-if="
isSignedIn && isSignedIn(me) &&
me.id !== user.id && me.id !== user.id &&
user.isFollowed user.isFollowed
" "
@ -120,7 +120,7 @@
/> />
<span <span
v-if=" v-if="
isSignedIn && isSignedIn(me) &&
me.id !== user.id && me.id !== user.id &&
user.isFollowed user.isFollowed
" "
@ -319,7 +319,7 @@
/> />
</div> </div>
<MkInfo <MkInfo
v-else-if="isSignedIn && me.id === user.id" v-else-if="isSignedIn(me) && me.id === user.id"
style="margin: 12px 0" style="margin: 12px 0"
>{{ i18n.ts.userPagePinTip }}</MkInfo >{{ i18n.ts.userPagePinTip }}</MkInfo
> >

View file

@ -93,7 +93,7 @@ const headerTabs = computed(() =>
title: i18n.ts.media, title: i18n.ts.media,
icon: `${icon("ph-grid-four")}`, icon: `${icon("ph-grid-four")}`,
}, },
...((isSignedIn && me.id === user.value.id) || ...((isSignedIn(me) && me.id === user.value.id) ||
user.value.publicReactions user.value.publicReactions
? [ ? [
{ {

View file

@ -18,7 +18,7 @@ type StateDef = Record<
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never; type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
const stream = useStream(); const stream = useStream();
const connection = isSignedIn && stream.useChannel("main"); const connection = isSignedIn(me) ? stream.useChannel("main") : null;
export class Storage<T extends StateDef> { export class Storage<T extends StateDef> {
public readonly key: string; public readonly key: string;
@ -44,12 +44,12 @@ export class Storage<T extends StateDef> {
const deviceState = JSON.parse( const deviceState = JSON.parse(
localStorage.getItem(this.keyForLocalStorage) || "{}", localStorage.getItem(this.keyForLocalStorage) || "{}",
); );
const deviceAccountState = isSignedIn const deviceAccountState = isSignedIn(me)
? JSON.parse( ? JSON.parse(
localStorage.getItem(`${this.keyForLocalStorage}::${me.id}`) || "{}", localStorage.getItem(`${this.keyForLocalStorage}::${me.id}`) || "{}",
) )
: {}; : {};
const registryCache = isSignedIn const registryCache = isSignedIn(me)
? JSON.parse( ? JSON.parse(
localStorage.getItem(`${this.keyForLocalStorage}::cache::${me.id}`) || localStorage.getItem(`${this.keyForLocalStorage}::cache::${me.id}`) ||
"{}", "{}",
@ -66,7 +66,7 @@ export class Storage<T extends StateDef> {
state[k] = deviceState[k]; state[k] = deviceState[k];
} else if ( } else if (
v.where === "account" && v.where === "account" &&
isSignedIn && isSignedIn(me) &&
Object.prototype.hasOwnProperty.call(registryCache, k) Object.prototype.hasOwnProperty.call(registryCache, k)
) { ) {
state[k] = registryCache[k]; state[k] = registryCache[k];
@ -86,7 +86,7 @@ export class Storage<T extends StateDef> {
this.state = state as typeof this.state; this.state = state as typeof this.state;
this.reactiveState = reactiveState as typeof this.reactiveState; this.reactiveState = reactiveState as typeof this.reactiveState;
if (isSignedIn) { if (isSignedIn(me)) {
// なぜかsetTimeoutしないとapi関数内でエラーになる(おそらく循環参照してることに原因がありそう) // なぜかsetTimeoutしないとapi関数内でエラーになる(おそらく循環参照してることに原因がありそう)
window.setTimeout(() => { window.setTimeout(() => {
api("i/registry/get-all", { scope: ["client", this.key] }).then( api("i/registry/get-all", { scope: ["client", this.key] }).then(
@ -170,7 +170,7 @@ export class Storage<T extends StateDef> {
break; break;
} }
case "deviceAccount": { case "deviceAccount": {
if (!isSignedIn) break; if (!isSignedIn(me)) break;
const deviceAccountState = JSON.parse( const deviceAccountState = JSON.parse(
localStorage.getItem(`${this.keyForLocalStorage}::${me.id}`) || "{}", localStorage.getItem(`${this.keyForLocalStorage}::${me.id}`) || "{}",
); );
@ -182,7 +182,7 @@ export class Storage<T extends StateDef> {
break; break;
} }
case "account": { case "account": {
if (!isSignedIn) break; if (!isSignedIn(me)) break;
const cache = JSON.parse( const cache = JSON.parse(
localStorage.getItem(`${this.keyForLocalStorage}::cache::${me.id}`) || localStorage.getItem(`${this.keyForLocalStorage}::cache::${me.id}`) ||
"{}", "{}",

View file

@ -293,7 +293,7 @@ export function getNoteMenu(props: {
} }
let menu: MenuItem[]; let menu: MenuItem[];
if (isSignedIn) { if (isSignedIn(me)) {
const statePromise = os.api("notes/state", { const statePromise = os.api("notes/state", {
noteId: appearNote.id, noteId: appearNote.id,
}); });

View file

@ -335,7 +335,7 @@ export function getUserMenu(user, router: Router = mainRouter) {
}, },
] as any; ] as any;
if (isSignedIn && me.id !== user.id) { if (isSignedIn(me) && me.id !== user.id) {
menu = menu.concat([ menu = menu.concat([
{ {
icon: user.isMuted ? "ph-eye ph-lg" : "ph-eye-slash ph-lg", icon: user.isMuted ? "ph-eye ph-lg" : "ph-eye-slash ph-lg",
@ -386,7 +386,7 @@ export function getUserMenu(user, router: Router = mainRouter) {
} }
} }
if (isSignedIn && me.id === user.id) { if (isSignedIn(me) && me.id === user.id) {
menu = menu.concat([ menu = menu.concat([
null, null,
{ {

View file

@ -1,11 +1,11 @@
import { defineAsyncComponent } from "vue"; import { defineAsyncComponent } from "vue";
import { isSignedIn } from "@/me"; import { isSignedIn, me } from "@/me";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { popup } from "@/os"; import { popup } from "@/os";
import { vibrate } from "@/scripts/vibrate"; import { vibrate } from "@/scripts/vibrate";
export function pleaseLogin(path?: string) { export function pleaseLogin(path?: string) {
if (isSignedIn) return; if (isSignedIn(me)) return;
vibrate(100); vibrate(100);
popup( popup(

View file

@ -11,7 +11,7 @@ export function useNoteCapture(props: {
isDeletedRef: Ref<boolean>; isDeletedRef: Ref<boolean>;
}) { }) {
const note = props.note; const note = props.note;
const connection = isSignedIn ? useStream() : null; const connection = isSignedIn(me) ? useStream() : null;
async function onStreamNoteUpdated(noteData): Promise<void> { async function onStreamNoteUpdated(noteData): Promise<void> {
const { type, id, body } = noteData; const { type, id, body } = noteData;
@ -34,7 +34,7 @@ export function useNoteCapture(props: {
note.value.reactions[reaction] = currentCount + 1; note.value.reactions[reaction] = currentCount + 1;
if (isSignedIn && body.userId === me.id) { if (isSignedIn(me) && body.userId === me.id) {
note.value.myReaction = reaction; note.value.myReaction = reaction;
} }
break; break;
@ -48,7 +48,7 @@ export function useNoteCapture(props: {
note.value.reactions[reaction] = Math.max(0, currentCount - 1); note.value.reactions[reaction] = Math.max(0, currentCount - 1);
if (isSignedIn && body.userId === me.id) { if (isSignedIn(me) && body.userId === me.id) {
note.value.myReaction = undefined; note.value.myReaction = undefined;
} }
break; break;
@ -62,7 +62,7 @@ export function useNoteCapture(props: {
choices[choice] = { choices[choice] = {
...choices[choice], ...choices[choice],
votes: choices[choice].votes + 1, votes: choices[choice].votes + 1,
...(isSignedIn && body.userId === me.id ...(isSignedIn(me) && body.userId === me.id
? { ? {
isVoted: true, isVoted: true,
} }

View file

@ -1,6 +1,6 @@
import { markRaw, ref } from "vue"; import { markRaw, ref } from "vue";
import type { ApiTypes, entities } from "firefish-js"; import type { ApiTypes, entities } from "firefish-js";
import { isSignedIn } from "./me"; import { isSignedIn, me } from "./me";
import { Storage } from "./pizzax"; import { Storage } from "./pizzax";
import type { NoteVisibility } from "@/types/note"; import type { NoteVisibility } from "@/types/note";
@ -167,7 +167,7 @@ export const defaultStore = markRaw(
tl: { tl: {
where: "deviceAccount", where: "deviceAccount",
default: { default: {
src: (isSignedIn ? "home" : "local") as src: (isSignedIn(me) ? "home" : "local") as
| "home" | "home"
| "local" | "local"
| "social" | "social"

View file

@ -2,14 +2,14 @@ import type { Theme } from "./scripts/theme";
import { isSignedIn, me } from "@/me"; import { isSignedIn, me } from "@/me";
import { api } from "@/os"; import { api } from "@/os";
const lsCacheKey = isSignedIn ? `themes:${me.id}` : ""; const lsCacheKey = isSignedIn(me) ? `themes:${me.id}` : "";
export function getThemes(): Theme[] { export function getThemes(): Theme[] {
return JSON.parse(localStorage.getItem(lsCacheKey) || "[]"); return JSON.parse(localStorage.getItem(lsCacheKey) || "[]");
} }
export async function fetchThemes(): Promise<void> { export async function fetchThemes(): Promise<void> {
if (!isSignedIn) return; if (!isSignedIn(me)) return;
try { try {
const themes = await api("i/registry/get", { const themes = await api("i/registry/get", {

View file

@ -57,7 +57,7 @@ const onNotification = (notification) => {
sound.play("notification"); sound.play("notification");
}; };
if (isSignedIn) { if (isSignedIn(me)) {
const connection = stream.useChannel("main", null, "UI"); const connection = stream.useChannel("main", null, "UI");
connection.on("notification", onNotification); connection.on("notification", onNotification);

View file

@ -239,7 +239,7 @@ watch(route, () => {
const columns = deckStore.reactiveState.columns; const columns = deckStore.reactiveState.columns;
const layout = deckStore.reactiveState.layout; const layout = deckStore.reactiveState.layout;
const menuIndicated = computed(() => { const menuIndicated = computed(() => {
if (!isSignedIn) return false; if (!isSignedIn(me)) return false;
for (const def in navbarItemDef) { for (const def in navbarItemDef) {
if (navbarItemDef[def].indicated) return true; if (navbarItemDef[def].indicated) return true;
} }

View file

@ -50,7 +50,7 @@ import type { Column } from "./deck-store";
import { removeColumn, updateColumn } from "./deck-store"; import { removeColumn, updateColumn } from "./deck-store";
import XTimeline from "@/components/MkTimeline.vue"; import XTimeline from "@/components/MkTimeline.vue";
import * as os from "@/os"; import * as os from "@/os";
import { isModerator, isSignedIn } from "@/me"; import { isModerator, isSignedIn, me } from "@/me";
import { instance } from "@/instance"; import { instance } from "@/instance";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
@ -72,7 +72,7 @@ const columnActive = ref(true);
onMounted(() => { onMounted(() => {
if (props.column.tl == null) { if (props.column.tl == null) {
setType(); setType();
} else if (isSignedIn) { } else if (isSignedIn(me)) {
disabled.value = disabled.value =
!isModerator && !isModerator &&
((instance.disableLocalTimeline && ((instance.disableLocalTimeline &&