Compare commits
No commits in common. "ff00dfebb632d5f677900ce7fc6a17da35c86784" and "fdf9966ef67571f9e7354996484ac441848b17a8" have entirely different histories.
ff00dfebb6
...
fdf9966ef6
File diff suppressed because it is too large
Load Diff
|
@ -26,7 +26,7 @@ edition = "2021"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
axum = "0.7"
|
axum = "0.7"
|
||||||
axum-extra = "0.9"
|
axum-extra = "0.9"
|
||||||
cached = "0.47"
|
cached = "0.46"
|
||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
compact_str = "0.7"
|
compact_str = "0.7"
|
||||||
|
|
|
@ -26,3 +26,4 @@ strum = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
once_cell = "1.18.0"
|
||||||
|
|
|
@ -65,6 +65,8 @@ pub enum Relation {
|
||||||
on_delete = "SetNull"
|
on_delete = "SetNull"
|
||||||
)]
|
)]
|
||||||
DriveFolder,
|
DriveFolder,
|
||||||
|
#[sea_orm(has_many = "super::page::Entity")]
|
||||||
|
Page,
|
||||||
#[sea_orm(
|
#[sea_orm(
|
||||||
belongs_to = "super::user::Entity",
|
belongs_to = "super::user::Entity",
|
||||||
from = "Column::UserId",
|
from = "Column::UserId",
|
||||||
|
@ -81,6 +83,12 @@ impl Related<super::drive_folder::Entity> for Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Related<super::page::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Page.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Related<super::user::Entity> for Entity {
|
impl Related<super::user::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::User.def()
|
Relation::User.def()
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "gallery_like")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: String,
|
||||||
|
#[sea_orm(column_name = "createdAt")]
|
||||||
|
pub created_at: DateTimeWithTimeZone,
|
||||||
|
#[sea_orm(column_name = "userId")]
|
||||||
|
pub user_id: String,
|
||||||
|
#[sea_orm(column_name = "postId")]
|
||||||
|
pub post_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::gallery_post::Entity",
|
||||||
|
from = "Column::PostId",
|
||||||
|
to = "super::gallery_post::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
GalleryPost,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::user::Entity",
|
||||||
|
from = "Column::UserId",
|
||||||
|
to = "super::user::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
User,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::gallery_post::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::GalleryPost.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::user::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::User.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -0,0 +1,54 @@
|
||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "gallery_post")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: String,
|
||||||
|
#[sea_orm(column_name = "createdAt")]
|
||||||
|
pub created_at: DateTimeWithTimeZone,
|
||||||
|
#[sea_orm(column_name = "updatedAt")]
|
||||||
|
pub updated_at: DateTimeWithTimeZone,
|
||||||
|
pub title: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[sea_orm(column_name = "userId")]
|
||||||
|
pub user_id: String,
|
||||||
|
#[sea_orm(column_name = "fileIds")]
|
||||||
|
pub file_ids: Vec<String>,
|
||||||
|
#[sea_orm(column_name = "isSensitive")]
|
||||||
|
pub is_sensitive: bool,
|
||||||
|
#[sea_orm(column_name = "likedCount")]
|
||||||
|
pub liked_count: i32,
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::gallery_like::Entity")]
|
||||||
|
GalleryLike,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::user::Entity",
|
||||||
|
from = "Column::UserId",
|
||||||
|
to = "super::user::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
User,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::gallery_like::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::GalleryLike.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::user::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::User.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -20,6 +20,8 @@ pub mod drive_folder;
|
||||||
pub mod emoji;
|
pub mod emoji;
|
||||||
pub mod follow_request;
|
pub mod follow_request;
|
||||||
pub mod following;
|
pub mod following;
|
||||||
|
pub mod gallery_like;
|
||||||
|
pub mod gallery_post;
|
||||||
pub mod hashtag;
|
pub mod hashtag;
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub mod meta;
|
pub mod meta;
|
||||||
|
@ -34,6 +36,8 @@ pub mod note_thread_muting;
|
||||||
pub mod note_unread;
|
pub mod note_unread;
|
||||||
pub mod note_watching;
|
pub mod note_watching;
|
||||||
pub mod notification;
|
pub mod notification;
|
||||||
|
pub mod page;
|
||||||
|
pub mod page_like;
|
||||||
pub mod password_reset_request;
|
pub mod password_reset_request;
|
||||||
pub mod poll;
|
pub mod poll;
|
||||||
pub mod poll_vote;
|
pub mod poll_vote;
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||||
|
|
||||||
|
use super::sea_orm_active_enums::PageVisibilityEnum;
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "page")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: String,
|
||||||
|
#[sea_orm(column_name = "createdAt")]
|
||||||
|
pub created_at: DateTimeWithTimeZone,
|
||||||
|
#[sea_orm(column_name = "updatedAt")]
|
||||||
|
pub updated_at: DateTimeWithTimeZone,
|
||||||
|
pub title: String,
|
||||||
|
pub name: String,
|
||||||
|
pub summary: Option<String>,
|
||||||
|
#[sea_orm(column_name = "alignCenter")]
|
||||||
|
pub align_center: bool,
|
||||||
|
pub font: String,
|
||||||
|
#[sea_orm(column_name = "userId")]
|
||||||
|
pub user_id: String,
|
||||||
|
#[sea_orm(column_name = "eyeCatchingImageId")]
|
||||||
|
pub eye_catching_image_id: Option<String>,
|
||||||
|
#[sea_orm(column_type = "JsonBinary")]
|
||||||
|
pub content: Json,
|
||||||
|
#[sea_orm(column_type = "JsonBinary")]
|
||||||
|
pub variables: Json,
|
||||||
|
pub visibility: PageVisibilityEnum,
|
||||||
|
#[sea_orm(column_name = "visibleUserIds")]
|
||||||
|
pub visible_user_ids: Vec<String>,
|
||||||
|
#[sea_orm(column_name = "likedCount")]
|
||||||
|
pub liked_count: i32,
|
||||||
|
#[sea_orm(column_name = "hideTitleWhenPinned")]
|
||||||
|
pub hide_title_when_pinned: bool,
|
||||||
|
pub script: String,
|
||||||
|
#[sea_orm(column_name = "isPublic")]
|
||||||
|
pub is_public: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::drive_file::Entity",
|
||||||
|
from = "Column::EyeCatchingImageId",
|
||||||
|
to = "super::drive_file::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
DriveFile,
|
||||||
|
#[sea_orm(has_many = "super::page_like::Entity")]
|
||||||
|
PageLike,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::user::Entity",
|
||||||
|
from = "Column::UserId",
|
||||||
|
to = "super::user::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
User,
|
||||||
|
#[sea_orm(has_one = "super::user_profile::Entity")]
|
||||||
|
UserProfile,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::drive_file::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::DriveFile.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::page_like::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::PageLike.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::user::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::User.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::user_profile::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::UserProfile.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -0,0 +1,51 @@
|
||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "page_like")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: String,
|
||||||
|
#[sea_orm(column_name = "createdAt")]
|
||||||
|
pub created_at: DateTimeWithTimeZone,
|
||||||
|
#[sea_orm(column_name = "userId")]
|
||||||
|
pub user_id: String,
|
||||||
|
#[sea_orm(column_name = "pageId")]
|
||||||
|
pub page_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::page::Entity",
|
||||||
|
from = "Column::PageId",
|
||||||
|
to = "super::page::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
Page,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::user::Entity",
|
||||||
|
from = "Column::UserId",
|
||||||
|
to = "super::user::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
User,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::page::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Page.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::user::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::User.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -18,6 +18,8 @@ pub use super::drive_folder::Entity as DriveFolder;
|
||||||
pub use super::emoji::Entity as Emoji;
|
pub use super::emoji::Entity as Emoji;
|
||||||
pub use super::follow_request::Entity as FollowRequest;
|
pub use super::follow_request::Entity as FollowRequest;
|
||||||
pub use super::following::Entity as Following;
|
pub use super::following::Entity as Following;
|
||||||
|
pub use super::gallery_like::Entity as GalleryLike;
|
||||||
|
pub use super::gallery_post::Entity as GalleryPost;
|
||||||
pub use super::hashtag::Entity as Hashtag;
|
pub use super::hashtag::Entity as Hashtag;
|
||||||
pub use super::instance::Entity as Instance;
|
pub use super::instance::Entity as Instance;
|
||||||
pub use super::meta::Entity as Meta;
|
pub use super::meta::Entity as Meta;
|
||||||
|
@ -32,6 +34,8 @@ pub use super::note_thread_muting::Entity as NoteThreadMuting;
|
||||||
pub use super::note_unread::Entity as NoteUnread;
|
pub use super::note_unread::Entity as NoteUnread;
|
||||||
pub use super::note_watching::Entity as NoteWatching;
|
pub use super::note_watching::Entity as NoteWatching;
|
||||||
pub use super::notification::Entity as Notification;
|
pub use super::notification::Entity as Notification;
|
||||||
|
pub use super::page::Entity as Page;
|
||||||
|
pub use super::page_like::Entity as PageLike;
|
||||||
pub use super::password_reset_request::Entity as PasswordResetRequest;
|
pub use super::password_reset_request::Entity as PasswordResetRequest;
|
||||||
pub use super::poll::Entity as Poll;
|
pub use super::poll::Entity as Poll;
|
||||||
pub use super::poll_vote::Entity as PollVote;
|
pub use super::poll_vote::Entity as PollVote;
|
||||||
|
|
|
@ -118,6 +118,20 @@ pub enum NotificationTypeEnum {
|
||||||
Reply,
|
Reply,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(
|
||||||
|
rs_type = "String",
|
||||||
|
db_type = "Enum",
|
||||||
|
enum_name = "page_visibility_enum"
|
||||||
|
)]
|
||||||
|
pub enum PageVisibilityEnum {
|
||||||
|
#[sea_orm(string_value = "followers")]
|
||||||
|
Followers,
|
||||||
|
#[sea_orm(string_value = "public")]
|
||||||
|
Public,
|
||||||
|
#[sea_orm(string_value = "specified")]
|
||||||
|
Specified,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy, Serialize, Deserialize)]
|
||||||
#[sea_orm(
|
#[sea_orm(
|
||||||
rs_type = "String",
|
rs_type = "String",
|
||||||
db_type = "Enum",
|
db_type = "Enum",
|
||||||
|
|
|
@ -106,6 +106,10 @@ pub enum Relation {
|
||||||
DriveFile1,
|
DriveFile1,
|
||||||
#[sea_orm(has_many = "super::drive_folder::Entity")]
|
#[sea_orm(has_many = "super::drive_folder::Entity")]
|
||||||
DriveFolder,
|
DriveFolder,
|
||||||
|
#[sea_orm(has_many = "super::gallery_like::Entity")]
|
||||||
|
GalleryLike,
|
||||||
|
#[sea_orm(has_many = "super::gallery_post::Entity")]
|
||||||
|
GalleryPost,
|
||||||
#[sea_orm(has_many = "super::meta::Entity")]
|
#[sea_orm(has_many = "super::meta::Entity")]
|
||||||
Meta,
|
Meta,
|
||||||
#[sea_orm(has_many = "super::moderation_log::Entity")]
|
#[sea_orm(has_many = "super::moderation_log::Entity")]
|
||||||
|
@ -124,6 +128,10 @@ pub enum Relation {
|
||||||
NoteUnread,
|
NoteUnread,
|
||||||
#[sea_orm(has_many = "super::note_watching::Entity")]
|
#[sea_orm(has_many = "super::note_watching::Entity")]
|
||||||
NoteWatching,
|
NoteWatching,
|
||||||
|
#[sea_orm(has_many = "super::page::Entity")]
|
||||||
|
Page,
|
||||||
|
#[sea_orm(has_many = "super::page_like::Entity")]
|
||||||
|
PageLike,
|
||||||
#[sea_orm(has_many = "super::password_reset_request::Entity")]
|
#[sea_orm(has_many = "super::password_reset_request::Entity")]
|
||||||
PasswordResetRequest,
|
PasswordResetRequest,
|
||||||
#[sea_orm(has_many = "super::poll_vote::Entity")]
|
#[sea_orm(has_many = "super::poll_vote::Entity")]
|
||||||
|
@ -202,6 +210,18 @@ impl Related<super::drive_folder::Entity> for Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Related<super::gallery_like::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::GalleryLike.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::gallery_post::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::GalleryPost.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Related<super::meta::Entity> for Entity {
|
impl Related<super::meta::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::Meta.def()
|
Relation::Meta.def()
|
||||||
|
@ -256,6 +276,18 @@ impl Related<super::note_watching::Entity> for Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Related<super::page::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Page.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::page_like::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::PageLike.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Related<super::password_reset_request::Entity> for Entity {
|
impl Related<super::password_reset_request::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::PasswordResetRequest.def()
|
Relation::PasswordResetRequest.def()
|
||||||
|
|
|
@ -42,6 +42,8 @@ pub struct Model {
|
||||||
pub security_keys_available: bool,
|
pub security_keys_available: bool,
|
||||||
#[sea_orm(column_name = "usePasswordLessLogin")]
|
#[sea_orm(column_name = "usePasswordLessLogin")]
|
||||||
pub use_password_less_login: bool,
|
pub use_password_less_login: bool,
|
||||||
|
#[sea_orm(column_name = "pinnedPageId", unique)]
|
||||||
|
pub pinned_page_id: Option<String>,
|
||||||
#[sea_orm(column_type = "JsonBinary")]
|
#[sea_orm(column_type = "JsonBinary")]
|
||||||
pub room: Json,
|
pub room: Json,
|
||||||
#[sea_orm(column_type = "JsonBinary")]
|
#[sea_orm(column_type = "JsonBinary")]
|
||||||
|
@ -75,6 +77,14 @@ pub struct Model {
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::page::Entity",
|
||||||
|
from = "Column::PinnedPageId",
|
||||||
|
to = "super::page::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
Page,
|
||||||
#[sea_orm(
|
#[sea_orm(
|
||||||
belongs_to = "super::user::Entity",
|
belongs_to = "super::user::Entity",
|
||||||
from = "Column::UserId",
|
from = "Column::UserId",
|
||||||
|
@ -85,6 +95,12 @@ pub enum Relation {
|
||||||
User,
|
User,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Related<super::page::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Page.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Related<super::user::Entity> for Entity {
|
impl Related<super::user::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::User.def()
|
Relation::User.def()
|
||||||
|
|
|
@ -7,8 +7,6 @@ mod m20230806_142918_drop_featured_note_option;
|
||||||
mod m20240107_005747_remove_user_groups;
|
mod m20240107_005747_remove_user_groups;
|
||||||
mod m20240107_220523_generated_is_quote;
|
mod m20240107_220523_generated_is_quote;
|
||||||
mod m20240107_224446_generated_is_renote;
|
mod m20240107_224446_generated_is_renote;
|
||||||
mod m20240112_215106_remove_pages;
|
|
||||||
mod m20240112_234759_remove_gallery;
|
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
|
@ -23,8 +21,6 @@ impl MigratorTrait for Migrator {
|
||||||
Box::new(m20240107_005747_remove_user_groups::Migration),
|
Box::new(m20240107_005747_remove_user_groups::Migration),
|
||||||
Box::new(m20240107_220523_generated_is_quote::Migration),
|
Box::new(m20240107_220523_generated_is_quote::Migration),
|
||||||
Box::new(m20240107_224446_generated_is_renote::Migration),
|
Box::new(m20240107_224446_generated_is_renote::Migration),
|
||||||
Box::new(m20240112_215106_remove_pages::Migration),
|
|
||||||
Box::new(m20240112_234759_remove_gallery::Migration),
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,118 +0,0 @@
|
||||||
use sea_orm_migration::prelude::*;
|
|
||||||
|
|
||||||
#[derive(DeriveMigrationName)]
|
|
||||||
pub struct Migration;
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl MigrationTrait for Migration {
|
|
||||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
|
||||||
let db = manager.get_connection();
|
|
||||||
|
|
||||||
db.execute_unprepared(
|
|
||||||
r#"
|
|
||||||
ALTER TABLE "user_profile" DROP COLUMN "pinnedPageId";
|
|
||||||
|
|
||||||
DROP TABLE "page_like";
|
|
||||||
DROP TABLE "page";
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
|
||||||
let db = manager.get_connection();
|
|
||||||
|
|
||||||
db.execute_unprepared(
|
|
||||||
r#"
|
|
||||||
create table page
|
|
||||||
(
|
|
||||||
id varchar(32) not null
|
|
||||||
constraint "PK_742f4117e065c5b6ad21b37ba1f"
|
|
||||||
primary key,
|
|
||||||
"createdAt" timestamp with time zone not null,
|
|
||||||
"updatedAt" timestamp with time zone not null,
|
|
||||||
title varchar(256) not null,
|
|
||||||
name varchar(256) not null,
|
|
||||||
summary varchar(256),
|
|
||||||
"alignCenter" boolean not null,
|
|
||||||
font varchar(32) not null,
|
|
||||||
"userId" varchar(32) not null
|
|
||||||
constraint "FK_ae1d917992dd0c9d9bbdad06c4a"
|
|
||||||
references "user"
|
|
||||||
on delete cascade,
|
|
||||||
"eyeCatchingImageId" varchar(32)
|
|
||||||
constraint "FK_a9ca79ad939bf06066b81c9d3aa"
|
|
||||||
references drive_file
|
|
||||||
on delete cascade,
|
|
||||||
content jsonb default '[]'::jsonb not null,
|
|
||||||
variables jsonb default '[]'::jsonb not null,
|
|
||||||
visibility page_visibility_enum not null,
|
|
||||||
"visibleUserIds" varchar(32)[] default '{}'::character varying[] not null,
|
|
||||||
"likedCount" integer default 0 not null,
|
|
||||||
"hideTitleWhenPinned" boolean default false not null,
|
|
||||||
script varchar(16384) default ''::character varying not null,
|
|
||||||
"isPublic" boolean default true not null
|
|
||||||
);
|
|
||||||
|
|
||||||
comment on column page."createdAt" is 'The created date of the Page.';
|
|
||||||
|
|
||||||
comment on column page."updatedAt" is 'The updated date of the Page.';
|
|
||||||
|
|
||||||
comment on column page."userId" is 'The ID of author.';
|
|
||||||
|
|
||||||
create index "IDX_fbb4297c927a9b85e9cefa2eb1"
|
|
||||||
on page ("createdAt");
|
|
||||||
|
|
||||||
create index "IDX_af639b066dfbca78b01a920f8a"
|
|
||||||
on page ("updatedAt");
|
|
||||||
|
|
||||||
create index "IDX_b82c19c08afb292de4600d99e4"
|
|
||||||
on page (name);
|
|
||||||
|
|
||||||
create index "IDX_ae1d917992dd0c9d9bbdad06c4"
|
|
||||||
on page ("userId");
|
|
||||||
|
|
||||||
create index "IDX_90148bbc2bf0854428786bfc15"
|
|
||||||
on page ("visibleUserIds");
|
|
||||||
|
|
||||||
create unique index "IDX_2133ef8317e4bdb839c0dcbf13"
|
|
||||||
on page ("userId", name);
|
|
||||||
|
|
||||||
create table page_like
|
|
||||||
(
|
|
||||||
id varchar(32) not null
|
|
||||||
constraint "PK_813f034843af992d3ae0f43c64c"
|
|
||||||
primary key,
|
|
||||||
"createdAt" timestamp with time zone not null,
|
|
||||||
"userId" varchar(32) not null
|
|
||||||
constraint "FK_0e61efab7f88dbb79c9166dbb48"
|
|
||||||
references "user"
|
|
||||||
on delete cascade,
|
|
||||||
"pageId" varchar(32) not null
|
|
||||||
constraint "FK_cf8782626dced3176038176a847"
|
|
||||||
references page
|
|
||||||
on delete cascade
|
|
||||||
);
|
|
||||||
|
|
||||||
create index "IDX_0e61efab7f88dbb79c9166dbb4"
|
|
||||||
on page_like ("userId");
|
|
||||||
|
|
||||||
create unique index "IDX_4ce6fb9c70529b4c8ac46c9bfa"
|
|
||||||
on page_like ("userId", "pageId");
|
|
||||||
|
|
||||||
alter table user_profile
|
|
||||||
add "pinnedPageId" varchar(32)
|
|
||||||
constraint "UQ_6dc44f1ceb65b1e72bacef2ca27"
|
|
||||||
unique
|
|
||||||
constraint "FK_6dc44f1ceb65b1e72bacef2ca27"
|
|
||||||
references page
|
|
||||||
on delete set null;
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
use sea_orm_migration::prelude::*;
|
|
||||||
|
|
||||||
#[derive(DeriveMigrationName)]
|
|
||||||
pub struct Migration;
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl MigrationTrait for Migration {
|
|
||||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
|
||||||
let db = manager.get_connection();
|
|
||||||
|
|
||||||
db.execute_unprepared(
|
|
||||||
r#"
|
|
||||||
DROP TABLE "gallery_like";
|
|
||||||
DROP TABLE "gallery_post";
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
|
||||||
let db = manager.get_connection();
|
|
||||||
|
|
||||||
db.execute_unprepared(
|
|
||||||
r#"
|
|
||||||
create table gallery_post
|
|
||||||
(
|
|
||||||
id varchar(32) not null
|
|
||||||
constraint "PK_8e90d7b6015f2c4518881b14753"
|
|
||||||
primary key,
|
|
||||||
"createdAt" timestamp with time zone not null,
|
|
||||||
"updatedAt" timestamp with time zone not null,
|
|
||||||
title varchar(256) not null,
|
|
||||||
description varchar(2048),
|
|
||||||
"userId" varchar(32) not null
|
|
||||||
constraint "FK_985b836dddd8615e432d7043ddb"
|
|
||||||
references "user"
|
|
||||||
on delete cascade,
|
|
||||||
"fileIds" varchar(32)[] default '{}'::character varying[] not null,
|
|
||||||
"isSensitive" boolean default false not null,
|
|
||||||
"likedCount" integer default 0 not null,
|
|
||||||
tags varchar(128)[] default '{}'::character varying[] not null
|
|
||||||
);
|
|
||||||
|
|
||||||
comment on column gallery_post."createdAt" is 'The created date of the GalleryPost.';
|
|
||||||
|
|
||||||
comment on column gallery_post."updatedAt" is 'The updated date of the GalleryPost.';
|
|
||||||
|
|
||||||
comment on column gallery_post."userId" is 'The ID of author.';
|
|
||||||
|
|
||||||
comment on column gallery_post."isSensitive" is 'Whether the post is sensitive.';
|
|
||||||
|
|
||||||
create index "IDX_8f1a239bd077c8864a20c62c2c"
|
|
||||||
on gallery_post ("createdAt");
|
|
||||||
|
|
||||||
create index "IDX_f631d37835adb04792e361807c"
|
|
||||||
on gallery_post ("updatedAt");
|
|
||||||
|
|
||||||
create index "IDX_985b836dddd8615e432d7043dd"
|
|
||||||
on gallery_post ("userId");
|
|
||||||
|
|
||||||
create index "IDX_3ca50563facd913c425e7a89ee"
|
|
||||||
on gallery_post ("fileIds");
|
|
||||||
|
|
||||||
create index "IDX_f2d744d9a14d0dfb8b96cb7fc5"
|
|
||||||
on gallery_post ("isSensitive");
|
|
||||||
|
|
||||||
create index "IDX_1a165c68a49d08f11caffbd206"
|
|
||||||
on gallery_post ("likedCount");
|
|
||||||
|
|
||||||
create index "IDX_05cca34b985d1b8edc1d1e28df"
|
|
||||||
on gallery_post (tags);
|
|
||||||
|
|
||||||
create table gallery_like
|
|
||||||
(
|
|
||||||
id varchar(32) not null
|
|
||||||
constraint "PK_853ab02be39b8de45cd720cc15f"
|
|
||||||
primary key,
|
|
||||||
"createdAt" timestamp with time zone not null,
|
|
||||||
"userId" varchar(32) not null
|
|
||||||
constraint "FK_8fd5215095473061855ceb948cf"
|
|
||||||
references "user"
|
|
||||||
on delete cascade,
|
|
||||||
"postId" varchar(32) not null
|
|
||||||
constraint "FK_b1cb568bfe569e47b7051699fc8"
|
|
||||||
references gallery_post
|
|
||||||
on delete cascade
|
|
||||||
);
|
|
||||||
|
|
||||||
create index "IDX_8fd5215095473061855ceb948c"
|
|
||||||
on gallery_like ("userId");
|
|
||||||
|
|
||||||
create unique index "IDX_df1b5f4099e99fb0bc5eae53b6"
|
|
||||||
on gallery_like ("userId", "postId");
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,9 +4,9 @@ use ext_calckey_model_migration::{
|
||||||
};
|
};
|
||||||
use magnetar_sdk::types::SpanFilter;
|
use magnetar_sdk::types::SpanFilter;
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
ColumnTrait, Condition, ConnectionTrait, Cursor, DbErr, DynIden, EntityTrait, FromQueryResult,
|
ColumnTrait, Condition, ConnectionTrait, Cursor, CursorTrait, DbErr, DynIden, EntityTrait,
|
||||||
Iden, IntoIdentity, Iterable, JoinType, RelationDef, RelationTrait, Select, SelectModel,
|
FromQueryResult, Iden, IntoIdentity, Iterable, JoinType, RelationDef, RelationTrait, Select,
|
||||||
SelectorTrait,
|
SelectModel, SelectorTrait,
|
||||||
};
|
};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
|
|
@ -312,7 +312,7 @@ impl NoteResolver {
|
||||||
&mut select,
|
&mut select,
|
||||||
¬e_tbl,
|
¬e_tbl,
|
||||||
options.with_reply_target.then_some(1).unwrap_or_default(),
|
options.with_reply_target.then_some(1).unwrap_or_default(),
|
||||||
options.with_renote_target.then_some(2).unwrap_or_default(),
|
options.with_renote_target.then_some(1).unwrap_or_default(),
|
||||||
options,
|
options,
|
||||||
&self.user_resolver,
|
&self.user_resolver,
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
FollowingFolloweePopulated,
|
FollowingFolloweePopulated,
|
||||||
FollowingFollowerPopulated,
|
FollowingFollowerPopulated,
|
||||||
FollowRequest,
|
FollowRequest,
|
||||||
|
GalleryPost,
|
||||||
Instance,
|
Instance,
|
||||||
LiteInstanceMetadata,
|
LiteInstanceMetadata,
|
||||||
MeDetailed,
|
MeDetailed,
|
||||||
|
@ -562,6 +563,25 @@ export type Endpoints = {
|
||||||
"following/requests/list": { req: NoParams; res: FollowRequest[] };
|
"following/requests/list": { req: NoParams; res: FollowRequest[] };
|
||||||
"following/requests/reject": { req: { userId: User["id"] }; res: null };
|
"following/requests/reject": { req: { userId: User["id"] }; res: null };
|
||||||
|
|
||||||
|
// gallery
|
||||||
|
"gallery/featured": { req: TODO; res: TODO };
|
||||||
|
"gallery/popular": { req: TODO; res: TODO };
|
||||||
|
"gallery/posts": { req: TODO; res: TODO };
|
||||||
|
"gallery/posts/create": { req: TODO; res: TODO };
|
||||||
|
"gallery/posts/delete": { req: { postId: GalleryPost["id"] }; res: null };
|
||||||
|
"gallery/posts/like": { req: TODO; res: TODO };
|
||||||
|
"gallery/posts/show": { req: TODO; res: TODO };
|
||||||
|
"gallery/posts/unlike": { req: TODO; res: TODO };
|
||||||
|
"gallery/posts/update": { req: TODO; res: TODO };
|
||||||
|
|
||||||
|
// games
|
||||||
|
"games/reversi/games": { req: TODO; res: TODO };
|
||||||
|
"games/reversi/games/show": { req: TODO; res: TODO };
|
||||||
|
"games/reversi/games/surrender": { req: TODO; res: TODO };
|
||||||
|
"games/reversi/invitations": { req: TODO; res: TODO };
|
||||||
|
"games/reversi/match": { req: TODO; res: TODO };
|
||||||
|
"games/reversi/match/cancel": { req: TODO; res: TODO };
|
||||||
|
|
||||||
// get-online-users-count
|
// get-online-users-count
|
||||||
"get-online-users-count": { req: NoParams; res: { count: number } };
|
"get-online-users-count": { req: NoParams; res: { count: number } };
|
||||||
|
|
||||||
|
@ -591,6 +611,8 @@ export type Endpoints = {
|
||||||
};
|
};
|
||||||
res: NoteFavorite[];
|
res: NoteFavorite[];
|
||||||
};
|
};
|
||||||
|
"i/gallery/likes": { req: TODO; res: TODO };
|
||||||
|
"i/gallery/posts": { req: TODO; res: TODO };
|
||||||
"i/get-word-muted-notes-count": { req: TODO; res: TODO };
|
"i/get-word-muted-notes-count": { req: TODO; res: TODO };
|
||||||
"i/import-following": { req: TODO; res: TODO };
|
"i/import-following": { req: TODO; res: TODO };
|
||||||
"i/import-user-lists": { req: TODO; res: TODO };
|
"i/import-user-lists": { req: TODO; res: TODO };
|
||||||
|
@ -928,6 +950,7 @@ export type Endpoints = {
|
||||||
};
|
};
|
||||||
res: FollowingFolloweePopulated[];
|
res: FollowingFolloweePopulated[];
|
||||||
};
|
};
|
||||||
|
"users/gallery/posts": { req: TODO; res: TODO };
|
||||||
"users/get-frequently-replied-users": { req: TODO; res: TODO };
|
"users/get-frequently-replied-users": { req: TODO; res: TODO };
|
||||||
"users/lists/create": { req: { name: string }; res: UserList };
|
"users/lists/create": { req: { name: string }; res: UserList };
|
||||||
"users/lists/delete": { req: { listId: UserList["id"] }; res: null };
|
"users/lists/delete": { req: { listId: UserList["id"] }; res: null };
|
||||||
|
@ -957,6 +980,7 @@ export type Endpoints = {
|
||||||
res: Note[];
|
res: Note[];
|
||||||
};
|
};
|
||||||
"users/recommendation": { req: TODO; res: TODO };
|
"users/recommendation": { req: TODO; res: TODO };
|
||||||
|
"users/relation": { req: TODO; res: TODO };
|
||||||
"users/report-abuse": { req: TODO; res: TODO };
|
"users/report-abuse": { req: TODO; res: TODO };
|
||||||
"users/search-by-username-and-host": { req: TODO; res: TODO };
|
"users/search-by-username-and-host": { req: TODO; res: TODO };
|
||||||
"users/search": { req: TODO; res: TODO };
|
"users/search": { req: TODO; res: TODO };
|
||||||
|
|
|
@ -44,4 +44,12 @@ export const permissions = [
|
||||||
"read:reactions",
|
"read:reactions",
|
||||||
"write:reactions",
|
"write:reactions",
|
||||||
"write:votes",
|
"write:votes",
|
||||||
|
"read:pages",
|
||||||
|
"write:pages",
|
||||||
|
"write:page-likes",
|
||||||
|
"read:page-likes",
|
||||||
|
"read:gallery",
|
||||||
|
"write:gallery",
|
||||||
|
"read:gallery-likes",
|
||||||
|
"write:gallery-likes",
|
||||||
];
|
];
|
||||||
|
|
|
@ -121,6 +121,8 @@ export type DriveFile = {
|
||||||
|
|
||||||
export type DriveFolder = TODO;
|
export type DriveFolder = TODO;
|
||||||
|
|
||||||
|
export type GalleryPost = TODO;
|
||||||
|
|
||||||
export type Note = {
|
export type Note = {
|
||||||
id: ID;
|
id: ID;
|
||||||
createdAt: DateString;
|
createdAt: DateString;
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
<template>
|
||||||
|
<MkA :to="`/gallery/${post.id}`" class="ttasepnz _panel">
|
||||||
|
<div class="thumbnail">
|
||||||
|
<ImgWithBlurhash
|
||||||
|
class="img"
|
||||||
|
:src="post.files[0].thumbnailUrl"
|
||||||
|
:hash="post.files[0].blurhash"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<MagAvatarResolvingProxy :user="post.user" class="avatar" />
|
||||||
|
</header>
|
||||||
|
<footer>
|
||||||
|
<span class="title">{{ post.title }}</span>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
</MkA>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import ImgWithBlurhash from "@/components/MkImgWithBlurhash.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
post: any;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.ttasepnz {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
height: 200px;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--accent);
|
||||||
|
|
||||||
|
> .thumbnail {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
> article {
|
||||||
|
> footer {
|
||||||
|
&:before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .thumbnail {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
|
||||||
|
> .img {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> article {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
> header {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
> .avatar {
|
||||||
|
margin-left: auto;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 0 8px var(--shadow);
|
||||||
|
background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(rgba(0, 0, 0, 0.4), transparent);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .title {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -49,7 +49,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import * as misskey from "calckey-js";
|
import * as misskey from "calckey-js";
|
||||||
import PhotoSwipeLightbox from "photoswipe/lightbox";
|
import PhotoSwipeLightbox from "photoswipe/lightbox";
|
||||||
import PhotoSwipe from "photoswipe";
|
import PhotoSwipe from "photoswipe";
|
||||||
|
@ -68,6 +68,7 @@ const props = defineProps<{
|
||||||
inDm?: boolean;
|
inDm?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const gallery = ref(null);
|
||||||
const pswpZIndex = os.claimZIndex("middle");
|
const pswpZIndex = os.claimZIndex("middle");
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
@ -103,6 +104,7 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
}),
|
}),
|
||||||
|
gallery: gallery.value,
|
||||||
children: ".image",
|
children: ".image",
|
||||||
thumbSelector: ".image",
|
thumbSelector: ".image",
|
||||||
loop: false,
|
loop: false,
|
||||||
|
|
|
@ -63,6 +63,11 @@ export const navbarItemDef = reactive({
|
||||||
show: computed(() => $i != null),
|
show: computed(() => $i != null),
|
||||||
to: "/my/favorites",
|
to: "/my/favorites",
|
||||||
},
|
},
|
||||||
|
gallery: {
|
||||||
|
title: "gallery",
|
||||||
|
icon: "ph-image-square ph-bold ph-lg",
|
||||||
|
to: "/gallery",
|
||||||
|
},
|
||||||
clips: {
|
clips: {
|
||||||
title: "clips",
|
title: "clips",
|
||||||
icon: "ph-paperclip ph-bold ph-lg",
|
icon: "ph-paperclip ph-bold ph-lg",
|
||||||
|
|
|
@ -240,7 +240,6 @@ definePageMetadata(
|
||||||
|
|
||||||
> .name {
|
> .name {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
> .name,
|
> .name,
|
||||||
> .acct {
|
> .acct {
|
||||||
|
@ -310,7 +309,6 @@ definePageMetadata(
|
||||||
|
|
||||||
> .field {
|
> .field {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -321,7 +319,6 @@ definePageMetadata(
|
||||||
|
|
||||||
> .name {
|
> .name {
|
||||||
flex-basis: 33%;
|
flex-basis: 33%;
|
||||||
flex-shrink: 0;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
<template>
|
||||||
|
<MkStickyContainer>
|
||||||
|
<template #header
|
||||||
|
><MkPageHeader :actions="headerActions" :tabs="headerTabs"
|
||||||
|
/></template>
|
||||||
|
<MkSpacer :content-max="800" :margin-min="16" :margin-max="32">
|
||||||
|
<FormSuspense :p="init">
|
||||||
|
<FormInput v-model="title">
|
||||||
|
<template #label>{{ i18n.ts.title }}</template>
|
||||||
|
</FormInput>
|
||||||
|
|
||||||
|
<FormTextarea v-model="description" :max="500">
|
||||||
|
<template #label>{{ i18n.ts.description }}</template>
|
||||||
|
</FormTextarea>
|
||||||
|
|
||||||
|
<div class="">
|
||||||
|
<div
|
||||||
|
v-for="file in files"
|
||||||
|
:key="file.id"
|
||||||
|
class="wqugxsfx"
|
||||||
|
:style="{
|
||||||
|
backgroundImage: file
|
||||||
|
? `url(${file.thumbnailUrl})`
|
||||||
|
: null,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="name">{{ file.name }}</div>
|
||||||
|
<button
|
||||||
|
v-tooltip="i18n.ts.remove"
|
||||||
|
class="remove _button"
|
||||||
|
@click="remove(file)"
|
||||||
|
>
|
||||||
|
<i class="ph-x ph-bold ph-lg"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<FormButton primary @click="selectFile"
|
||||||
|
><i class="ph-plus ph-bold ph-lg"></i>
|
||||||
|
{{ i18n.ts.attachFile }}</FormButton
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormSwitch v-model="isSensitive">{{
|
||||||
|
i18n.ts.markAsSensitive
|
||||||
|
}}</FormSwitch>
|
||||||
|
|
||||||
|
<FormButton v-if="postId" primary @click="save"
|
||||||
|
><i class="ph-floppy-disk-back ph-bold ph-lg"></i>
|
||||||
|
{{ i18n.ts.save }}</FormButton
|
||||||
|
>
|
||||||
|
<FormButton v-else primary @click="save"
|
||||||
|
><i class="ph-floppy-disk-back ph-bold ph-lg"></i>
|
||||||
|
{{ i18n.ts.publish }}</FormButton
|
||||||
|
>
|
||||||
|
|
||||||
|
<FormButton v-if="postId" danger @click="del"
|
||||||
|
><i class="ph-trash ph-bold ph-lg"></i>
|
||||||
|
{{ i18n.ts.delete }}</FormButton
|
||||||
|
>
|
||||||
|
</FormSuspense>
|
||||||
|
</MkSpacer>
|
||||||
|
</MkStickyContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, inject, watch } from "vue";
|
||||||
|
import FormButton from "@/components/MkButton.vue";
|
||||||
|
import FormInput from "@/components/form/input.vue";
|
||||||
|
import FormTextarea from "@/components/form/textarea.vue";
|
||||||
|
import FormSwitch from "@/components/form/switch.vue";
|
||||||
|
import FormSuspense from "@/components/form/suspense.vue";
|
||||||
|
import { selectFiles } from "@/scripts/select-file";
|
||||||
|
import * as os from "@/os";
|
||||||
|
import { useRouter } from "@/router";
|
||||||
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
postId?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let init = $ref(null);
|
||||||
|
let files = $ref([]);
|
||||||
|
let description = $ref(null);
|
||||||
|
let title = $ref(null);
|
||||||
|
let isSensitive = $ref(false);
|
||||||
|
|
||||||
|
function selectFile(evt) {
|
||||||
|
selectFiles(evt.currentTarget ?? evt.target, null).then((selected) => {
|
||||||
|
files = files.concat(selected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(file) {
|
||||||
|
files = files.filter((f) => f.id !== file.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
if (props.postId) {
|
||||||
|
await os.apiWithDialog("gallery/posts/update", {
|
||||||
|
postId: props.postId,
|
||||||
|
title: title,
|
||||||
|
description: description,
|
||||||
|
fileIds: files.map((file) => file.id),
|
||||||
|
isSensitive: isSensitive,
|
||||||
|
});
|
||||||
|
router.push(`/gallery/${props.postId}`);
|
||||||
|
} else {
|
||||||
|
const created = await os.apiWithDialog("gallery/posts/create", {
|
||||||
|
title: title,
|
||||||
|
description: description,
|
||||||
|
fileIds: files.map((file) => file.id),
|
||||||
|
isSensitive: isSensitive,
|
||||||
|
});
|
||||||
|
router.push(`/gallery/${created.id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function del() {
|
||||||
|
const { canceled } = await os.confirm({
|
||||||
|
type: "warning",
|
||||||
|
text: i18n.ts.deleteConfirm,
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
await os.apiWithDialog("gallery/posts/delete", {
|
||||||
|
postId: props.postId,
|
||||||
|
});
|
||||||
|
router.push("/gallery");
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.postId,
|
||||||
|
() => {
|
||||||
|
init = () =>
|
||||||
|
props.postId
|
||||||
|
? os
|
||||||
|
.api("gallery/posts/show", {
|
||||||
|
postId: props.postId,
|
||||||
|
})
|
||||||
|
.then((post) => {
|
||||||
|
files = post.files;
|
||||||
|
title = post.title;
|
||||||
|
description = post.description;
|
||||||
|
isSensitive = post.isSensitive;
|
||||||
|
})
|
||||||
|
: Promise.resolve(null);
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const headerActions = $computed(() => []);
|
||||||
|
|
||||||
|
const headerTabs = $computed(() => []);
|
||||||
|
|
||||||
|
definePageMetadata(
|
||||||
|
computed(() =>
|
||||||
|
props.postId
|
||||||
|
? {
|
||||||
|
title: i18n.ts.edit,
|
||||||
|
icon: "ph-pencil ph-bold ph-lg",
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
title: i18n.ts.postToGallery,
|
||||||
|
icon: "ph-pencil ph-bold ph-lg",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.wqugxsfx {
|
||||||
|
height: 200px;
|
||||||
|
background-size: contain;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> .name {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 9px;
|
||||||
|
padding: 8px;
|
||||||
|
background: var(--panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .remove {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 9px;
|
||||||
|
padding: 8px;
|
||||||
|
background: var(--panel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,223 @@
|
||||||
|
<template>
|
||||||
|
<MkStickyContainer>
|
||||||
|
<template #header
|
||||||
|
><MkPageHeader
|
||||||
|
v-model:tab="tab"
|
||||||
|
:actions="headerActions"
|
||||||
|
:tabs="headerTabs"
|
||||||
|
:display-back-button="true"
|
||||||
|
/></template>
|
||||||
|
<MkSpacer :content-max="1200">
|
||||||
|
<swiper
|
||||||
|
:round-lengths="true"
|
||||||
|
:touch-angle="25"
|
||||||
|
:threshold="10"
|
||||||
|
:centeredSlides="true"
|
||||||
|
:modules="[Virtual]"
|
||||||
|
:space-between="20"
|
||||||
|
:virtual="true"
|
||||||
|
:allow-touch-move="
|
||||||
|
!(
|
||||||
|
deviceKind === 'desktop' &&
|
||||||
|
!defaultStore.state.swipeOnDesktop
|
||||||
|
)
|
||||||
|
"
|
||||||
|
@swiper="setSwiperRef"
|
||||||
|
@slide-change="onSlideChange"
|
||||||
|
>
|
||||||
|
<swiper-slide>
|
||||||
|
<MkFolder class="_gap">
|
||||||
|
<template #header
|
||||||
|
><i class="ph-clock ph-bold ph-lg"></i>
|
||||||
|
{{ i18n.ts.recentPosts }}</template
|
||||||
|
>
|
||||||
|
<MkPagination
|
||||||
|
v-slot="{ items }"
|
||||||
|
:pagination="recentPostsPagination"
|
||||||
|
:disable-auto-load="true"
|
||||||
|
>
|
||||||
|
<div class="vfpdbgtk">
|
||||||
|
<MkGalleryPostPreview
|
||||||
|
v-for="post in items"
|
||||||
|
:key="post.id"
|
||||||
|
:post="post"
|
||||||
|
class="post"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</MkPagination>
|
||||||
|
</MkFolder>
|
||||||
|
<MkFolder class="_gap">
|
||||||
|
<template #header
|
||||||
|
><i class="ph-fire-simple ph-bold ph-lg"></i>
|
||||||
|
{{ i18n.ts.popularPosts }}</template
|
||||||
|
>
|
||||||
|
<MkPagination
|
||||||
|
v-slot="{ items }"
|
||||||
|
:pagination="popularPostsPagination"
|
||||||
|
:disable-auto-load="true"
|
||||||
|
>
|
||||||
|
<div class="vfpdbgtk">
|
||||||
|
<MkGalleryPostPreview
|
||||||
|
v-for="post in items"
|
||||||
|
:key="post.id"
|
||||||
|
:post="post"
|
||||||
|
class="post"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</MkPagination>
|
||||||
|
</MkFolder>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide>
|
||||||
|
<MkPagination
|
||||||
|
v-slot="{ items }"
|
||||||
|
:pagination="likedPostsPagination"
|
||||||
|
>
|
||||||
|
<div class="vfpdbgtk">
|
||||||
|
<MkGalleryPostPreview
|
||||||
|
v-for="like in items"
|
||||||
|
:key="like.id"
|
||||||
|
:post="like.post"
|
||||||
|
class="post"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</MkPagination>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide>
|
||||||
|
<MkA to="/gallery/new" class="_link" style="margin: 16px"
|
||||||
|
><i class="ph-plus ph-bold ph-lg"></i>
|
||||||
|
{{ i18n.ts.postToGallery }}</MkA
|
||||||
|
>
|
||||||
|
<MkPagination
|
||||||
|
v-slot="{ items }"
|
||||||
|
:pagination="myPostsPagination"
|
||||||
|
>
|
||||||
|
<div class="vfpdbgtk">
|
||||||
|
<MkGalleryPostPreview
|
||||||
|
v-for="post in items"
|
||||||
|
:key="post.id"
|
||||||
|
:post="post"
|
||||||
|
class="post"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</MkPagination>
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper>
|
||||||
|
</MkSpacer>
|
||||||
|
</MkStickyContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, defineComponent, watch, onMounted } from "vue";
|
||||||
|
import { Virtual } from "swiper";
|
||||||
|
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||||
|
import MkFolder from "@/components/MkFolder.vue";
|
||||||
|
import MkPagination from "@/components/MkPagination.vue";
|
||||||
|
import MkGalleryPostPreview from "@/components/MkGalleryPostPreview.vue";
|
||||||
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
import { deviceKind } from "@/scripts/device-kind";
|
||||||
|
import { i18n } from "@/i18n";
|
||||||
|
import { useRouter } from "@/router";
|
||||||
|
import { defaultStore } from "@/store";
|
||||||
|
import "swiper/scss";
|
||||||
|
import "swiper/scss/virtual";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
tag?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const tabs = ["explore", "liked", "my"];
|
||||||
|
let tab = $ref(tabs[0]);
|
||||||
|
watch($$(tab), () => syncSlide(tabs.indexOf(tab)));
|
||||||
|
|
||||||
|
let tagsRef = $ref();
|
||||||
|
|
||||||
|
const recentPostsPagination = {
|
||||||
|
endpoint: "gallery/posts" as const,
|
||||||
|
limit: 6,
|
||||||
|
};
|
||||||
|
const popularPostsPagination = {
|
||||||
|
endpoint: "gallery/featured" as const,
|
||||||
|
limit: 5,
|
||||||
|
};
|
||||||
|
const myPostsPagination = {
|
||||||
|
endpoint: "i/gallery/posts" as const,
|
||||||
|
limit: 5,
|
||||||
|
};
|
||||||
|
const likedPostsPagination = {
|
||||||
|
endpoint: "i/gallery/likes" as const,
|
||||||
|
limit: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.tag,
|
||||||
|
() => {
|
||||||
|
if (tagsRef) tagsRef.tags.toggleContent(props.tag == null);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const headerActions = $computed(() => [
|
||||||
|
{
|
||||||
|
icon: "ph-plus ph-bold ph-lg",
|
||||||
|
text: i18n.ts.create,
|
||||||
|
handler: () => {
|
||||||
|
router.push("/gallery/new");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const headerTabs = $computed(() => [
|
||||||
|
{
|
||||||
|
key: "explore",
|
||||||
|
title: i18n.ts.gallery,
|
||||||
|
icon: "ph-image-square ph-bold ph-lg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "liked",
|
||||||
|
title: i18n.ts._gallery.liked,
|
||||||
|
icon: "ph-heart ph-bold ph-lg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "my",
|
||||||
|
title: i18n.ts._gallery.my,
|
||||||
|
icon: "ph-crown-simple ph-bold ph-lg",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
definePageMetadata({
|
||||||
|
title: i18n.ts.gallery,
|
||||||
|
icon: "ph-image-square ph-bold ph-lg",
|
||||||
|
});
|
||||||
|
|
||||||
|
let swiperRef = null;
|
||||||
|
|
||||||
|
function setSwiperRef(swiper) {
|
||||||
|
swiperRef = swiper;
|
||||||
|
syncSlide(tabs.indexOf(tab));
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSlideChange() {
|
||||||
|
tab = tabs[swiperRef.activeIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncSlide(index) {
|
||||||
|
swiperRef.slideTo(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
syncSlide(tabs.indexOf(swiperRef.activeIndex));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vfpdbgtk {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||||
|
grid-gap: 12px;
|
||||||
|
margin: 0 var(--margin);
|
||||||
|
|
||||||
|
> .post {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,360 @@
|
||||||
|
<template>
|
||||||
|
<MkStickyContainer>
|
||||||
|
<template #header
|
||||||
|
><MkPageHeader :actions="headerActions" :tabs="headerTabs"
|
||||||
|
/></template>
|
||||||
|
<MkSpacer :content-max="1000" :margin-min="16" :margin-max="32">
|
||||||
|
<div class="_root">
|
||||||
|
<transition
|
||||||
|
:name="$store.state.animation ? 'fade' : ''"
|
||||||
|
mode="out-in"
|
||||||
|
>
|
||||||
|
<div v-if="post" class="rkxwuolj">
|
||||||
|
<div class="files">
|
||||||
|
<div
|
||||||
|
v-for="file in post.files"
|
||||||
|
:key="file.id"
|
||||||
|
class="file"
|
||||||
|
>
|
||||||
|
<img :src="file.url" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="body _block">
|
||||||
|
<div class="title">{{ post.title }}</div>
|
||||||
|
<div class="description">
|
||||||
|
<Mfm :text="post.description" />
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<i class="ph-clock ph-bold ph-lg"></i>
|
||||||
|
<MkTime :time="post.createdAt" mode="detail" />
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<div class="like">
|
||||||
|
<MkButton
|
||||||
|
v-if="post.isLiked"
|
||||||
|
v-tooltip="i18n.ts._gallery.unlike"
|
||||||
|
class="button"
|
||||||
|
primary
|
||||||
|
@click="unlike()"
|
||||||
|
><i class="ph-heart ph-fill ph-lg"></i
|
||||||
|
><span
|
||||||
|
v-if="post.likedCount > 0"
|
||||||
|
class="count"
|
||||||
|
>{{ post.likedCount }}</span
|
||||||
|
></MkButton
|
||||||
|
>
|
||||||
|
<MkButton
|
||||||
|
v-else
|
||||||
|
v-tooltip="i18n.ts._gallery.like"
|
||||||
|
class="button"
|
||||||
|
@click="like()"
|
||||||
|
><i class="ph-heart ph-bold"></i
|
||||||
|
><span
|
||||||
|
v-if="post.likedCount > 0"
|
||||||
|
class="count"
|
||||||
|
>{{ post.likedCount }}</span
|
||||||
|
></MkButton
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="other">
|
||||||
|
<button
|
||||||
|
v-if="$i && $i.id === post.user.id"
|
||||||
|
v-tooltip="i18n.ts.edit"
|
||||||
|
class="_button"
|
||||||
|
@click="edit"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="ph-pencil ph-bold ph-lg ph-fw ph-lg"
|
||||||
|
></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-tooltip="i18n.ts.shareWithNote"
|
||||||
|
class="_button"
|
||||||
|
@click="shareWithNote"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="ph-repeat ph-bold ph-lg ph-fw ph-lg"
|
||||||
|
></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="shareAvailable()"
|
||||||
|
v-tooltip="i18n.ts.share"
|
||||||
|
class="_button"
|
||||||
|
@click="share"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="ph-share-network ph-bold ph-lg ph-fw ph-lg"
|
||||||
|
></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="user">
|
||||||
|
<MagAvatarResolvingProxy
|
||||||
|
:user="post.user"
|
||||||
|
class="avatar"
|
||||||
|
/>
|
||||||
|
<div class="name">
|
||||||
|
<MkUserName
|
||||||
|
:user="post.user"
|
||||||
|
style="display: block"
|
||||||
|
/>
|
||||||
|
<MkAcct :user="post.user" />
|
||||||
|
</div>
|
||||||
|
<MkFollowButton
|
||||||
|
v-if="!$i || $i.id != post.user.id"
|
||||||
|
:user="post.user"
|
||||||
|
:inline="true"
|
||||||
|
:transparent="false"
|
||||||
|
:full="true"
|
||||||
|
large
|
||||||
|
class="koudoku"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<MkContainer
|
||||||
|
:max-height="300"
|
||||||
|
:foldable="true"
|
||||||
|
class="other"
|
||||||
|
>
|
||||||
|
<template #header
|
||||||
|
><i class="ph-clock ph-bold ph-lg"></i>
|
||||||
|
{{ i18n.ts.recentPosts }}</template
|
||||||
|
>
|
||||||
|
<MkPagination
|
||||||
|
v-slot="{ items }"
|
||||||
|
:pagination="otherPostsPagination"
|
||||||
|
>
|
||||||
|
<div class="sdrarzaf">
|
||||||
|
<MkGalleryPostPreview
|
||||||
|
v-for="post in items"
|
||||||
|
:key="post.id"
|
||||||
|
:post="post"
|
||||||
|
class="post"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</MkPagination>
|
||||||
|
</MkContainer>
|
||||||
|
</div>
|
||||||
|
<MkError v-else-if="error" @retry="fetch()" />
|
||||||
|
<MkLoading v-else />
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</MkSpacer>
|
||||||
|
</MkStickyContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, watch } from "vue";
|
||||||
|
import MkButton from "@/components/MkButton.vue";
|
||||||
|
import * as os from "@/os";
|
||||||
|
import MkContainer from "@/components/MkContainer.vue";
|
||||||
|
import MkPagination from "@/components/MkPagination.vue";
|
||||||
|
import MkGalleryPostPreview from "@/components/MkGalleryPostPreview.vue";
|
||||||
|
import MkFollowButton from "@/components/MkFollowButton.vue";
|
||||||
|
import { url } from "@/config";
|
||||||
|
import { useRouter } from "@/router";
|
||||||
|
import { i18n } from "@/i18n";
|
||||||
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
import { shareAvailable } from "@/scripts/share-available";
|
||||||
|
import { $i } from "@/account";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
postId: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let post = $ref(null);
|
||||||
|
let error = $ref(null);
|
||||||
|
const otherPostsPagination = {
|
||||||
|
endpoint: "users/gallery/posts" as const,
|
||||||
|
limit: 6,
|
||||||
|
params: computed(() => ({
|
||||||
|
userId: post.user.id,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
function fetchPost() {
|
||||||
|
post = null;
|
||||||
|
os.api("gallery/posts/show", {
|
||||||
|
postId: props.postId,
|
||||||
|
})
|
||||||
|
.then((_post) => {
|
||||||
|
post = _post;
|
||||||
|
})
|
||||||
|
.catch((_error) => {
|
||||||
|
error = _error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function share() {
|
||||||
|
navigator.share({
|
||||||
|
title: post.title,
|
||||||
|
text: post.description,
|
||||||
|
url: `${url}/gallery/${post.id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function shareWithNote() {
|
||||||
|
os.post({
|
||||||
|
initialText: `${post.title} ${url}/gallery/${post.id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function like() {
|
||||||
|
os.api("gallery/posts/like", {
|
||||||
|
postId: props.postId,
|
||||||
|
}).then(() => {
|
||||||
|
post.isLiked = true;
|
||||||
|
post.likedCount++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unlike() {
|
||||||
|
os.api("gallery/posts/unlike", {
|
||||||
|
postId: props.postId,
|
||||||
|
}).then(() => {
|
||||||
|
post.isLiked = false;
|
||||||
|
post.likedCount--;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit() {
|
||||||
|
router.push(`/gallery/${post.id}/edit`);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.postId, fetchPost, { immediate: true });
|
||||||
|
|
||||||
|
const headerActions = $computed(() => [
|
||||||
|
{
|
||||||
|
icon: "ph-pencil ph-bold ph-lg",
|
||||||
|
text: i18n.ts.edit,
|
||||||
|
handler: edit,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const headerTabs = $computed(() => []);
|
||||||
|
|
||||||
|
definePageMetadata(
|
||||||
|
computed(() =>
|
||||||
|
post
|
||||||
|
? {
|
||||||
|
title: post.title,
|
||||||
|
avatar: post.user,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: opacity 0.125s ease;
|
||||||
|
}
|
||||||
|
.fade-enter-from,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rkxwuolj {
|
||||||
|
> .files {
|
||||||
|
> .file {
|
||||||
|
> img {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& + .file {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .body {
|
||||||
|
padding: 32px;
|
||||||
|
|
||||||
|
> .title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .info {
|
||||||
|
margin-top: 16px;
|
||||||
|
font-size: 90%;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 16px 0 0 0;
|
||||||
|
border-top: solid 0.5px var(--divider);
|
||||||
|
|
||||||
|
> .like {
|
||||||
|
> .button {
|
||||||
|
--accent: #eb6f92;
|
||||||
|
--X8: #eb6f92;
|
||||||
|
--buttonBg: rgb(216 71 106 / 5%);
|
||||||
|
--buttonHoverBg: rgb(216 71 106 / 10%);
|
||||||
|
color: #eb6f92;
|
||||||
|
|
||||||
|
::v-deep(.count) {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .other {
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
> button {
|
||||||
|
padding: 8px;
|
||||||
|
margin: 0 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--fgHighlighted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .user {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 16px 0 0 0;
|
||||||
|
border-top: solid 0.5px var(--divider);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> .avatar {
|
||||||
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .name {
|
||||||
|
margin: 0 0 0 12px;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .koudoku {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sdrarzaf {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||||
|
grid-gap: 12px;
|
||||||
|
margin: var(--margin);
|
||||||
|
|
||||||
|
> .post {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,45 @@
|
||||||
|
<template>
|
||||||
|
<MkSpacer :content-max="800">
|
||||||
|
<MkPagination v-slot="{ items }" :pagination="pagination">
|
||||||
|
<div class="jrnovfpt">
|
||||||
|
<MkGalleryPostPreview
|
||||||
|
v-for="post in items"
|
||||||
|
:key="post.id"
|
||||||
|
:post="post"
|
||||||
|
class="post"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</MkPagination>
|
||||||
|
</MkSpacer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from "vue";
|
||||||
|
import MkGalleryPostPreview from "@/components/MkGalleryPostPreview.vue";
|
||||||
|
import MkPagination from "@/components/MkPagination.vue";
|
||||||
|
import { packed } from "magnetar-common";
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
user: packed.PackUserBase;
|
||||||
|
}>(),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const pagination = {
|
||||||
|
endpoint: "users/gallery/posts" as const,
|
||||||
|
limit: 6,
|
||||||
|
params: computed(() => ({
|
||||||
|
userId: props.user.id,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.jrnovfpt {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||||
|
grid-gap: 12px;
|
||||||
|
margin: var(--margin);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -18,6 +18,7 @@
|
||||||
/>
|
/>
|
||||||
<XReactions v-else-if="tab === 'reactions'" :user="user" />
|
<XReactions v-else-if="tab === 'reactions'" :user="user" />
|
||||||
<XClips v-else-if="tab === 'clips'" :user="user" />
|
<XClips v-else-if="tab === 'clips'" :user="user" />
|
||||||
|
<XGallery v-else-if="tab === 'gallery'" :user="user" />
|
||||||
</div>
|
</div>
|
||||||
<MkError v-else-if="error" @retry="fetchUser()" />
|
<MkError v-else-if="error" @retry="fetchUser()" />
|
||||||
<MkLoading v-else />
|
<MkLoading v-else />
|
||||||
|
@ -40,6 +41,7 @@ import * as Acct from "calckey-js/built/acct";
|
||||||
const XHome = defineAsyncComponent(() => import("./home.vue"));
|
const XHome = defineAsyncComponent(() => import("./home.vue"));
|
||||||
const XReactions = defineAsyncComponent(() => import("./reactions.vue"));
|
const XReactions = defineAsyncComponent(() => import("./reactions.vue"));
|
||||||
const XClips = defineAsyncComponent(() => import("./clips.vue"));
|
const XClips = defineAsyncComponent(() => import("./clips.vue"));
|
||||||
|
const XGallery = defineAsyncComponent(() => import("./gallery.vue"));
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -140,6 +142,11 @@ const headerTabs = $computed(() =>
|
||||||
title: i18n.ts.clips,
|
title: i18n.ts.clips,
|
||||||
icon: "ph-paperclip ph-bold ph-lg",
|
icon: "ph-paperclip ph-bold ph-lg",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "gallery",
|
||||||
|
title: i18n.ts.gallery,
|
||||||
|
icon: "ph-image-square ph-bold ph-lg",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
]
|
]
|
||||||
|
|
|
@ -364,6 +364,24 @@ export const routes = [
|
||||||
path: "/tags/:tag",
|
path: "/tags/:tag",
|
||||||
component: page(() => import("./pages/tag.vue")),
|
component: page(() => import("./pages/tag.vue")),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/gallery/:postId/edit",
|
||||||
|
component: page(() => import("./pages/gallery/edit.vue")),
|
||||||
|
loginRequired: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/gallery/new",
|
||||||
|
component: page(() => import("./pages/gallery/edit.vue")),
|
||||||
|
loginRequired: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/gallery/:postId",
|
||||||
|
component: page(() => import("./pages/gallery/post.vue")),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/gallery",
|
||||||
|
component: page(() => import("./pages/gallery/index.vue")),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/registry/keys/system/:path(*)?",
|
path: "/registry/keys/system/:path(*)?",
|
||||||
component: page(() => import("./pages/registry.keys.vue")),
|
component: page(() => import("./pages/registry.keys.vue")),
|
||||||
|
|
|
@ -47,6 +47,10 @@
|
||||||
><i class="ph-compass ph-bold ph-lg icon"></i
|
><i class="ph-compass ph-bold ph-lg icon"></i
|
||||||
>{{ i18n.ts.explore }}</MkA
|
>{{ i18n.ts.explore }}</MkA
|
||||||
>
|
>
|
||||||
|
<MkA to="/gallery" class="link" active-class="active"
|
||||||
|
><i class="ph-image-square ph-bold ph-lg icon"></i
|
||||||
|
>{{ i18n.ts.gallery }}</MkA
|
||||||
|
>
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<button class="_buttonPrimary" @click="signup()">
|
<button class="_buttonPrimary" @click="signup()">
|
||||||
{{ i18n.ts.signup }}
|
{{ i18n.ts.signup }}
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
><i class="ph-compass ph-bold ph-lg icon"></i
|
><i class="ph-compass ph-bold ph-lg icon"></i
|
||||||
>{{ i18n.ts.explore }}</MkA
|
>{{ i18n.ts.explore }}</MkA
|
||||||
>
|
>
|
||||||
|
<MkA to="/gallery" class="link" active-class="active"
|
||||||
|
><i class="ph-image-square ph-bold ph-lg icon"></i
|
||||||
|
>{{ i18n.ts.gallery }}</MkA
|
||||||
|
>
|
||||||
<div v-if="info" class="page active link">
|
<div v-if="info" class="page active link">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<i v-if="info.icon" class="icon" :class="info.icon"></i>
|
<i v-if="info.icon" class="icon" :class="info.icon"></i>
|
||||||
|
|
Loading…
Reference in New Issue