Compare commits
6 Commits
789852211b
...
ff00dfebb6
Author | SHA1 | Date |
---|---|---|
Natty | ff00dfebb6 | |
Natty | 364074aecc | |
Natty | d16f9d704d | |
Natty | ce1bfc0ee1 | |
Natty | 4dd3cd59ac | |
Natty | 13f5ad3672 |
File diff suppressed because it is too large
Load Diff
|
@ -26,7 +26,7 @@ edition = "2021"
|
|||
async-trait = "0.1"
|
||||
axum = "0.7"
|
||||
axum-extra = "0.9"
|
||||
cached = "0.46"
|
||||
cached = "0.47"
|
||||
cfg-if = "1"
|
||||
chrono = "0.4"
|
||||
compact_str = "0.7"
|
||||
|
|
|
@ -26,4 +26,3 @@ strum = { workspace = true }
|
|||
chrono = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
once_cell = "1.18.0"
|
||||
|
|
|
@ -65,8 +65,6 @@ pub enum Relation {
|
|||
on_delete = "SetNull"
|
||||
)]
|
||||
DriveFolder,
|
||||
#[sea_orm(has_many = "super::page::Entity")]
|
||||
Page,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
|
@ -83,12 +81,6 @@ 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 {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
//! `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 {}
|
|
@ -1,54 +0,0 @@
|
|||
//! `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,8 +20,6 @@ pub mod drive_folder;
|
|||
pub mod emoji;
|
||||
pub mod follow_request;
|
||||
pub mod following;
|
||||
pub mod gallery_like;
|
||||
pub mod gallery_post;
|
||||
pub mod hashtag;
|
||||
pub mod instance;
|
||||
pub mod meta;
|
||||
|
@ -36,8 +34,6 @@ pub mod note_thread_muting;
|
|||
pub mod note_unread;
|
||||
pub mod note_watching;
|
||||
pub mod notification;
|
||||
pub mod page;
|
||||
pub mod page_like;
|
||||
pub mod password_reset_request;
|
||||
pub mod poll;
|
||||
pub mod poll_vote;
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
//! `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 {}
|
|
@ -1,51 +0,0 @@
|
|||
//! `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,8 +18,6 @@ pub use super::drive_folder::Entity as DriveFolder;
|
|||
pub use super::emoji::Entity as Emoji;
|
||||
pub use super::follow_request::Entity as FollowRequest;
|
||||
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::instance::Entity as Instance;
|
||||
pub use super::meta::Entity as Meta;
|
||||
|
@ -34,8 +32,6 @@ pub use super::note_thread_muting::Entity as NoteThreadMuting;
|
|||
pub use super::note_unread::Entity as NoteUnread;
|
||||
pub use super::note_watching::Entity as NoteWatching;
|
||||
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::poll::Entity as Poll;
|
||||
pub use super::poll_vote::Entity as PollVote;
|
||||
|
|
|
@ -118,20 +118,6 @@ pub enum NotificationTypeEnum {
|
|||
Reply,
|
||||
}
|
||||
#[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(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
|
|
|
@ -106,10 +106,6 @@ pub enum Relation {
|
|||
DriveFile1,
|
||||
#[sea_orm(has_many = "super::drive_folder::Entity")]
|
||||
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")]
|
||||
Meta,
|
||||
#[sea_orm(has_many = "super::moderation_log::Entity")]
|
||||
|
@ -128,10 +124,6 @@ pub enum Relation {
|
|||
NoteUnread,
|
||||
#[sea_orm(has_many = "super::note_watching::Entity")]
|
||||
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")]
|
||||
PasswordResetRequest,
|
||||
#[sea_orm(has_many = "super::poll_vote::Entity")]
|
||||
|
@ -210,18 +202,6 @@ 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 {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Meta.def()
|
||||
|
@ -276,18 +256,6 @@ 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 {
|
||||
fn to() -> RelationDef {
|
||||
Relation::PasswordResetRequest.def()
|
||||
|
|
|
@ -42,8 +42,6 @@ pub struct Model {
|
|||
pub security_keys_available: bool,
|
||||
#[sea_orm(column_name = "usePasswordLessLogin")]
|
||||
pub use_password_less_login: bool,
|
||||
#[sea_orm(column_name = "pinnedPageId", unique)]
|
||||
pub pinned_page_id: Option<String>,
|
||||
#[sea_orm(column_type = "JsonBinary")]
|
||||
pub room: Json,
|
||||
#[sea_orm(column_type = "JsonBinary")]
|
||||
|
@ -77,14 +75,6 @@ pub struct Model {
|
|||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
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(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
|
@ -95,12 +85,6 @@ pub enum Relation {
|
|||
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()
|
||||
|
|
|
@ -7,6 +7,8 @@ mod m20230806_142918_drop_featured_note_option;
|
|||
mod m20240107_005747_remove_user_groups;
|
||||
mod m20240107_220523_generated_is_quote;
|
||||
mod m20240107_224446_generated_is_renote;
|
||||
mod m20240112_215106_remove_pages;
|
||||
mod m20240112_234759_remove_gallery;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
|
@ -21,6 +23,8 @@ impl MigratorTrait for Migrator {
|
|||
Box::new(m20240107_005747_remove_user_groups::Migration),
|
||||
Box::new(m20240107_220523_generated_is_quote::Migration),
|
||||
Box::new(m20240107_224446_generated_is_renote::Migration),
|
||||
Box::new(m20240112_215106_remove_pages::Migration),
|
||||
Box::new(m20240112_234759_remove_gallery::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
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(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
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 sea_orm::{
|
||||
ColumnTrait, Condition, ConnectionTrait, Cursor, CursorTrait, DbErr, DynIden, EntityTrait,
|
||||
FromQueryResult, Iden, IntoIdentity, Iterable, JoinType, RelationDef, RelationTrait, Select,
|
||||
SelectModel, SelectorTrait,
|
||||
ColumnTrait, Condition, ConnectionTrait, Cursor, DbErr, DynIden, EntityTrait, FromQueryResult,
|
||||
Iden, IntoIdentity, Iterable, JoinType, RelationDef, RelationTrait, Select, SelectModel,
|
||||
SelectorTrait,
|
||||
};
|
||||
use std::fmt::Write;
|
||||
|
||||
|
|
|
@ -312,7 +312,7 @@ impl NoteResolver {
|
|||
&mut select,
|
||||
¬e_tbl,
|
||||
options.with_reply_target.then_some(1).unwrap_or_default(),
|
||||
options.with_renote_target.then_some(1).unwrap_or_default(),
|
||||
options.with_renote_target.then_some(2).unwrap_or_default(),
|
||||
options,
|
||||
&self.user_resolver,
|
||||
);
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
FollowingFolloweePopulated,
|
||||
FollowingFollowerPopulated,
|
||||
FollowRequest,
|
||||
GalleryPost,
|
||||
Instance,
|
||||
LiteInstanceMetadata,
|
||||
MeDetailed,
|
||||
|
@ -563,25 +562,6 @@ export type Endpoints = {
|
|||
"following/requests/list": { req: NoParams; res: FollowRequest[] };
|
||||
"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": { req: NoParams; res: { count: number } };
|
||||
|
||||
|
@ -611,8 +591,6 @@ export type Endpoints = {
|
|||
};
|
||||
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/import-following": { req: TODO; res: TODO };
|
||||
"i/import-user-lists": { req: TODO; res: TODO };
|
||||
|
@ -950,7 +928,6 @@ export type Endpoints = {
|
|||
};
|
||||
res: FollowingFolloweePopulated[];
|
||||
};
|
||||
"users/gallery/posts": { req: TODO; res: TODO };
|
||||
"users/get-frequently-replied-users": { req: TODO; res: TODO };
|
||||
"users/lists/create": { req: { name: string }; res: UserList };
|
||||
"users/lists/delete": { req: { listId: UserList["id"] }; res: null };
|
||||
|
@ -980,7 +957,6 @@ export type Endpoints = {
|
|||
res: Note[];
|
||||
};
|
||||
"users/recommendation": { req: TODO; res: TODO };
|
||||
"users/relation": { req: TODO; res: TODO };
|
||||
"users/report-abuse": { req: TODO; res: TODO };
|
||||
"users/search-by-username-and-host": { req: TODO; res: TODO };
|
||||
"users/search": { req: TODO; res: TODO };
|
||||
|
|
|
@ -44,12 +44,4 @@ export const permissions = [
|
|||
"read:reactions",
|
||||
"write:reactions",
|
||||
"write:votes",
|
||||
"read:pages",
|
||||
"write:pages",
|
||||
"write:page-likes",
|
||||
"read:page-likes",
|
||||
"read:gallery",
|
||||
"write:gallery",
|
||||
"read:gallery-likes",
|
||||
"write:gallery-likes",
|
||||
];
|
||||
|
|
|
@ -121,8 +121,6 @@ export type DriveFile = {
|
|||
|
||||
export type DriveFolder = TODO;
|
||||
|
||||
export type GalleryPost = TODO;
|
||||
|
||||
export type Note = {
|
||||
id: ID;
|
||||
createdAt: DateString;
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
<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>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { onMounted } from "vue";
|
||||
import * as misskey from "calckey-js";
|
||||
import PhotoSwipeLightbox from "photoswipe/lightbox";
|
||||
import PhotoSwipe from "photoswipe";
|
||||
|
@ -68,7 +68,6 @@ const props = defineProps<{
|
|||
inDm?: boolean;
|
||||
}>();
|
||||
|
||||
const gallery = ref(null);
|
||||
const pswpZIndex = os.claimZIndex("middle");
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -104,7 +103,6 @@ onMounted(() => {
|
|||
}
|
||||
return item;
|
||||
}),
|
||||
gallery: gallery.value,
|
||||
children: ".image",
|
||||
thumbSelector: ".image",
|
||||
loop: false,
|
||||
|
|
|
@ -63,11 +63,6 @@ export const navbarItemDef = reactive({
|
|||
show: computed(() => $i != null),
|
||||
to: "/my/favorites",
|
||||
},
|
||||
gallery: {
|
||||
title: "gallery",
|
||||
icon: "ph-image-square ph-bold ph-lg",
|
||||
to: "/gallery",
|
||||
},
|
||||
clips: {
|
||||
title: "clips",
|
||||
icon: "ph-paperclip ph-bold ph-lg",
|
||||
|
|
|
@ -1,195 +0,0 @@
|
|||
<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>
|
|
@ -1,223 +0,0 @@
|
|||
<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>
|
|
@ -1,360 +0,0 @@
|
|||
<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>
|
|
@ -1,45 +0,0 @@
|
|||
<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,7 +18,6 @@
|
|||
/>
|
||||
<XReactions v-else-if="tab === 'reactions'" :user="user" />
|
||||
<XClips v-else-if="tab === 'clips'" :user="user" />
|
||||
<XGallery v-else-if="tab === 'gallery'" :user="user" />
|
||||
</div>
|
||||
<MkError v-else-if="error" @retry="fetchUser()" />
|
||||
<MkLoading v-else />
|
||||
|
@ -41,7 +40,6 @@ import * as Acct from "calckey-js/built/acct";
|
|||
const XHome = defineAsyncComponent(() => import("./home.vue"));
|
||||
const XReactions = defineAsyncComponent(() => import("./reactions.vue"));
|
||||
const XClips = defineAsyncComponent(() => import("./clips.vue"));
|
||||
const XGallery = defineAsyncComponent(() => import("./gallery.vue"));
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
@ -142,11 +140,6 @@ const headerTabs = $computed(() =>
|
|||
title: i18n.ts.clips,
|
||||
icon: "ph-paperclip ph-bold ph-lg",
|
||||
},
|
||||
{
|
||||
key: "gallery",
|
||||
title: i18n.ts.gallery,
|
||||
icon: "ph-image-square ph-bold ph-lg",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
|
|
|
@ -364,24 +364,6 @@ export const routes = [
|
|||
path: "/tags/:tag",
|
||||
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(*)?",
|
||||
component: page(() => import("./pages/registry.keys.vue")),
|
||||
|
|
|
@ -47,10 +47,6 @@
|
|||
><i class="ph-compass ph-bold ph-lg icon"></i
|
||||
>{{ 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">
|
||||
<button class="_buttonPrimary" @click="signup()">
|
||||
{{ i18n.ts.signup }}
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
><i class="ph-compass ph-bold ph-lg icon"></i
|
||||
>{{ 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 class="title">
|
||||
<i v-if="info.icon" class="icon" :class="info.icon"></i>
|
||||
|
|
Loading…
Reference in New Issue