Fixed notification filtering and a bunch of bugs
This commit is contained in:
parent
7c2909bceb
commit
3eef9490b2
|
@ -153,35 +153,3 @@ pub enum UserProfileFfvisibilityEnum {
|
||||||
#[sea_orm(string_value = "public")]
|
#[sea_orm(string_value = "public")]
|
||||||
Public,
|
Public,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy, Serialize, Deserialize)]
|
|
||||||
#[sea_orm(
|
|
||||||
rs_type = "String",
|
|
||||||
db_type = "Enum",
|
|
||||||
enum_name = "user_profile_mutingnotificationtypes_enum"
|
|
||||||
)]
|
|
||||||
pub enum UserProfileMutingnotificationtypesEnum {
|
|
||||||
#[sea_orm(string_value = "app")]
|
|
||||||
App,
|
|
||||||
#[sea_orm(string_value = "follow")]
|
|
||||||
Follow,
|
|
||||||
#[sea_orm(string_value = "followRequestAccepted")]
|
|
||||||
FollowRequestAccepted,
|
|
||||||
#[sea_orm(string_value = "groupInvited")]
|
|
||||||
GroupInvited,
|
|
||||||
#[sea_orm(string_value = "mention")]
|
|
||||||
Mention,
|
|
||||||
#[sea_orm(string_value = "pollEnded")]
|
|
||||||
PollEnded,
|
|
||||||
#[sea_orm(string_value = "pollVote")]
|
|
||||||
PollVote,
|
|
||||||
#[sea_orm(string_value = "quote")]
|
|
||||||
Quote,
|
|
||||||
#[sea_orm(string_value = "reaction")]
|
|
||||||
Reaction,
|
|
||||||
#[sea_orm(string_value = "receiveFollowRequest")]
|
|
||||||
ReceiveFollowRequest,
|
|
||||||
#[sea_orm(string_value = "renote")]
|
|
||||||
Renote,
|
|
||||||
#[sea_orm(string_value = "reply")]
|
|
||||||
Reply,
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||||
|
|
||||||
|
use super::sea_orm_active_enums::NotificationTypeEnum;
|
||||||
use super::sea_orm_active_enums::UserProfileFfvisibilityEnum;
|
use super::sea_orm_active_enums::UserProfileFfvisibilityEnum;
|
||||||
use super::sea_orm_active_enums::UserProfileMutingnotificationtypesEnum;
|
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ pub struct Model {
|
||||||
#[sea_orm(column_name = "mutedWords", column_type = "JsonBinary")]
|
#[sea_orm(column_name = "mutedWords", column_type = "JsonBinary")]
|
||||||
pub muted_words: Json,
|
pub muted_words: Json,
|
||||||
#[sea_orm(column_name = "mutingNotificationTypes")]
|
#[sea_orm(column_name = "mutingNotificationTypes")]
|
||||||
pub muting_notification_types: Vec<UserProfileMutingnotificationtypesEnum>,
|
pub muting_notification_types: Vec<NotificationTypeEnum>,
|
||||||
#[sea_orm(column_name = "noCrawle")]
|
#[sea_orm(column_name = "noCrawle")]
|
||||||
pub no_crawle: bool,
|
pub no_crawle: bool,
|
||||||
#[sea_orm(column_name = "receiveAnnouncementEmail")]
|
#[sea_orm(column_name = "receiveAnnouncementEmail")]
|
||||||
|
|
|
@ -10,6 +10,7 @@ mod m20240107_224446_generated_is_renote;
|
||||||
mod m20240112_215106_remove_pages;
|
mod m20240112_215106_remove_pages;
|
||||||
mod m20240112_234759_remove_gallery;
|
mod m20240112_234759_remove_gallery;
|
||||||
mod m20240115_212109_remove_poll_vote_notification;
|
mod m20240115_212109_remove_poll_vote_notification;
|
||||||
|
mod m20240228_155051_mag_notification_types_muting;
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ impl MigratorTrait for Migrator {
|
||||||
Box::new(m20240112_215106_remove_pages::Migration),
|
Box::new(m20240112_215106_remove_pages::Migration),
|
||||||
Box::new(m20240112_234759_remove_gallery::Migration),
|
Box::new(m20240112_234759_remove_gallery::Migration),
|
||||||
Box::new(m20240115_212109_remove_poll_vote_notification::Migration),
|
Box::new(m20240115_212109_remove_poll_vote_notification::Migration),
|
||||||
|
Box::new(m20240228_155051_mag_notification_types_muting::Migration),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ impl FromQueryResult for NoteData {
|
||||||
let prefix = if prefix.is_empty() {
|
let prefix = if prefix.is_empty() {
|
||||||
note::Entity.base_prefix()
|
note::Entity.base_prefix()
|
||||||
} else {
|
} else {
|
||||||
MagIden::alias(&prefix)
|
MagIden::alias(prefix)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(NoteData {
|
Ok(NoteData {
|
||||||
|
|
|
@ -95,7 +95,7 @@ impl NotificationResolver {
|
||||||
note_resolver: &NoteResolver,
|
note_resolver: &NoteResolver,
|
||||||
user_resolver: &UserResolver,
|
user_resolver: &UserResolver,
|
||||||
) {
|
) {
|
||||||
q.add_aliased_columns::<notification::Entity>(¬ification_tbl);
|
q.add_aliased_columns::<notification::Entity>(notification_tbl);
|
||||||
|
|
||||||
let notifier_tbl = notification_tbl.join_str(NOTIFIER);
|
let notifier_tbl = notification_tbl.join_str(NOTIFIER);
|
||||||
q.add_aliased_columns::<user::Entity>(¬ifier_tbl);
|
q.add_aliased_columns::<user::Entity>(¬ifier_tbl);
|
||||||
|
@ -143,7 +143,7 @@ impl NotificationResolver {
|
||||||
self.resolve(
|
self.resolve(
|
||||||
&mut query,
|
&mut query,
|
||||||
¬ification_tbl,
|
¬ification_tbl,
|
||||||
&resolve_options,
|
resolve_options,
|
||||||
&self.note_resolver,
|
&self.note_resolver,
|
||||||
&self.user_resolver,
|
&self.user_resolver,
|
||||||
);
|
);
|
||||||
|
@ -183,7 +183,7 @@ impl NotificationResolver {
|
||||||
self.resolve(
|
self.resolve(
|
||||||
&mut query,
|
&mut query,
|
||||||
¬ification_tbl,
|
¬ification_tbl,
|
||||||
&resolve_options,
|
resolve_options,
|
||||||
&self.note_resolver,
|
&self.note_resolver,
|
||||||
&self.user_resolver,
|
&self.user_resolver,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,95 +1,96 @@
|
||||||
{
|
{
|
||||||
"name": "client",
|
"name": "client",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch": "pnpm vite build --watch --mode development",
|
"watch": "pnpm vite build --watch --mode development",
|
||||||
"watchRebuild": "pnpm vite build --watch",
|
"watchRebuild": "pnpm vite build --watch",
|
||||||
"build": "pnpm vite build",
|
"build": "pnpm vite build",
|
||||||
"format": "pnpm prettier --write '**/*.vue'"
|
"format": "pnpm prettier --write '**/*.vue'"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@discordapp/twemoji": "14.1.2",
|
"@discordapp/twemoji": "14.1.2",
|
||||||
"@phosphor-icons/web": "^2.0.3",
|
"@phosphor-icons/web": "^2.0.3",
|
||||||
"@rollup/plugin-alias": "3.1.9",
|
"@rollup/plugin-alias": "3.1.9",
|
||||||
"@rollup/plugin-json": "4.1.0",
|
"@rollup/plugin-json": "4.1.0",
|
||||||
"@rollup/pluginutils": "^4.2.1",
|
"@rollup/pluginutils": "^4.2.1",
|
||||||
"@types/escape-regexp": "0.0.1",
|
"@types/escape-regexp": "0.0.1",
|
||||||
"@types/glob": "8.1.0",
|
"@types/glob": "8.1.0",
|
||||||
"@types/gulp": "4.0.11",
|
"@types/gulp": "4.0.11",
|
||||||
"@types/gulp-rename": "2.0.2",
|
"@types/gulp-rename": "2.0.2",
|
||||||
"@types/katex": "0.16.0",
|
"@types/katex": "0.16.0",
|
||||||
"@types/matter-js": "0.18.2",
|
"@types/matter-js": "0.18.2",
|
||||||
"@types/punycode": "2.1.0",
|
"@types/punycode": "2.1.0",
|
||||||
"@types/seedrandom": "3.0.5",
|
"@types/seedrandom": "3.0.5",
|
||||||
"@types/throttle-debounce": "5.0.0",
|
"@types/throttle-debounce": "5.0.0",
|
||||||
"@types/tinycolor2": "1.4.3",
|
"@types/tinycolor2": "1.4.3",
|
||||||
"@types/uuid": "8.3.4",
|
"@types/uuid": "8.3.4",
|
||||||
"@vitejs/plugin-vue": "4.2.3",
|
"@vitejs/plugin-vue": "4.2.3",
|
||||||
"@vue/compiler-sfc": "3.3.4",
|
"@vue/compiler-sfc": "3.3.4",
|
||||||
"@vue/runtime-core": "3.3.4",
|
"@vue/runtime-core": "3.3.4",
|
||||||
"autobind-decorator": "2.4.0",
|
"autobind-decorator": "2.4.0",
|
||||||
"autosize": "5.0.2",
|
"autosize": "5.0.2",
|
||||||
"blurhash": "1.1.5",
|
"blurhash": "1.1.5",
|
||||||
"broadcast-channel": "4.19.1",
|
"broadcast-channel": "4.19.1",
|
||||||
"browser-image-resizer": "github:misskey-dev/browser-image-resizer",
|
"browser-image-resizer": "github:misskey-dev/browser-image-resizer",
|
||||||
"calckey-js": "workspace:*",
|
"calckey-js": "workspace:*",
|
||||||
"chart.js": "4.3.0",
|
"chart.js": "4.3.0",
|
||||||
"chartjs-adapter-date-fns": "3.0.0",
|
"chartjs-adapter-date-fns": "3.0.0",
|
||||||
"chartjs-chart-matrix": "^2.0.1",
|
"chartjs-chart-matrix": "^2.0.1",
|
||||||
"chartjs-plugin-gradient": "0.6.1",
|
"chartjs-plugin-gradient": "0.6.1",
|
||||||
"chartjs-plugin-zoom": "2.0.1",
|
"chartjs-plugin-zoom": "2.0.1",
|
||||||
"city-timezones": "^1.2.1",
|
"city-timezones": "^1.2.1",
|
||||||
"compare-versions": "5.0.3",
|
"compare-versions": "5.0.3",
|
||||||
"cropperjs": "2.0.0-beta.2",
|
"cropperjs": "2.0.0-beta.2",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "10.11.0",
|
"cypress": "10.11.0",
|
||||||
"date-fns": "2.30.0",
|
"date-fns": "2.30.0",
|
||||||
"emojilib": "github:thatonecalculator/emojilib",
|
"emojilib": "github:thatonecalculator/emojilib",
|
||||||
"escape-regexp": "0.0.1",
|
"escape-regexp": "0.0.1",
|
||||||
"eventemitter3": "4.0.7",
|
"eventemitter3": "4.0.7",
|
||||||
"focus-trap": "^7.4.3",
|
"focus-trap": "^7.4.3",
|
||||||
"focus-trap-vue": "^4.0.2",
|
"focus-trap-vue": "^4.0.2",
|
||||||
"gsap": "^3.11.5",
|
"gsap": "^3.11.5",
|
||||||
"idb-keyval": "6.2.1",
|
"idb-keyval": "6.2.1",
|
||||||
"insert-text-at-cursor": "0.3.0",
|
"insert-text-at-cursor": "0.3.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"katex": "0.16.7",
|
"katex": "0.16.7",
|
||||||
"magnetar-common": "workspace:*",
|
"magnetar-common": "workspace:*",
|
||||||
"matter-js": "0.18.0",
|
"matter-js": "0.18.0",
|
||||||
"mfm-js": "0.23.3",
|
"mfm-js": "0.23.3",
|
||||||
"photoswipe": "5.3.7",
|
"photoswipe": "5.3.7",
|
||||||
"prettier": "2.8.8",
|
"prettier": "2.8.8",
|
||||||
"prettier-plugin-vue": "1.1.6",
|
"prettier-plugin-vue": "1.1.6",
|
||||||
"prismjs": "1.29.0",
|
"prismjs": "1.29.0",
|
||||||
"punycode": "2.1.1",
|
"punycode": "2.1.1",
|
||||||
"querystring": "0.2.1",
|
"querystring": "0.2.1",
|
||||||
"rndstr": "1.0.0",
|
"rndstr": "1.0.0",
|
||||||
"rollup": "3.23.1",
|
"rollup": "3.23.1",
|
||||||
"s-age": "1.1.2",
|
"s-age": "1.1.2",
|
||||||
"sass": "1.62.1",
|
"sass": "1.62.1",
|
||||||
"seedrandom": "3.0.5",
|
"seedrandom": "3.0.5",
|
||||||
"start-server-and-test": "1.15.2",
|
"start-server-and-test": "1.15.2",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"stringz": "2.1.0",
|
"stringz": "2.1.0",
|
||||||
"swiper": "9.3.2",
|
"swiper": "9.3.2",
|
||||||
"syuilo-password-strength": "0.0.1",
|
"syuilo-password-strength": "0.0.1",
|
||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"three": "0.146.0",
|
"three": "0.146.0",
|
||||||
"throttle-debounce": "5.0.0",
|
"throttle-debounce": "5.0.0",
|
||||||
"tinycolor2": "1.5.2",
|
"tinycolor2": "1.5.2",
|
||||||
"tsc-alias": "1.8.6",
|
"tsc-alias": "1.8.6",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"twemoji-parser": "14.0.0",
|
"twemoji-parser": "14.0.0",
|
||||||
"typescript": "5.1.3",
|
"typescript": "5.1.3",
|
||||||
"unicode-emoji-json": "^0.4.0",
|
"unicode-emoji-json": "^0.4.0",
|
||||||
"uuid": "9.0.0",
|
"uuid": "9.0.0",
|
||||||
"vanilla-tilt": "1.8.0",
|
"vanilla-tilt": "1.8.0",
|
||||||
"vite": "4.3.9",
|
"vite": "4.3.9",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vue": "3.3.4",
|
"vue": "3.3.4",
|
||||||
"vue-isyourpasswordsafe": "^2.0.0",
|
"vue-component-type-helpers": "1.8.27",
|
||||||
"vue-plyr": "^7.0.0",
|
"vue-isyourpasswordsafe": "^2.0.0",
|
||||||
"vue3-otp-input": "^0.4.1",
|
"vue-plyr": "^7.0.0",
|
||||||
"vuedraggable": "4.1.0"
|
"vue3-otp-input": "^0.4.1",
|
||||||
}
|
"vuedraggable": "4.1.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,13 +25,13 @@
|
||||||
notification.type === 'Mention' ||
|
notification.type === 'Mention' ||
|
||||||
notification.type === 'Reply')
|
notification.type === 'Reply')
|
||||||
"
|
"
|
||||||
:key="notification.id"
|
:key="'noteNotif' + notification.id"
|
||||||
:note="notification.note"
|
:note="notification.note"
|
||||||
:collapsedReply="!!notification.note.parent_note"
|
:collapsedReply="!!notification.note.parent_note"
|
||||||
/>
|
/>
|
||||||
<XNotification
|
<XNotification
|
||||||
v-else
|
v-else
|
||||||
:key="notification.id"
|
:key="'basicNotif' + notification.id"
|
||||||
:notification="notification"
|
:notification="notification"
|
||||||
:with-time="true"
|
:with-time="true"
|
||||||
:full="true"
|
:full="true"
|
||||||
|
@ -43,14 +43,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, ref } from "vue";
|
import { onMounted, onUnmounted, reactive, ref, watch } from "vue";
|
||||||
|
import type { ComponentExposed } from "vue-component-type-helpers";
|
||||||
import XNotification from "@/components/MagNotification.vue";
|
import XNotification from "@/components/MagNotification.vue";
|
||||||
import XList from "@/components/MkDateSeparatedList.vue";
|
import XList from "@/components/MkDateSeparatedList.vue";
|
||||||
import XNote from "@/components/MagNote.vue";
|
import XNote from "@/components/MagNote.vue";
|
||||||
import { magStream, stream } from "@/stream";
|
import { magStream, stream } from "@/stream";
|
||||||
import { $i } from "@/account";
|
import { $i } from "@/account";
|
||||||
import MagPagination, { Paging } from "@/components/MagPagination.vue";
|
import MagPagination, { Paging } from "@/components/MagPagination.vue";
|
||||||
import { endpoints, types } from "magnetar-common";
|
import { endpoints, packed, types } from "magnetar-common";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { ChannelEvent } from "magnetar-common/built/types";
|
import { ChannelEvent } from "magnetar-common/built/types";
|
||||||
|
|
||||||
|
@ -59,10 +60,16 @@ const props = defineProps<{
|
||||||
unreadOnly?: boolean;
|
unreadOnly?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const pagingComponent = ref<InstanceType<typeof MagPagination>>();
|
const pagingComponent =
|
||||||
|
ref<
|
||||||
|
ComponentExposed<
|
||||||
|
typeof MagPagination<typeof endpoints.GetNotifications>
|
||||||
|
>
|
||||||
|
>();
|
||||||
|
|
||||||
const pagination: Paging = {
|
const pagination: Paging<typeof endpoints.GetNotifications> = reactive({
|
||||||
endpoint: endpoints.GetNotifications,
|
endpoint: endpoints.GetNotifications,
|
||||||
|
pathParams: {},
|
||||||
params: {
|
params: {
|
||||||
include_types: props.includeTypes ?? undefined,
|
include_types: props.includeTypes ?? undefined,
|
||||||
exclude_types: props.includeTypes
|
exclude_types: props.includeTypes
|
||||||
|
@ -70,9 +77,16 @@ const pagination: Paging = {
|
||||||
: $i?.mutingNotificationTypes,
|
: $i?.mutingNotificationTypes,
|
||||||
unread_only: props.unreadOnly,
|
unread_only: props.unreadOnly,
|
||||||
} as types.NotificationsReq,
|
} as types.NotificationsReq,
|
||||||
};
|
});
|
||||||
|
|
||||||
const onNotification = (notification) => {
|
watch(
|
||||||
|
() => props.includeTypes,
|
||||||
|
(types) => {
|
||||||
|
pagination.params!.include_types = types ?? undefined;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const onNotification = (notification: packed.PackNotification) => {
|
||||||
const isMuted = props.includeTypes
|
const isMuted = props.includeTypes
|
||||||
? !props.includeTypes.includes(notification.type)
|
? !props.includeTypes.includes(notification.type)
|
||||||
: $i?.mutingNotificationTypes?.includes(notification.type);
|
: $i?.mutingNotificationTypes?.includes(notification.type);
|
||||||
|
@ -83,7 +97,7 @@ const onNotification = (notification) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMuted) {
|
if (!isMuted) {
|
||||||
pagingComponent.value.prepend({
|
pagingComponent.value?.prepend({
|
||||||
...notification,
|
...notification,
|
||||||
isRead: document.visibilityState === "visible",
|
isRead: document.visibilityState === "visible",
|
||||||
});
|
});
|
||||||
|
|
|
@ -73,15 +73,7 @@
|
||||||
T['paginated'] & true
|
T['paginated'] & true
|
||||||
>"
|
>"
|
||||||
>
|
>
|
||||||
import {
|
import { computed, isRef, onActivated, onDeactivated, ref, watch } from "vue";
|
||||||
computed,
|
|
||||||
ComputedRef,
|
|
||||||
isRef,
|
|
||||||
onActivated,
|
|
||||||
onDeactivated,
|
|
||||||
ref,
|
|
||||||
watch,
|
|
||||||
} from "vue";
|
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import {
|
import {
|
||||||
getScrollContainer,
|
getScrollContainer,
|
||||||
|
@ -99,16 +91,32 @@ import {
|
||||||
import { SpanFilter } from "magnetar-common/built/types/SpanFilter";
|
import { SpanFilter } from "magnetar-common/built/types/SpanFilter";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
type PathParams = {
|
export type PathParams<
|
||||||
[K in keyof T["pathParams"] as T["pathParams"][K] & string]:
|
P extends BackendApiEndpoint<
|
||||||
|
P["method"] & Method,
|
||||||
|
P["pathParams"] & string[],
|
||||||
|
P["request"],
|
||||||
|
P["response"],
|
||||||
|
P["paginated"] & true
|
||||||
|
>
|
||||||
|
> = {
|
||||||
|
[K in keyof P["pathParams"] as P["pathParams"][K] & string]:
|
||||||
| string
|
| string
|
||||||
| number;
|
| number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Paging = {
|
export type Paging<
|
||||||
endpoint: T;
|
P extends BackendApiEndpoint<
|
||||||
pathParams: PathParams | ComputedRef<PathParams>;
|
P["method"] & Method,
|
||||||
params?: T["request"] | ComputedRef<T["request"]>;
|
P["pathParams"] & string[],
|
||||||
|
P["request"],
|
||||||
|
P["response"],
|
||||||
|
P["paginated"] & true
|
||||||
|
>
|
||||||
|
> = {
|
||||||
|
endpoint: P;
|
||||||
|
pathParams: PathParams<P>;
|
||||||
|
params?: P["request"];
|
||||||
|
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
|
||||||
|
@ -117,7 +125,7 @@ export type Paging = {
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
pagination: Paging;
|
pagination: Paging<T>;
|
||||||
disableAutoLoad?: boolean;
|
disableAutoLoad?: boolean;
|
||||||
displayLimit?: number;
|
displayLimit?: number;
|
||||||
}>(),
|
}>(),
|
||||||
|
@ -146,12 +154,8 @@ const error = ref(false);
|
||||||
const fetch = async (
|
const fetch = async (
|
||||||
pagination: SpanFilter
|
pagination: SpanFilter
|
||||||
): Promise<PaginatedResult<T["response"]> & { data: { id: string } }[]> => {
|
): Promise<PaginatedResult<T["response"]> & { data: { id: string } }[]> => {
|
||||||
const pathParams = isRef(props.pagination.pathParams)
|
const pathParams = props.pagination.pathParams;
|
||||||
? props.pagination.pathParams.value
|
const params = props.pagination.params;
|
||||||
: props.pagination.pathParams;
|
|
||||||
const params = isRef(props.pagination.params)
|
|
||||||
? props.pagination.params.value
|
|
||||||
: props.pagination.params;
|
|
||||||
|
|
||||||
return os
|
return os
|
||||||
.magApi(
|
.magApi(
|
||||||
|
@ -196,6 +200,8 @@ const reload = (): void => {
|
||||||
init();
|
init();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(props.pagination, reload);
|
||||||
|
|
||||||
const refresh = async (): Promise<void> => {
|
const refresh = async (): Promise<void> => {
|
||||||
fetch({}).then(
|
fetch({}).then(
|
||||||
(res) => {
|
(res) => {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
:with-ok-button="true"
|
:with-ok-button="true"
|
||||||
:ok-button-disabled="false"
|
:ok-button-disabled="false"
|
||||||
@ok="ok()"
|
@ok="ok()"
|
||||||
@close="dialog.close()"
|
@close="dialog?.close()"
|
||||||
@closed="emit('closed')"
|
@closed="emit('closed')"
|
||||||
>
|
>
|
||||||
<template #header>{{ i18n.ts.notificationSetting }}</template>
|
<template #header>{{ i18n.ts.notificationSetting }}</template>
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
v-for="ntype in notificationTypes"
|
v-for="ntype in notificationTypes"
|
||||||
:key="ntype"
|
:key="ntype"
|
||||||
v-model="typesMap[ntype]"
|
v-model="typesMap[ntype]"
|
||||||
>{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch
|
>{{ i18n.t(`_notification._magTypes.${ntype}`) }}</MkSwitch
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,25 +40,25 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import { notificationTypes } from "calckey-js";
|
|
||||||
import MkSwitch from "./form/switch.vue";
|
import MkSwitch from "./form/switch.vue";
|
||||||
import MkInfo from "./MkInfo.vue";
|
import MkInfo from "./MkInfo.vue";
|
||||||
import MkButton from "./MkButton.vue";
|
import MkButton from "./MkButton.vue";
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
import { notificationTypes, types } from "magnetar-common";
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "done", v: { includingTypes: string[] | null }): void;
|
done: [{ includingTypes: types.NotificationType[] | null }];
|
||||||
(ev: "closed"): void;
|
closed: [];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
includingTypes?: (typeof notificationTypes)[number][] | null;
|
includingTypes?: types.NotificationType[] | null;
|
||||||
showGlobalToggle?: boolean;
|
showGlobalToggle?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
includingTypes: () => [],
|
includingTypes: () => notificationTypes,
|
||||||
showGlobalToggle: true,
|
showGlobalToggle: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -67,39 +67,35 @@ let includingTypes = $computed(() => props.includingTypes || []);
|
||||||
|
|
||||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
||||||
|
|
||||||
let typesMap = $ref<Record<(typeof notificationTypes)[number], boolean>>({});
|
let typesMap = $ref(
|
||||||
let useGlobalSetting = $ref(
|
Object.fromEntries(
|
||||||
(includingTypes === null || includingTypes.length === 0) &&
|
notificationTypes.map((n) => [n, includingTypes.includes(n)])
|
||||||
props.showGlobalToggle
|
) as Record<types.NotificationType, boolean>
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const ntype of notificationTypes) {
|
let useGlobalSetting = $ref(includingTypes === null || props.showGlobalToggle);
|
||||||
typesMap[ntype] = includingTypes.includes(ntype);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ok() {
|
function ok() {
|
||||||
if (useGlobalSetting) {
|
if (useGlobalSetting) {
|
||||||
emit("done", { includingTypes: null });
|
emit("done", { includingTypes: null });
|
||||||
} else {
|
} else {
|
||||||
emit("done", {
|
emit("done", {
|
||||||
includingTypes: (
|
includingTypes: notificationTypes.filter((type) => typesMap[type]),
|
||||||
Object.keys(typesMap) as (typeof notificationTypes)[number][]
|
|
||||||
).filter((type) => typesMap[type]),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.close();
|
dialog?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableAll() {
|
function disableAll() {
|
||||||
for (const type in typesMap) {
|
for (const type in typesMap) {
|
||||||
typesMap[type as (typeof notificationTypes)[number]] = false;
|
typesMap[type as types.NotificationType] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableAll() {
|
function enableAll() {
|
||||||
for (const type in typesMap) {
|
for (const type in typesMap) {
|
||||||
typesMap[type as (typeof notificationTypes)[number]] = true;
|
typesMap[type as types.NotificationType] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {
|
||||||
types,
|
types,
|
||||||
} from "magnetar-common";
|
} from "magnetar-common";
|
||||||
import { magReactionToLegacy } from "@/scripts-mag/mag-util";
|
import { magReactionToLegacy } from "@/scripts-mag/mag-util";
|
||||||
|
import { ComponentProps } from "vue-component-type-helpers";
|
||||||
|
|
||||||
export const pendingApiRequestsCount = ref(0);
|
export const pendingApiRequestsCount = ref(0);
|
||||||
|
|
||||||
|
@ -307,11 +308,49 @@ export function getUniqueId(): string {
|
||||||
return uniqueId++ + "";
|
return uniqueId++ + "";
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function popup(
|
// See https://github.com/misskey-dev/misskey/blob/5f43c2faa2fae3866a9921d81ab43c3b9e8bd222/packages/frontend/src/os.ts#L150
|
||||||
component: Component,
|
// We cannot use "vue-component-type-helpers"'s ComponentEmit, because it returns a type intersection, making it useless
|
||||||
props: Record<string, any>,
|
// for type checking of emit handlers
|
||||||
events = {},
|
//
|
||||||
disposeEvent?: string
|
// We're not sure why the props were picked from T, because it didn't work, so we made it work on $props, which seems to work correctly
|
||||||
|
type ComponentEmit<T> = T extends new () => { $props: infer Props }
|
||||||
|
? [keyof Pick<Props, Extract<keyof Props, `on${string}`>>] extends [never]
|
||||||
|
? Record<string, unknown>
|
||||||
|
: EmitsExtractor<Props>
|
||||||
|
: T extends (...args: any) => any
|
||||||
|
? ReturnType<T> extends {
|
||||||
|
[x: string]: any;
|
||||||
|
__ctx?: { [x: string]: any; props: infer Props };
|
||||||
|
}
|
||||||
|
? [keyof Pick<Props, Extract<keyof Props, `on${string}`>>] extends [
|
||||||
|
never
|
||||||
|
]
|
||||||
|
? Record<string, unknown>
|
||||||
|
: EmitsExtractor<Props>
|
||||||
|
: never
|
||||||
|
: never;
|
||||||
|
|
||||||
|
type EmitsExtractor<T> = {
|
||||||
|
[K in keyof T as K extends `onVnode${string}`
|
||||||
|
? never
|
||||||
|
: K extends `on${infer E}`
|
||||||
|
? Uncapitalize<E>
|
||||||
|
: K extends string
|
||||||
|
? never
|
||||||
|
: K]: T[K];
|
||||||
|
};
|
||||||
|
|
||||||
|
type ComponentPropsRef<T extends Component> = {
|
||||||
|
[K in keyof ComponentProps<T>]:
|
||||||
|
| ComponentProps<T>[K]
|
||||||
|
| Ref<ComponentProps<T>[K]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function popup<C extends Component>(
|
||||||
|
component: C,
|
||||||
|
props: ComponentPropsRef<C>,
|
||||||
|
events: Partial<ComponentEmit<C>> = {} as ComponentEmit<C>,
|
||||||
|
disposeEvent?: keyof ComponentEmit<C>
|
||||||
) {
|
) {
|
||||||
markRaw(component);
|
markRaw(component);
|
||||||
|
|
||||||
|
@ -1022,14 +1061,14 @@ export const deckGlobalEvents = new EventEmitter();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
export function checkExistence(fileData: ArrayBuffer): Promise<any> {
|
export function checkExistence(fileData: ArrayBuffer): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const data = new FormData();
|
const data = new FormData();
|
||||||
data.append('md5', getMD5(fileData));
|
data.append('md5', getMD5(fileData));
|
||||||
|
|
||||||
os.api('drive/files/find-by-hash', {
|
os.api('drive/files/find-by-hash', {
|
||||||
md5: getMD5(fileData)
|
md5: getMD5(fileData)
|
||||||
}).then(resp => {
|
}).then(resp => {
|
||||||
resolve(resp.length > 0 ? resp[0] : null);
|
resolve(resp.length > 0 ? resp[0] : null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}*/
|
}*/
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
import { computed, ref, watch } from "vue";
|
import { computed, ref, watch } from "vue";
|
||||||
import { Virtual } from "swiper";
|
import { Virtual } from "swiper";
|
||||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||||
import { notificationTypes } from "calckey-js";
|
import { notificationTypes, types } from "magnetar-common";
|
||||||
import XNotifications from "@/components/MagNotifications.vue";
|
import XNotifications from "@/components/MagNotifications.vue";
|
||||||
import XNotes from "@/components/MkNotes.vue";
|
import XNotes from "@/components/MkNotes.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -76,7 +76,7 @@ const tabs = ["all", "unread", "mentions", "directNotes"];
|
||||||
let tab = $ref(tabs[0]);
|
let tab = $ref(tabs[0]);
|
||||||
watch($$(tab), () => syncSlide(tabs.indexOf(tab)));
|
watch($$(tab), () => syncSlide(tabs.indexOf(tab)));
|
||||||
|
|
||||||
let includeTypes = $ref<string[] | null>(null);
|
let includeTypes = $ref<types.NotificationType[]>();
|
||||||
let unreadOnly = $computed(() => tab === "unread");
|
let unreadOnly = $computed(() => tab === "unread");
|
||||||
os.api("notifications/mark-all-as-read");
|
os.api("notifications/mark-all-as-read");
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ const directNotesPagination = {
|
||||||
|
|
||||||
function setFilter(ev) {
|
function setFilter(ev) {
|
||||||
const typeItems = notificationTypes.map((t) => ({
|
const typeItems = notificationTypes.map((t) => ({
|
||||||
text: i18n.t(`_notification._types.${t}`),
|
text: i18n.t(`_notification._magTypes.${t}`),
|
||||||
active: includeTypes && includeTypes.includes(t),
|
active: includeTypes && includeTypes.includes(t),
|
||||||
action: () => {
|
action: () => {
|
||||||
includeTypes = [t];
|
includeTypes = [t];
|
||||||
|
@ -117,7 +117,7 @@ function setFilter(ev) {
|
||||||
icon: "ph-x ph-bold ph-lg",
|
icon: "ph-x ph-bold ph-lg",
|
||||||
text: i18n.ts.clear,
|
text: i18n.ts.clear,
|
||||||
action: () => {
|
action: () => {
|
||||||
includeTypes = null;
|
includeTypes = undefined;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
|
@ -191,6 +191,6 @@ function onSlideChange() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncSlide(index) {
|
function syncSlide(index) {
|
||||||
swiperRef.slideTo(index);
|
swiperRef?.slideTo(index);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -47,7 +47,6 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent } from "vue";
|
||||||
import { notificationTypes } from "calckey-js";
|
|
||||||
import FormButton from "@/components/MkButton.vue";
|
import FormButton from "@/components/MkButton.vue";
|
||||||
import FormSection from "@/components/form/section.vue";
|
import FormSection from "@/components/form/section.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -55,6 +54,12 @@ import { $i } from "@/account";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
import MkPushNotificationAllowButton from "@/components/MkPushNotificationAllowButton.vue";
|
import MkPushNotificationAllowButton from "@/components/MkPushNotificationAllowButton.vue";
|
||||||
|
import {
|
||||||
|
magLegacyNotificationType,
|
||||||
|
magNotificationType,
|
||||||
|
} from "@/scripts-mag/mag-util";
|
||||||
|
import { notificationTypes } from "magnetar-common";
|
||||||
|
import type * as Misskey from "calckey-js";
|
||||||
|
|
||||||
let allowButton =
|
let allowButton =
|
||||||
$shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
|
$shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
|
||||||
|
@ -75,7 +80,14 @@ async function readAllNotifications() {
|
||||||
|
|
||||||
function configure() {
|
function configure() {
|
||||||
const includingTypes = notificationTypes.filter(
|
const includingTypes = notificationTypes.filter(
|
||||||
(x) => !$i!.mutingNotificationTypes.includes(x)
|
(x) =>
|
||||||
|
!$i!.mutingNotificationTypes
|
||||||
|
.map((s) =>
|
||||||
|
magNotificationType(
|
||||||
|
s as Misskey.entities.Notification["type"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.includes(x)
|
||||||
);
|
);
|
||||||
os.popup(
|
os.popup(
|
||||||
defineAsyncComponent(
|
defineAsyncComponent(
|
||||||
|
@ -86,13 +98,14 @@ function configure() {
|
||||||
showGlobalToggle: false,
|
showGlobalToggle: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
done: async (res) => {
|
done: async ({ includingTypes }) => {
|
||||||
const { includingTypes: value } = res;
|
|
||||||
await os
|
await os
|
||||||
.apiWithDialog("i/update", {
|
.apiWithDialog("i/update", {
|
||||||
mutingNotificationTypes: notificationTypes.filter(
|
mutingNotificationTypes: notificationTypes
|
||||||
(x) => !value.includes(x)
|
.filter((x) => !includingTypes!.includes(x))
|
||||||
),
|
.map(magLegacyNotificationType)
|
||||||
|
.filter((s) => s)
|
||||||
|
.map((s) => s!),
|
||||||
})
|
})
|
||||||
.then((i) => {
|
.then((i) => {
|
||||||
$i!.mutingNotificationTypes = i.mutingNotificationTypes;
|
$i!.mutingNotificationTypes = i.mutingNotificationTypes;
|
||||||
|
|
|
@ -149,6 +149,64 @@ export function magEffectiveNote(
|
||||||
return note.is_renote && note.renoted_note ? note.renoted_note : note;
|
return note.is_renote && note.renoted_note ? note.renoted_note : note;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function magLegacyNotificationType(
|
||||||
|
nt: types.NotificationType | undefined
|
||||||
|
): Misskey.entities.Notification["type"] | undefined {
|
||||||
|
if (typeof nt === "undefined") return nt;
|
||||||
|
|
||||||
|
switch (nt) {
|
||||||
|
case "Reply":
|
||||||
|
return "reply";
|
||||||
|
case "Renote":
|
||||||
|
return "renote";
|
||||||
|
case "Reaction":
|
||||||
|
return "reaction";
|
||||||
|
case "Quote":
|
||||||
|
return "quote";
|
||||||
|
case "Mention":
|
||||||
|
return "mention";
|
||||||
|
case "Follow":
|
||||||
|
return "follow";
|
||||||
|
case "FollowRequestAccepted":
|
||||||
|
return "followRequestAccepted";
|
||||||
|
case "FollowRequestReceived":
|
||||||
|
return "receiveFollowRequest";
|
||||||
|
case "App":
|
||||||
|
return "app";
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function magNotificationType(
|
||||||
|
nt: Misskey.entities.Notification["type"] | undefined
|
||||||
|
): types.NotificationType | undefined {
|
||||||
|
if (typeof nt === "undefined") return nt;
|
||||||
|
|
||||||
|
switch (nt) {
|
||||||
|
case "reply":
|
||||||
|
return "Reply";
|
||||||
|
case "renote":
|
||||||
|
return "Renote";
|
||||||
|
case "reaction":
|
||||||
|
return "Reaction";
|
||||||
|
case "quote":
|
||||||
|
return "Quote";
|
||||||
|
case "mention":
|
||||||
|
return "Mention";
|
||||||
|
case "follow":
|
||||||
|
return "Follow";
|
||||||
|
case "followRequestAccepted":
|
||||||
|
return "FollowRequestAccepted";
|
||||||
|
case "receiveFollowRequest":
|
||||||
|
return "FollowRequestReceived";
|
||||||
|
case "app":
|
||||||
|
return "App";
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function magLegacyVisibility(
|
export function magLegacyVisibility(
|
||||||
vis: types.NoteVisibility | Misskey.entities.Note["visibility"]
|
vis: types.NoteVisibility | Misskey.entities.Note["visibility"]
|
||||||
): Misskey.entities.Note["visibility"];
|
): Misskey.entities.Note["visibility"];
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { throttle } from "throttle-debounce";
|
import { throttle } from "throttle-debounce";
|
||||||
import { markRaw } from "vue";
|
import { markRaw } from "vue";
|
||||||
import { notificationTypes } from "calckey-js";
|
|
||||||
import { Storage } from "../../pizzax";
|
import { Storage } from "../../pizzax";
|
||||||
import { api } from "@/os";
|
import { api } from "@/os";
|
||||||
|
import { types } from "magnetar-common";
|
||||||
|
|
||||||
type ColumnWidget = {
|
type ColumnWidget = {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -13,15 +13,15 @@ type ColumnWidget = {
|
||||||
export type Column = {
|
export type Column = {
|
||||||
id: string;
|
id: string;
|
||||||
type:
|
type:
|
||||||
| "main"
|
| "main"
|
||||||
| "widgets"
|
| "widgets"
|
||||||
| "notifications"
|
| "notifications"
|
||||||
| "tl"
|
| "tl"
|
||||||
| "antenna"
|
| "antenna"
|
||||||
| "list"
|
| "list"
|
||||||
| "mentions"
|
| "mentions"
|
||||||
| "direct"
|
| "direct"
|
||||||
| "bookmarks";
|
| "bookmarks";
|
||||||
name: string | null;
|
name: string | null;
|
||||||
width: number;
|
width: number;
|
||||||
widgets?: ColumnWidget[];
|
widgets?: ColumnWidget[];
|
||||||
|
@ -29,7 +29,7 @@ export type Column = {
|
||||||
flexible?: boolean;
|
flexible?: boolean;
|
||||||
antennaId?: string;
|
antennaId?: string;
|
||||||
listId?: string;
|
listId?: string;
|
||||||
includingTypes?: (typeof notificationTypes)[number][];
|
includingTypes?: types.NotificationType[];
|
||||||
tl?: "home" | "local" | "social" | "global";
|
tl?: "home" | "local" | "social" | "global";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -316,9 +316,9 @@ export function updateColumnWidget(
|
||||||
column.widgets = column.widgets.map((w) =>
|
column.widgets = column.widgets.map((w) =>
|
||||||
w.id === widgetId
|
w.id === widgetId
|
||||||
? {
|
? {
|
||||||
...w,
|
...w,
|
||||||
data: widgetData,
|
data: widgetData,
|
||||||
}
|
}
|
||||||
: w
|
: w
|
||||||
);
|
);
|
||||||
columns[columnIndex] = column;
|
columns[columnIndex] = column;
|
||||||
|
|
|
@ -10,18 +10,20 @@
|
||||||
>{{ column.name }}</template
|
>{{ column.name }}</template
|
||||||
>
|
>
|
||||||
|
|
||||||
<XNotifications :include-types="column.includingTypes" />
|
<XNotifications :include-types="includingTypes" />
|
||||||
</XColumn>
|
</XColumn>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent, ref } from "vue";
|
||||||
import XColumn from "./column.vue";
|
import XColumn from "./column.vue";
|
||||||
import type { Column } from "./deck-store";
|
import type { Column } from "./deck-store";
|
||||||
import { updateColumn } from "./deck-store";
|
import { updateColumn } from "./deck-store";
|
||||||
import XNotifications from "@/components/MagNotifications.vue";
|
import XNotifications from "@/components/MagNotifications.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
import MkNotificationSettingWindow from "@/components/MkNotificationSettingWindow.vue";
|
||||||
|
import { types } from "magnetar-common";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
column: Column;
|
column: Column;
|
||||||
|
@ -32,8 +34,12 @@ const emit = defineEmits<{
|
||||||
(ev: "parent-focus", direction: "up" | "down" | "left" | "right"): void;
|
(ev: "parent-focus", direction: "up" | "down" | "left" | "right"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const includingTypes = ref<types.NotificationType[] | undefined>(
|
||||||
|
props.column.includingTypes
|
||||||
|
);
|
||||||
|
|
||||||
function func(): void {
|
function func(): void {
|
||||||
os.popup(
|
os.popup<typeof MkNotificationSettingWindow>(
|
||||||
defineAsyncComponent(
|
defineAsyncComponent(
|
||||||
() => import("@/components/MkNotificationSettingWindow.vue")
|
() => import("@/components/MkNotificationSettingWindow.vue")
|
||||||
),
|
),
|
||||||
|
@ -41,11 +47,11 @@ function func(): void {
|
||||||
includingTypes: props.column.includingTypes,
|
includingTypes: props.column.includingTypes,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
done: async (res) => {
|
done: async ({ includingTypes: notifTypes }) => {
|
||||||
const { includingTypes } = res;
|
|
||||||
updateColumn(props.column.id, {
|
updateColumn(props.column.id, {
|
||||||
includingTypes: includingTypes,
|
includingTypes: notifTypes ?? undefined,
|
||||||
});
|
});
|
||||||
|
includingTypes.value = notifTypes ?? undefined;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"closed"
|
"closed"
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
<i class="ph-gear-six ph-bold ph-lg"></i></button
|
<i class="ph-gear-six ph-bold ph-lg"></i></button
|
||||||
></template>
|
></template>
|
||||||
<div>
|
<div>
|
||||||
<XNotifications :include-types="widgetProps.includingTypes" />
|
<XNotifications
|
||||||
|
:include-types="widgetProps.includingTypes as types.NotificationType[]"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</MkContainer>
|
</MkContainer>
|
||||||
</template>
|
</template>
|
||||||
|
@ -30,8 +32,10 @@ import { useWidgetPropsManager, Widget, WidgetComponentExpose } from "./widget";
|
||||||
import { GetFormResultType } from "@/scripts/form";
|
import { GetFormResultType } from "@/scripts/form";
|
||||||
import MkContainer from "@/components/MkContainer.vue";
|
import MkContainer from "@/components/MkContainer.vue";
|
||||||
import XNotifications from "@/components/MagNotifications.vue";
|
import XNotifications from "@/components/MagNotifications.vue";
|
||||||
|
import MkNotificationSettingWindow from "@/components/MkNotificationSettingWindow.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
import { types } from "magnetar-common";
|
||||||
|
|
||||||
const name = "notifications";
|
const name = "notifications";
|
||||||
|
|
||||||
|
@ -46,8 +50,8 @@ const widgetPropsDef = {
|
||||||
},
|
},
|
||||||
includingTypes: {
|
includingTypes: {
|
||||||
type: "array" as const,
|
type: "array" as const,
|
||||||
hidden: true,
|
|
||||||
default: null,
|
default: null,
|
||||||
|
hidden: true as true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,16 +72,16 @@ const { widgetProps, configure, save } = useWidgetPropsManager(
|
||||||
|
|
||||||
const configureNotification = () => {
|
const configureNotification = () => {
|
||||||
os.popup(
|
os.popup(
|
||||||
defineAsyncComponent(
|
defineAsyncComponent<typeof MkNotificationSettingWindow>(
|
||||||
() => import("@/components/MkNotificationSettingWindow.vue")
|
() => import("@/components/MkNotificationSettingWindow.vue")
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
includingTypes: widgetProps.includingTypes,
|
includingTypes:
|
||||||
|
widgetProps.includingTypes as types.NotificationType[],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
done: async (res) => {
|
done: async ({ includingTypes }) => {
|
||||||
const { includingTypes } = res;
|
widgetProps.includingTypes = includingTypes ?? undefined;
|
||||||
widgetProps.includingTypes = includingTypes;
|
|
||||||
save();
|
save();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -2122,7 +2122,18 @@ _notification:
|
||||||
emptyPushNotificationMessage: "Push notifications have been updated"
|
emptyPushNotificationMessage: "Push notifications have been updated"
|
||||||
reacted: "reacted to your post"
|
reacted: "reacted to your post"
|
||||||
renoted: "boosted your post"
|
renoted: "boosted your post"
|
||||||
voted: "voted on your poll"
|
_magTypes:
|
||||||
|
All: "All"
|
||||||
|
Follow: "New followers"
|
||||||
|
Mention: "Mentions"
|
||||||
|
Reply: "Replies"
|
||||||
|
Renote: "Boosts"
|
||||||
|
Quote: "Quotes"
|
||||||
|
Reaction: "Reactions"
|
||||||
|
PollEnd: "Polls ending"
|
||||||
|
FollowRequestReceived: "Received follow requests"
|
||||||
|
FollowRequestAccepted: "Accepted follow requests"
|
||||||
|
App: "Notifications from linked apps"
|
||||||
_types:
|
_types:
|
||||||
all: "All"
|
all: "All"
|
||||||
follow: "New followers"
|
follow: "New followers"
|
||||||
|
|
|
@ -19,6 +19,19 @@ import * as types from "./types";
|
||||||
import * as packed from "./packed";
|
import * as packed from "./packed";
|
||||||
import * as endpoints from "./endpoints";
|
import * as endpoints from "./endpoints";
|
||||||
|
|
||||||
|
export const notificationTypes: types.NotificationType[] = [
|
||||||
|
"Follow",
|
||||||
|
"FollowRequestReceived",
|
||||||
|
"FollowRequestAccepted",
|
||||||
|
"Mention",
|
||||||
|
"Reply",
|
||||||
|
"Renote",
|
||||||
|
"Reaction",
|
||||||
|
"Quote",
|
||||||
|
"PollEnd",
|
||||||
|
"App",
|
||||||
|
];
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Method,
|
Method,
|
||||||
BackendApiEndpoint,
|
BackendApiEndpoint,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
import type { NotificationSettings } from "./NotificationSettings";
|
import type { NotificationSettings } from "./NotificationSettings";
|
||||||
|
|
||||||
export interface UserSelfExt { avatar_id: string | null, banner_id: string | null, email_announcements: boolean, always_mark_sensitive: boolean, reject_bot_follow_requests: boolean, reject_crawlers: boolean, reject_ai_training: boolean, has_unread_announcements: boolean, has_unread_antenna: boolean, has_unread_notifications: boolean, has_pending_follow_requests: boolean, word_mutes: Array<string>, instance_mutes: Array<string>, notification_settings: NotificationSettings, }
|
export interface UserSelfExt { avatar_id: string | null, banner_id: string | null, email_announcements: boolean, always_mark_sensitive: boolean, reject_bot_follow_requests: boolean, reject_crawlers: boolean, reject_ai_training: boolean, word_mutes: Array<string>, instance_mutes: Array<string>, notification_settings: NotificationSettings, }
|
|
@ -1,3 +1,3 @@
|
||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
export interface UserSelfReq { profile?: boolean, pins?: boolean, detail?: boolean, secrets?: boolean, }
|
export interface UserSelfReq { profile?: boolean, pins?: boolean, detail?: boolean, secrets?: boolean, self_detail?: boolean, }
|
|
@ -3,7 +3,7 @@ import type { Empty } from "../Empty";
|
||||||
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
|
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
|
||||||
|
|
||||||
export const GetFollowRequestsSelf = {
|
export const GetFollowRequestsSelf = {
|
||||||
endpoint: "/users/@self/follow-requests",
|
endpoint: "/users/@self/follow-requests" as "/users/@self/follow-requests",
|
||||||
pathParams: [] as [],
|
pathParams: [] as [],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as Empty,
|
request: undefined as unknown as Empty,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { Empty } from "../Empty";
|
||||||
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
|
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
|
||||||
|
|
||||||
export const GetFollowersById = {
|
export const GetFollowersById = {
|
||||||
endpoint: "/users/:id/followers",
|
endpoint: "/users/:id/followers" as "/users/:id/followers",
|
||||||
pathParams: ["id"] as ["id"],
|
pathParams: ["id"] as ["id"],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as Empty,
|
request: undefined as unknown as Empty,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { Empty } from "../Empty";
|
||||||
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
|
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
|
||||||
|
|
||||||
export const GetFollowersSelf = {
|
export const GetFollowersSelf = {
|
||||||
endpoint: "/users/@self/followers",
|
endpoint: "/users/@self/followers" as "/users/@self/followers",
|
||||||
pathParams: [] as [],
|
pathParams: [] as [],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as Empty,
|
request: undefined as unknown as Empty,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { Empty } from "../Empty";
|
||||||
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
|
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
|
||||||
|
|
||||||
export const GetFollowingById = {
|
export const GetFollowingById = {
|
||||||
endpoint: "/users/:id/following",
|
endpoint: "/users/:id/following" as "/users/:id/following",
|
||||||
pathParams: ["id"] as ["id"],
|
pathParams: ["id"] as ["id"],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as Empty,
|
request: undefined as unknown as Empty,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { Empty } from "../Empty";
|
||||||
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
|
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
|
||||||
|
|
||||||
export const GetFollowingSelf = {
|
export const GetFollowingSelf = {
|
||||||
endpoint: "/users/@self/following",
|
endpoint: "/users/@self/following" as "/users/@self/following",
|
||||||
pathParams: [] as [],
|
pathParams: [] as [],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as Empty,
|
request: undefined as unknown as Empty,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { ManyUsersByIdReq } from "../ManyUsersByIdReq";
|
||||||
import type { PackUserBase } from "../packed/PackUserBase";
|
import type { PackUserBase } from "../packed/PackUserBase";
|
||||||
|
|
||||||
export const GetManyUsersById = {
|
export const GetManyUsersById = {
|
||||||
endpoint: "/users/lookup-many",
|
endpoint: "/users/lookup-many" as "/users/lookup-many",
|
||||||
pathParams: [] as [],
|
pathParams: [] as [],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as ManyUsersByIdReq,
|
request: undefined as unknown as ManyUsersByIdReq,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { NoteByIdReq } from "../NoteByIdReq";
|
||||||
import type { PackNoteMaybeFull } from "../packed/PackNoteMaybeFull";
|
import type { PackNoteMaybeFull } from "../packed/PackNoteMaybeFull";
|
||||||
|
|
||||||
export const GetNoteById = {
|
export const GetNoteById = {
|
||||||
endpoint: "/notes/:id",
|
endpoint: "/notes/:id" as "/notes/:id",
|
||||||
pathParams: ["id"] as ["id"],
|
pathParams: ["id"] as ["id"],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as NoteByIdReq,
|
request: undefined as unknown as NoteByIdReq,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { NotificationsReq } from "../NotificationsReq";
|
||||||
import type { PackNotification } from "../PackNotification";
|
import type { PackNotification } from "../PackNotification";
|
||||||
|
|
||||||
export const GetNotifications = {
|
export const GetNotifications = {
|
||||||
endpoint: "/users/@self/notifications",
|
endpoint: "/users/@self/notifications" as "/users/@self/notifications",
|
||||||
pathParams: [] as [],
|
pathParams: [] as [],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as NotificationsReq,
|
request: undefined as unknown as NotificationsReq,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { GetTimelineReq } from "../GetTimelineReq";
|
||||||
import type { PackNoteMaybeFull } from "../packed/PackNoteMaybeFull";
|
import type { PackNoteMaybeFull } from "../packed/PackNoteMaybeFull";
|
||||||
|
|
||||||
export const GetTimeline = {
|
export const GetTimeline = {
|
||||||
endpoint: "/timeline/:timeline_type",
|
endpoint: "/timeline/:timeline_type" as "/timeline/:timeline_type",
|
||||||
pathParams: ["timeline_type"] as ["timeline_type"],
|
pathParams: ["timeline_type"] as ["timeline_type"],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as GetTimelineReq,
|
request: undefined as unknown as GetTimelineReq,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
|
||||||
import type { UserByIdReq } from "../UserByIdReq";
|
import type { UserByIdReq } from "../UserByIdReq";
|
||||||
|
|
||||||
export const GetUserByAcct = {
|
export const GetUserByAcct = {
|
||||||
endpoint: "/users/by-acct/:user_acct",
|
endpoint: "/users/by-acct/:user_acct" as "/users/by-acct/:user_acct",
|
||||||
pathParams: ["user_acct"] as ["user_acct"],
|
pathParams: ["user_acct"] as ["user_acct"],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as UserByIdReq,
|
request: undefined as unknown as UserByIdReq,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
|
||||||
import type { UserByIdReq } from "../UserByIdReq";
|
import type { UserByIdReq } from "../UserByIdReq";
|
||||||
|
|
||||||
export const GetUserById = {
|
export const GetUserById = {
|
||||||
endpoint: "/users/:user_id",
|
endpoint: "/users/:user_id" as "/users/:user_id",
|
||||||
pathParams: ["user_id"] as ["user_id"],
|
pathParams: ["user_id"] as ["user_id"],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as UserByIdReq,
|
request: undefined as unknown as UserByIdReq,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { PackUserSelfMaybeAll } from "../packed/PackUserSelfMaybeAll";
|
||||||
import type { UserSelfReq } from "../UserSelfReq";
|
import type { UserSelfReq } from "../UserSelfReq";
|
||||||
|
|
||||||
export const GetUserSelf = {
|
export const GetUserSelf = {
|
||||||
endpoint: "/users/@self",
|
endpoint: "/users/@self" as "/users/@self",
|
||||||
pathParams: [] as [],
|
pathParams: [] as [],
|
||||||
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
request: undefined as unknown as UserSelfReq,
|
request: undefined as unknown as UserSelfReq,
|
||||||
|
|
|
@ -5,5 +5,6 @@ import type { UserDetailExt } from "../UserDetailExt";
|
||||||
import type { UserProfileExt } from "../UserProfileExt";
|
import type { UserProfileExt } from "../UserProfileExt";
|
||||||
import type { UserProfilePinsEx } from "../UserProfilePinsEx";
|
import type { UserProfilePinsEx } from "../UserProfilePinsEx";
|
||||||
import type { UserSecretsExt } from "../UserSecretsExt";
|
import type { UserSecretsExt } from "../UserSecretsExt";
|
||||||
|
import type { UserSelfExt } from "../UserSelfExt";
|
||||||
|
|
||||||
export type PackUserSelfMaybeAll = Id & UserBase & Partial<UserProfileExt> & Partial<UserProfilePinsEx> & Partial<UserDetailExt> & Partial<UserSecretsExt>;
|
export type PackUserSelfMaybeAll = Id & UserBase & Partial<UserProfileExt> & Partial<UserProfilePinsEx> & Partial<UserDetailExt> & Partial<UserSecretsExt> & Partial<UserSelfExt>;
|
|
@ -344,6 +344,9 @@ importers:
|
||||||
vue:
|
vue:
|
||||||
specifier: 3.3.4
|
specifier: 3.3.4
|
||||||
version: 3.3.4
|
version: 3.3.4
|
||||||
|
vue-component-type-helpers:
|
||||||
|
specifier: 1.8.27
|
||||||
|
version: 1.8.27
|
||||||
vue-isyourpasswordsafe:
|
vue-isyourpasswordsafe:
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
|
@ -1016,7 +1019,7 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@swc/core': ^1.2.66
|
'@swc/core': ^1.2.66
|
||||||
chokidar: ^3.5.1
|
chokidar: ^3.3.1
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
chokidar:
|
chokidar:
|
||||||
optional: true
|
optional: true
|
||||||
|
@ -7436,6 +7439,10 @@ packages:
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/vue-component-type-helpers@1.8.27:
|
||||||
|
resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/vue-isyourpasswordsafe@2.0.0:
|
/vue-isyourpasswordsafe@2.0.0:
|
||||||
resolution: {integrity: sha512-j3ORj18R9AgFiP2UOM35KuZbSeJAUiwCSyeRBFN3CGFYTJSKsxqU9qGqOHOz6OhLAYKMTin8JOmqugAbF9O+Bg==}
|
resolution: {integrity: sha512-j3ORj18R9AgFiP2UOM35KuZbSeJAUiwCSyeRBFN3CGFYTJSKsxqU9qGqOHOz6OhLAYKMTin8JOmqugAbF9O+Bg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
@ -59,7 +59,7 @@ fn split_tag_inner(tag: impl AsRef<str>) -> (String, Option<String>) {
|
||||||
let tag = tag.strip_prefix('@').unwrap_or(tag.as_ref());
|
let tag = tag.strip_prefix('@').unwrap_or(tag.as_ref());
|
||||||
|
|
||||||
match tag.split_once('@') {
|
match tag.split_once('@') {
|
||||||
Some((name, host)) if name.is_empty() => (host.to_owned(), None),
|
Some((name, "")) => (name.to_owned(), None),
|
||||||
Some((name, host)) => (name.to_owned(), Some(host.to_owned())),
|
Some((name, host)) => (name.to_owned(), Some(host.to_owned())),
|
||||||
None => (tag.to_owned(), None),
|
None => (tag.to_owned(), None),
|
||||||
}
|
}
|
||||||
|
|
|
@ -406,7 +406,7 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
|
||||||
fn decl() -> String {
|
fn decl() -> String {
|
||||||
format!(
|
format!(
|
||||||
"const {} = {{\n \
|
"const {} = {{\n \
|
||||||
endpoint: \"{}\",\n \
|
endpoint: \"{}\" as \"{}\",\n \
|
||||||
pathParams: {} as {},\n \
|
pathParams: {} as {},\n \
|
||||||
method: \"{}\" as \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\",\n \
|
method: \"{}\" as \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\",\n \
|
||||||
request: undefined as unknown as {},\n \
|
request: undefined as unknown as {},\n \
|
||||||
|
@ -416,6 +416,7 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
|
||||||
",
|
",
|
||||||
Self::name(),
|
Self::name(),
|
||||||
Self::ENDPOINT,
|
Self::ENDPOINT,
|
||||||
|
Self::ENDPOINT,
|
||||||
#path_params,
|
#path_params,
|
||||||
#path_params,
|
#path_params,
|
||||||
Self::METHOD,
|
Self::METHOD,
|
||||||
|
|
|
@ -20,6 +20,8 @@ pub struct UserSelfReq {
|
||||||
pub detail: Option<bool>,
|
pub detail: Option<bool>,
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub secrets: Option<bool>,
|
pub secrets: Option<bool>,
|
||||||
|
#[ts(optional)]
|
||||||
|
pub self_detail: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Endpoint)]
|
#[derive(Endpoint)]
|
||||||
|
|
|
@ -138,10 +138,6 @@ pub struct UserSelfExt {
|
||||||
pub reject_bot_follow_requests: bool,
|
pub reject_bot_follow_requests: bool,
|
||||||
pub reject_crawlers: bool,
|
pub reject_crawlers: bool,
|
||||||
pub reject_ai_training: bool,
|
pub reject_ai_training: bool,
|
||||||
pub has_unread_announcements: bool,
|
|
||||||
pub has_unread_antenna: bool,
|
|
||||||
pub has_unread_notifications: bool,
|
|
||||||
pub has_pending_follow_requests: bool,
|
|
||||||
pub word_mutes: Vec<String>,
|
pub word_mutes: Vec<String>,
|
||||||
pub instance_mutes: Vec<String>,
|
pub instance_mutes: Vec<String>,
|
||||||
pub notification_settings: NotificationSettings,
|
pub notification_settings: NotificationSettings,
|
||||||
|
@ -210,6 +206,7 @@ pack!(
|
||||||
& Optional<UserProfilePinsEx> as pins
|
& Optional<UserProfilePinsEx> as pins
|
||||||
& Optional<UserDetailExt> as detail
|
& Optional<UserDetailExt> as detail
|
||||||
& Optional<UserSecretsExt> as secrets
|
& Optional<UserSecretsExt> as secrets
|
||||||
|
& Optional<UserSelfExt> as self_detail
|
||||||
);
|
);
|
||||||
|
|
||||||
impl From<PackUserBase> for PackUserSelfMaybeAll {
|
impl From<PackUserBase> for PackUserSelfMaybeAll {
|
||||||
|
@ -221,6 +218,7 @@ impl From<PackUserBase> for PackUserSelfMaybeAll {
|
||||||
Optional(None),
|
Optional(None),
|
||||||
Optional(None),
|
Optional(None),
|
||||||
Optional(None),
|
Optional(None),
|
||||||
|
Optional(None),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,14 @@ use magnetar_sdk::types::drive::PackDriveFileBase;
|
||||||
use magnetar_sdk::types::emoji::{EmojiContext, PackEmojiBase};
|
use magnetar_sdk::types::emoji::{EmojiContext, PackEmojiBase};
|
||||||
use magnetar_sdk::types::instance::InstanceTicker;
|
use magnetar_sdk::types::instance::InstanceTicker;
|
||||||
use magnetar_sdk::types::note::PackNoteMaybeFull;
|
use magnetar_sdk::types::note::PackNoteMaybeFull;
|
||||||
|
use magnetar_sdk::types::notification::NotificationType;
|
||||||
use magnetar_sdk::types::user::{
|
use magnetar_sdk::types::user::{
|
||||||
AvatarDecoration, MovedTo, PackSecurityKeyBase, ProfileField, SecurityKeyBase, SpeechTransform,
|
AvatarDecoration, MovedTo, PackSecurityKeyBase, ProfileField, SecurityKeyBase, SpeechTransform,
|
||||||
UserAuthOverviewExt, UserBase, UserDetailExt, UserProfileExt, UserProfilePinsEx,
|
UserAuthOverviewExt, UserBase, UserDetailExt, UserProfileExt, UserProfilePinsEx,
|
||||||
UserRelationExt, UserSecretsExt,
|
UserRelationExt, UserSecretsExt, UserSelfExt,
|
||||||
};
|
};
|
||||||
use magnetar_sdk::types::MmXml;
|
use magnetar_sdk::types::{MmXml, NotificationSettings};
|
||||||
|
use serde::Deserialize;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::model::{PackType, PackingContext};
|
use crate::model::{PackType, PackingContext};
|
||||||
|
@ -68,7 +70,7 @@ impl PackType<UserBaseSource<'_>> for UserBase {
|
||||||
is_moderator: user.is_moderator,
|
is_moderator: user.is_moderator,
|
||||||
is_bot: user.is_bot,
|
is_bot: user.is_bot,
|
||||||
emojis: emoji_context.clone(),
|
emojis: emoji_context.clone(),
|
||||||
instance: instance.map(|i| i.clone()),
|
instance: instance.cloned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,10 +113,7 @@ impl PackType<UserProfileExtSource<'_>> for UserProfileExt {
|
||||||
description: profile.description.clone(),
|
description: profile.description.clone(),
|
||||||
description_mm: description_mm.cloned(),
|
description_mm: description_mm.cloned(),
|
||||||
location: profile.location.clone(),
|
location: profile.location.clone(),
|
||||||
birthday: profile
|
birthday: profile.birthday.clone().and_then(|b| b.parse().ok()),
|
||||||
.birthday
|
|
||||||
.clone()
|
|
||||||
.and_then(|b| b.parse().map_or_else(|_| None, Some)),
|
|
||||||
fields: profile_fields.clone(),
|
fields: profile_fields.clone(),
|
||||||
follower_count: follow_visibility.then_some(user.followers_count as usize),
|
follower_count: follow_visibility.then_some(user.followers_count as usize),
|
||||||
following_count: follow_visibility.then_some(user.following_count as usize),
|
following_count: follow_visibility.then_some(user.following_count as usize),
|
||||||
|
@ -215,3 +214,29 @@ impl PackType<&ck::user_profile::Model> for UserAuthOverviewExt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PackType<(&ck::user::Model, &ck::user_profile::Model)> for UserSelfExt {
|
||||||
|
fn extract(
|
||||||
|
context: &PackingContext,
|
||||||
|
(user, profile): (&ck::user::Model, &ck::user_profile::Model),
|
||||||
|
) -> Self {
|
||||||
|
UserSelfExt {
|
||||||
|
avatar_id: user.avatar_id.clone(),
|
||||||
|
banner_id: user.banner_id.clone(),
|
||||||
|
email_announcements: profile.receive_announcement_email,
|
||||||
|
always_mark_sensitive: profile.always_mark_nsfw,
|
||||||
|
reject_bot_follow_requests: profile.careful_bot,
|
||||||
|
reject_crawlers: profile.no_crawle,
|
||||||
|
reject_ai_training: profile.prevent_ai_learning,
|
||||||
|
word_mutes: Vec::<String>::deserialize(&profile.muted_words).unwrap_or_else(|_| vec![]),
|
||||||
|
instance_mutes: vec![],
|
||||||
|
notification_settings: NotificationSettings {
|
||||||
|
enabled: profile
|
||||||
|
.muting_notification_types
|
||||||
|
.iter()
|
||||||
|
.map(|n| NotificationType::extract(context, n))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -137,6 +137,6 @@ impl PackingContext {
|
||||||
write.insert(link, relationship);
|
write.insert(link, relationship);
|
||||||
drop(write);
|
drop(write);
|
||||||
|
|
||||||
return Ok(relationship);
|
Ok(relationship)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@ impl DriveModel {
|
||||||
Required(DriveFileBase::extract(
|
Required(DriveFileBase::extract(
|
||||||
ctx,
|
ctx,
|
||||||
PackFileBaseInput {
|
PackFileBaseInput {
|
||||||
file: &file,
|
file,
|
||||||
effective_url: url.as_ref(),
|
effective_url: url.as_ref(),
|
||||||
effective_thumbnail_url: thumbnail_url.as_ref(),
|
effective_thumbnail_url: thumbnail_url.as_ref(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -59,7 +59,7 @@ impl EmojiModel {
|
||||||
pub fn pack_existing(&self, ctx: &PackingContext, emoji: &ck::emoji::Model) -> PackEmojiBase {
|
pub fn pack_existing(&self, ctx: &PackingContext, emoji: &ck::emoji::Model) -> PackEmojiBase {
|
||||||
PackEmojiBase::pack_from((
|
PackEmojiBase::pack_from((
|
||||||
Required(Id::from(&emoji.id)),
|
Required(Id::from(&emoji.id)),
|
||||||
Required(EmojiBase::extract(ctx, &emoji)),
|
Required(EmojiBase::extract(ctx, emoji)),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ impl EmojiModel {
|
||||||
host: Option<&str>,
|
host: Option<&str>,
|
||||||
) -> PackResult<Vec<PackEmojiBase>> {
|
) -> PackResult<Vec<PackEmojiBase>> {
|
||||||
let emojis = ctx.service.emoji_cache.get_many(shortcodes, host).await?;
|
let emojis = ctx.service.emoji_cache.get_many(shortcodes, host).await?;
|
||||||
let packed_emojis = emojis.iter().map(|e| self.pack_existing(ctx, &e)).collect();
|
let packed_emojis = emojis.iter().map(|e| self.pack_existing(ctx, e)).collect();
|
||||||
|
|
||||||
Ok(packed_emojis)
|
Ok(packed_emojis)
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ impl EmojiModel {
|
||||||
tags: &[EmojiTag<'_>],
|
tags: &[EmojiTag<'_>],
|
||||||
) -> PackResult<Vec<PackEmojiBase>> {
|
) -> PackResult<Vec<PackEmojiBase>> {
|
||||||
let emojis = ctx.service.emoji_cache.get_many_tagged(tags).await?;
|
let emojis = ctx.service.emoji_cache.get_many_tagged(tags).await?;
|
||||||
let packed_emojis = emojis.iter().map(|e| self.pack_existing(ctx, &e)).collect();
|
let packed_emojis = emojis.iter().map(|e| self.pack_existing(ctx, e)).collect();
|
||||||
|
|
||||||
Ok(packed_emojis)
|
Ok(packed_emojis)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use magnetar_sdk::types::instance::InstanceTicker;
|
||||||
use magnetar_sdk::types::user::{
|
use magnetar_sdk::types::user::{
|
||||||
MovedTo, PackSecurityKeyBase, PackUserBase, PackUserMaybeAll, PackUserSelfMaybeAll,
|
MovedTo, PackSecurityKeyBase, PackUserBase, PackUserMaybeAll, PackUserSelfMaybeAll,
|
||||||
ProfileField, SecurityKeyBase, UserAuthOverviewExt, UserBase, UserDetailExt, UserProfileExt,
|
ProfileField, SecurityKeyBase, UserAuthOverviewExt, UserBase, UserDetailExt, UserProfileExt,
|
||||||
UserProfilePinsEx, UserRelationExt, UserRelationship, UserSecretsExt,
|
UserProfilePinsEx, UserRelationExt, UserRelationship, UserSecretsExt, UserSelfExt,
|
||||||
};
|
};
|
||||||
use magnetar_sdk::types::{Id, MmXml};
|
use magnetar_sdk::types::{Id, MmXml};
|
||||||
use magnetar_sdk::{mmm, Optional, Packed, Required};
|
use magnetar_sdk::{mmm, Optional, Packed, Required};
|
||||||
|
@ -166,7 +166,7 @@ impl UserModel {
|
||||||
let base = UserBase::extract(
|
let base = UserBase::extract(
|
||||||
ctx,
|
ctx,
|
||||||
UserBaseSource {
|
UserBaseSource {
|
||||||
user: &user,
|
user,
|
||||||
username_mm: mmm::to_xml_string(&username_mm).map(MmXml).as_ref().ok(),
|
username_mm: mmm::to_xml_string(&username_mm).map(MmXml).as_ref().ok(),
|
||||||
avatar_url,
|
avatar_url,
|
||||||
avatar: avatar.as_ref(),
|
avatar: avatar.as_ref(),
|
||||||
|
@ -243,14 +243,14 @@ impl UserModel {
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let description_mm = self.tokenize_description(&profile);
|
let description_mm = self.tokenize_description(profile);
|
||||||
|
|
||||||
let fields = Vec::<ProfileFieldRaw>::deserialize(&profile.fields)?;
|
let fields = Vec::<ProfileFieldRaw>::deserialize(&profile.fields)?;
|
||||||
let parser = mmm::Context::new(2);
|
let parser = mmm::Context::new(2);
|
||||||
let profile_fields = fields
|
let profile_fields = fields
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
let tok = parser.parse_profile_fields(&f.value);
|
let tok = parser.parse_profile_fields(f.value);
|
||||||
|
|
||||||
ProfileField {
|
ProfileField {
|
||||||
name: f.name.to_string(),
|
name: f.name.to_string(),
|
||||||
|
@ -263,7 +263,7 @@ impl UserModel {
|
||||||
|
|
||||||
if let Some(desc_mm) = &description_mm {
|
if let Some(desc_mm) = &description_mm {
|
||||||
let emoji_model = EmojiModel;
|
let emoji_model = EmojiModel;
|
||||||
let shortcodes = emoji_model.deduplicate_emoji(ctx, get_mm_token_emoji(&desc_mm));
|
let shortcodes = emoji_model.deduplicate_emoji(ctx, get_mm_token_emoji(desc_mm));
|
||||||
let emojis = emoji_model
|
let emojis = emoji_model
|
||||||
.fetch_many_emojis(ctx, &shortcodes, user.host.as_deref())
|
.fetch_many_emojis(ctx, &shortcodes, user.host.as_deref())
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -275,7 +275,7 @@ impl UserModel {
|
||||||
ctx,
|
ctx,
|
||||||
UserProfileExtSource {
|
UserProfileExtSource {
|
||||||
user,
|
user,
|
||||||
profile: &profile,
|
profile,
|
||||||
profile_fields: &profile_fields,
|
profile_fields: &profile_fields,
|
||||||
banner: banner.as_ref(),
|
banner: banner.as_ref(),
|
||||||
description_mm: description_mm
|
description_mm: description_mm
|
||||||
|
@ -322,7 +322,9 @@ impl UserModel {
|
||||||
let user = user_data.user();
|
let user = user_data.user();
|
||||||
|
|
||||||
let should_fetch_profile = user_data.profile().is_none()
|
let should_fetch_profile = user_data.profile().is_none()
|
||||||
&& (req.profile.unwrap_or_default() || req.secrets.unwrap_or_default());
|
&& (req.profile.unwrap_or_default()
|
||||||
|
|| req.secrets.unwrap_or_default()
|
||||||
|
|| req.self_detail.unwrap_or_default());
|
||||||
let profile_raw_promise =
|
let profile_raw_promise =
|
||||||
OptionFuture::from(should_fetch_profile.then(|| self.get_profile(ctx, user)));
|
OptionFuture::from(should_fetch_profile.then(|| self.get_profile(ctx, user)));
|
||||||
let (base_res, profile_res) =
|
let (base_res, profile_res) =
|
||||||
|
@ -370,6 +372,11 @@ impl UserModel {
|
||||||
.map(|notes| UserProfilePinsEx::extract(ctx, ¬es));
|
.map(|notes| UserProfilePinsEx::extract(ctx, ¬es));
|
||||||
let secrets_resolved = secrets_res.transpose()?;
|
let secrets_resolved = secrets_res.transpose()?;
|
||||||
|
|
||||||
|
let self_info = req
|
||||||
|
.self_detail
|
||||||
|
.unwrap_or_default()
|
||||||
|
.then(|| UserSelfExt::extract(ctx, (&user, profile_ref.unwrap())));
|
||||||
|
|
||||||
Ok(PackUserSelfMaybeAll {
|
Ok(PackUserSelfMaybeAll {
|
||||||
id: base.id,
|
id: base.id,
|
||||||
user: base.user,
|
user: base.user,
|
||||||
|
@ -377,6 +384,7 @@ impl UserModel {
|
||||||
pins: Optional(pins_resolved),
|
pins: Optional(pins_resolved),
|
||||||
detail: Optional(detail),
|
detail: Optional(detail),
|
||||||
secrets: Optional(secrets_resolved),
|
secrets: Optional(secrets_resolved),
|
||||||
|
self_detail: Optional(self_info),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub fn serialize_as_urlenc(value: &Value) -> String {
|
||||||
.map(|(k, v)| {
|
.map(|(k, v)| {
|
||||||
format!(
|
format!(
|
||||||
"{}={}",
|
"{}={}",
|
||||||
utf8_percent_encode(&k, NON_ALPHANUMERIC).to_string(),
|
utf8_percent_encode(k, NON_ALPHANUMERIC),
|
||||||
if matches!(v, Value::Array(_) | Value::Object(_)) {
|
if matches!(v, Value::Array(_) | Value::Object(_)) {
|
||||||
utf8_percent_encode(&serialize_as_urlenc(v), NON_ALPHANUMERIC)
|
utf8_percent_encode(&serialize_as_urlenc(v), NON_ALPHANUMERIC)
|
||||||
.to_string()
|
.to_string()
|
||||||
|
|
|
@ -114,7 +114,7 @@ impl IntoResponseParts for Pagination {
|
||||||
let wrap = |uri: Uri, query: String, rel: Either<RelPrev, RelNext>| {
|
let wrap = |uri: Uri, query: String, rel: Either<RelPrev, RelNext>| {
|
||||||
format!(
|
format!(
|
||||||
"<{}?{}>; rel=\"{}\"",
|
"<{}?{}>; rel=\"{}\"",
|
||||||
uri.to_string(),
|
uri,
|
||||||
query,
|
query,
|
||||||
rel.as_ref()
|
rel.as_ref()
|
||||||
.map_either(RelPrev::as_ref, RelNext::as_ref)
|
.map_either(RelPrev::as_ref, RelNext::as_ref)
|
||||||
|
|
Loading…
Reference in New Issue