refactor: extract view-model mappers from presentation handlers
Some checks failed
CI / Check / Test (push) Has been cancelled
Some checks failed
CI / Check / Test (push) Has been cancelled
Move mapping logic (domain→DTO/template structs) into mappers/ module. Handlers now call mapper fns instead of inline conversions.
This commit is contained in:
@@ -38,9 +38,7 @@ use application::{
|
|||||||
};
|
};
|
||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
models::{
|
models::{ExportFormat, PersonId, collections::PageParams},
|
||||||
DiaryEntry, ExportFormat, Movie, MovieSummary, PersonId, Review, collections::PageParams,
|
|
||||||
},
|
|
||||||
services::review_history::Trend,
|
services::review_history::Trend,
|
||||||
value_objects::UserId,
|
value_objects::UserId,
|
||||||
};
|
};
|
||||||
@@ -57,13 +55,13 @@ use api_types::search::{
|
|||||||
};
|
};
|
||||||
use api_types::{
|
use api_types::{
|
||||||
ActivityFeedQueryParams, ActivityFeedResponse, AddToWatchlistRequest, CastMemberDto,
|
ActivityFeedQueryParams, ActivityFeedResponse, AddToWatchlistRequest, CastMemberDto,
|
||||||
CrewMemberDto, DiaryEntryDto, DiaryQueryParams, DiaryResponse, DirectorStatDto,
|
CrewMemberDto, DiaryQueryParams, DiaryResponse, DirectorStatDto, ExportQueryParams, GenreDto,
|
||||||
ExportQueryParams, FeedEntryDto, GenreDto, KeywordDto, LogReviewRequest, LoginRequest,
|
KeywordDto, LogReviewRequest, LoginRequest, LoginResponse, MonthActivityDto, MonthlyRatingDto,
|
||||||
LoginResponse, MonthActivityDto, MonthlyRatingDto, MovieDetailResponse, MovieDto,
|
MovieDetailResponse, MovieProfileResponse, MovieStatsDto, MoviesQueryParams, MoviesResponse,
|
||||||
MovieProfileResponse, MovieStatsDto, MoviesQueryParams, MoviesResponse, PaginationQueryParams,
|
PaginationQueryParams, ProfileResponse, RegisterRequest, ReviewHistoryResponse,
|
||||||
ProfileResponse, RegisterRequest, ReviewDto, ReviewHistoryResponse, SocialFeedResponse,
|
SocialFeedResponse, SocialReviewDto, UserProfileQueryParams, UserProfileResponse, UserStatsDto,
|
||||||
SocialReviewDto, UserProfileQueryParams, UserProfileResponse, UserStatsDto, UserSummaryDto,
|
UserSummaryDto, UserTrendsDto, UsersResponse, WatchlistEntryDto, WatchlistResponse,
|
||||||
UserTrendsDto, UsersResponse, WatchlistEntryDto, WatchlistResponse, WatchlistStatusResponse,
|
WatchlistStatusResponse,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "federation")]
|
#[cfg(feature = "federation")]
|
||||||
use api_types::{
|
use api_types::{
|
||||||
@@ -573,51 +571,7 @@ pub async fn update_profile_fields_handler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn movie_to_dto(movie: &Movie) -> MovieDto {
|
use crate::mappers::movies::{entry_to_dto, movie_to_dto, review_to_dto, summary_to_dto};
|
||||||
MovieDto {
|
|
||||||
id: movie.id().value(),
|
|
||||||
title: movie.title().value().to_string(),
|
|
||||||
release_year: movie.release_year().value(),
|
|
||||||
director: movie.director().map(|d| d.to_string()),
|
|
||||||
poster_path: movie.poster_path().map(|p| p.value().to_string()),
|
|
||||||
genres: vec![],
|
|
||||||
runtime_minutes: None,
|
|
||||||
original_language: None,
|
|
||||||
overview: None,
|
|
||||||
collection_name: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn summary_to_dto(summary: &MovieSummary) -> MovieDto {
|
|
||||||
MovieDto {
|
|
||||||
id: summary.movie.id().value(),
|
|
||||||
title: summary.movie.title().value().to_string(),
|
|
||||||
release_year: summary.movie.release_year().value(),
|
|
||||||
director: summary.movie.director().map(|d| d.to_string()),
|
|
||||||
poster_path: summary.movie.poster_path().map(|p| p.value().to_string()),
|
|
||||||
genres: summary.genres.clone(),
|
|
||||||
runtime_minutes: summary.runtime_minutes,
|
|
||||||
original_language: summary.original_language.clone(),
|
|
||||||
overview: summary.overview.clone(),
|
|
||||||
collection_name: summary.collection_name.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn review_to_dto(review: &Review) -> ReviewDto {
|
|
||||||
ReviewDto {
|
|
||||||
id: review.id().value(),
|
|
||||||
rating: review.rating().value(),
|
|
||||||
comment: review.comment().map(|c| c.value().to_string()),
|
|
||||||
watched_at: review.watched_at().to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn entry_to_dto(entry: &DiaryEntry) -> DiaryEntryDto {
|
|
||||||
DiaryEntryDto {
|
|
||||||
movie: movie_to_dto(entry.movie()),
|
|
||||||
review: review_to_dto(entry.review()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "federation")]
|
#[cfg(feature = "federation")]
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -1020,12 +974,7 @@ pub async fn get_activity_feed(
|
|||||||
items: page
|
items: page
|
||||||
.items
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| FeedEntryDto {
|
.map(crate::mappers::diary::feed_entry_to_dto)
|
||||||
movie: movie_to_dto(e.movie()),
|
|
||||||
review: review_to_dto(e.review()),
|
|
||||||
user_email: e.user_email().to_string(),
|
|
||||||
user_display_name: e.user_display_name().to_string(),
|
|
||||||
})
|
|
||||||
.collect(),
|
.collect(),
|
||||||
total_count: page.total_count,
|
total_count: page.total_count,
|
||||||
limit: page.limit,
|
limit: page.limit,
|
||||||
|
|||||||
@@ -430,38 +430,13 @@ pub async fn get_users_list(
|
|||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
let users: Vec<UserSummaryView> = result
|
let users: Vec<UserSummaryView> = result
|
||||||
.users
|
.users
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|u| {
|
.map(crate::mappers::users::user_summary_view)
|
||||||
let name = u.email().split('@').next().unwrap_or("?").to_string();
|
|
||||||
let initial = name.chars().next().unwrap_or('?').to_ascii_uppercase();
|
|
||||||
let avg_display = u
|
|
||||||
.avg_rating
|
|
||||||
.map(|r| format!("{:.1}", r))
|
|
||||||
.unwrap_or_else(|| "—".to_string());
|
|
||||||
let avatar_url = u.avatar_path.map(|p| format!("/images/{}", p));
|
|
||||||
UserSummaryView {
|
|
||||||
user_id: u.user_id.value(),
|
|
||||||
display_name: name,
|
|
||||||
initial,
|
|
||||||
avg_rating_display: avg_display,
|
|
||||||
total_movies: u.total_movies,
|
|
||||||
avatar_url,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
let remote_actors: Vec<RemoteActorDisplay> = result
|
let remote_actors: Vec<RemoteActorDisplay> = result
|
||||||
.remote_actors
|
.remote_actors
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|a| {
|
.map(crate::mappers::users::remote_actor_display)
|
||||||
let display = a.display_name.unwrap_or_else(|| a.handle.clone());
|
|
||||||
let initial = display.chars().next().unwrap_or('?').to_ascii_uppercase();
|
|
||||||
RemoteActorDisplay {
|
|
||||||
handle: a.handle,
|
|
||||||
display_name: display,
|
|
||||||
initial,
|
|
||||||
url: a.url,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
render_page(UsersTemplate {
|
render_page(UsersTemplate {
|
||||||
users,
|
users,
|
||||||
@@ -644,13 +619,8 @@ pub async fn get_user_profile(
|
|||||||
let page_items = build_page_items(total_pages, current_page);
|
let page_items = build_page_items(total_pages, current_page);
|
||||||
let pending_followers: Vec<RemoteActorData> = profile
|
let pending_followers: Vec<RemoteActorData> = profile
|
||||||
.pending_followers
|
.pending_followers
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|p| RemoteActorData {
|
.map(crate::mappers::users::pending_follower_data)
|
||||||
handle: p.handle,
|
|
||||||
url: p.url,
|
|
||||||
display_name: p.display_name,
|
|
||||||
avatar_url: p.avatar_url,
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
render_page(ProfileTemplate {
|
render_page(ProfileTemplate {
|
||||||
ctx: &ctx,
|
ctx: &ctx,
|
||||||
@@ -1576,16 +1546,8 @@ pub async fn get_integrations_page(
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let token_views: Vec<template_askama::WebhookTokenView> = tokens
|
let token_views: Vec<template_askama::WebhookTokenView> = tokens
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|t| template_askama::WebhookTokenView {
|
.map(crate::mappers::integrations::webhook_token_view)
|
||||||
id: t.id().value().to_string(),
|
|
||||||
provider: t.provider().to_string(),
|
|
||||||
label: t.label().map(String::from),
|
|
||||||
created_at: t.created_at().format("%Y-%m-%d %H:%M").to_string(),
|
|
||||||
last_used_at: t
|
|
||||||
.last_used_at()
|
|
||||||
.map(|d| d.format("%Y-%m-%d %H:%M").to_string()),
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let webhook_base_url = state.app_ctx.config.base_url.clone();
|
let webhook_base_url = state.app_ctx.config.base_url.clone();
|
||||||
@@ -1676,15 +1638,8 @@ pub async fn get_watch_queue_page(
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let entries: Vec<template_askama::WatchQueueDisplayEntry> = events
|
let entries: Vec<template_askama::WatchQueueDisplayEntry> = events
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|e| template_askama::WatchQueueDisplayEntry {
|
.map(crate::mappers::integrations::watch_queue_entry)
|
||||||
id: e.id().value().to_string(),
|
|
||||||
title: e.title().to_string(),
|
|
||||||
year: e.year(),
|
|
||||||
source: e.source().to_string(),
|
|
||||||
watched_at: e.watched_at().format("%Y-%m-%d %H:%M").to_string(),
|
|
||||||
movie_url: e.movie_id().map(|m| format!("/movies/{}", m.value())),
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
render_page(WatchQueueTemplate {
|
render_page(WatchQueueTemplate {
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ use application::import::{
|
|||||||
};
|
};
|
||||||
use domain::models::{
|
use domain::models::{
|
||||||
AnnotatedRow, FieldMapping, FileFormat,
|
AnnotatedRow, FieldMapping, FileFormat,
|
||||||
import::{DomainField, RowResult, Transform},
|
import::{DomainField, Transform},
|
||||||
};
|
};
|
||||||
use domain::value_objects::ImportSessionId;
|
use domain::value_objects::ImportSessionId;
|
||||||
use template_askama::{
|
use template_askama::{
|
||||||
ImportMappingTemplate, ImportPreviewRow, ImportPreviewTemplate, ImportProfileView,
|
ImportMappingTemplate, ImportPreviewRow, ImportPreviewTemplate, ImportProfileView,
|
||||||
ImportRowStatus, ImportUploadTemplate,
|
ImportUploadTemplate,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -98,34 +98,7 @@ fn parse_mapping_form(form: &HashMap<String, String>) -> Vec<FieldMapping> {
|
|||||||
mappings
|
mappings
|
||||||
}
|
}
|
||||||
|
|
||||||
fn annotated_to_preview_row(idx: usize, annotated: &AnnotatedRow) -> ImportPreviewRow {
|
use crate::mappers::import::annotated_to_preview_row;
|
||||||
match &annotated.result {
|
|
||||||
RowResult::Valid(row) => {
|
|
||||||
let cells = vec![
|
|
||||||
row.title.clone().unwrap_or_default(),
|
|
||||||
row.release_year.clone().unwrap_or_default(),
|
|
||||||
row.director.clone().unwrap_or_default(),
|
|
||||||
row.rating.clone().unwrap_or_default(),
|
|
||||||
row.watched_at.clone().unwrap_or_default(),
|
|
||||||
row.comment.clone().unwrap_or_default(),
|
|
||||||
];
|
|
||||||
ImportPreviewRow {
|
|
||||||
index: idx,
|
|
||||||
status: if annotated.is_duplicate {
|
|
||||||
ImportRowStatus::Duplicate
|
|
||||||
} else {
|
|
||||||
ImportRowStatus::Valid
|
|
||||||
},
|
|
||||||
cells,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RowResult::Invalid { errors, raw } => ImportPreviewRow {
|
|
||||||
index: idx,
|
|
||||||
status: ImportRowStatus::Invalid(errors.join("; ")),
|
|
||||||
cells: raw.iter().map(|(_, v)| v.clone()).collect(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── HTML wizard handlers ───────────────────────────────────────────────────
|
// ── HTML wizard handlers ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ pub mod extractors;
|
|||||||
pub mod factory;
|
pub mod factory;
|
||||||
pub mod forms;
|
pub mod forms;
|
||||||
pub mod handlers;
|
pub mod handlers;
|
||||||
|
pub mod mappers;
|
||||||
pub mod openapi;
|
pub mod openapi;
|
||||||
pub mod ports;
|
pub mod ports;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
|
|||||||
13
crates/presentation/src/mappers/diary.rs
Normal file
13
crates/presentation/src/mappers/diary.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
use api_types::FeedEntryDto;
|
||||||
|
use domain::models::FeedEntry;
|
||||||
|
|
||||||
|
use super::movies::{movie_to_dto, review_to_dto};
|
||||||
|
|
||||||
|
pub fn feed_entry_to_dto(e: &FeedEntry) -> FeedEntryDto {
|
||||||
|
FeedEntryDto {
|
||||||
|
movie: movie_to_dto(e.movie()),
|
||||||
|
review: review_to_dto(e.review()),
|
||||||
|
user_email: e.user_email().to_string(),
|
||||||
|
user_display_name: e.user_display_name().to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
32
crates/presentation/src/mappers/import.rs
Normal file
32
crates/presentation/src/mappers/import.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
use domain::models::AnnotatedRow;
|
||||||
|
use domain::models::import::RowResult;
|
||||||
|
use template_askama::{ImportPreviewRow, ImportRowStatus};
|
||||||
|
|
||||||
|
pub fn annotated_to_preview_row(idx: usize, annotated: &AnnotatedRow) -> ImportPreviewRow {
|
||||||
|
match &annotated.result {
|
||||||
|
RowResult::Valid(row) => {
|
||||||
|
let cells = vec![
|
||||||
|
row.title.clone().unwrap_or_default(),
|
||||||
|
row.release_year.clone().unwrap_or_default(),
|
||||||
|
row.director.clone().unwrap_or_default(),
|
||||||
|
row.rating.clone().unwrap_or_default(),
|
||||||
|
row.watched_at.clone().unwrap_or_default(),
|
||||||
|
row.comment.clone().unwrap_or_default(),
|
||||||
|
];
|
||||||
|
ImportPreviewRow {
|
||||||
|
index: idx,
|
||||||
|
status: if annotated.is_duplicate {
|
||||||
|
ImportRowStatus::Duplicate
|
||||||
|
} else {
|
||||||
|
ImportRowStatus::Valid
|
||||||
|
},
|
||||||
|
cells,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowResult::Invalid { errors, raw } => ImportPreviewRow {
|
||||||
|
index: idx,
|
||||||
|
status: ImportRowStatus::Invalid(errors.join("; ")),
|
||||||
|
cells: raw.iter().map(|(_, v)| v.clone()).collect(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
25
crates/presentation/src/mappers/integrations.rs
Normal file
25
crates/presentation/src/mappers/integrations.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use domain::models::{WatchEvent, WebhookToken};
|
||||||
|
use template_askama::{WatchQueueDisplayEntry, WebhookTokenView};
|
||||||
|
|
||||||
|
pub fn webhook_token_view(t: &WebhookToken) -> WebhookTokenView {
|
||||||
|
WebhookTokenView {
|
||||||
|
id: t.id().value().to_string(),
|
||||||
|
provider: t.provider().to_string(),
|
||||||
|
label: t.label().map(String::from),
|
||||||
|
created_at: t.created_at().format("%Y-%m-%d %H:%M").to_string(),
|
||||||
|
last_used_at: t
|
||||||
|
.last_used_at()
|
||||||
|
.map(|d| d.format("%Y-%m-%d %H:%M").to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn watch_queue_entry(e: &WatchEvent) -> WatchQueueDisplayEntry {
|
||||||
|
WatchQueueDisplayEntry {
|
||||||
|
id: e.id().value().to_string(),
|
||||||
|
title: e.title().to_string(),
|
||||||
|
year: e.year(),
|
||||||
|
source: e.source().to_string(),
|
||||||
|
watched_at: e.watched_at().format("%Y-%m-%d %H:%M").to_string(),
|
||||||
|
movie_url: e.movie_id().map(|m| format!("/movies/{}", m.value())),
|
||||||
|
}
|
||||||
|
}
|
||||||
5
crates/presentation/src/mappers/mod.rs
Normal file
5
crates/presentation/src/mappers/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod diary;
|
||||||
|
pub mod import;
|
||||||
|
pub mod integrations;
|
||||||
|
pub mod movies;
|
||||||
|
pub mod users;
|
||||||
48
crates/presentation/src/mappers/movies.rs
Normal file
48
crates/presentation/src/mappers/movies.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
use api_types::{DiaryEntryDto, MovieDto, ReviewDto};
|
||||||
|
use domain::models::{DiaryEntry, Movie, MovieSummary, Review};
|
||||||
|
|
||||||
|
pub fn movie_to_dto(movie: &Movie) -> MovieDto {
|
||||||
|
MovieDto {
|
||||||
|
id: movie.id().value(),
|
||||||
|
title: movie.title().value().to_string(),
|
||||||
|
release_year: movie.release_year().value(),
|
||||||
|
director: movie.director().map(|d| d.to_string()),
|
||||||
|
poster_path: movie.poster_path().map(|p| p.value().to_string()),
|
||||||
|
genres: vec![],
|
||||||
|
runtime_minutes: None,
|
||||||
|
original_language: None,
|
||||||
|
overview: None,
|
||||||
|
collection_name: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn summary_to_dto(summary: &MovieSummary) -> MovieDto {
|
||||||
|
MovieDto {
|
||||||
|
id: summary.movie.id().value(),
|
||||||
|
title: summary.movie.title().value().to_string(),
|
||||||
|
release_year: summary.movie.release_year().value(),
|
||||||
|
director: summary.movie.director().map(|d| d.to_string()),
|
||||||
|
poster_path: summary.movie.poster_path().map(|p| p.value().to_string()),
|
||||||
|
genres: summary.genres.clone(),
|
||||||
|
runtime_minutes: summary.runtime_minutes,
|
||||||
|
original_language: summary.original_language.clone(),
|
||||||
|
overview: summary.overview.clone(),
|
||||||
|
collection_name: summary.collection_name.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn review_to_dto(review: &Review) -> ReviewDto {
|
||||||
|
ReviewDto {
|
||||||
|
id: review.id().value(),
|
||||||
|
rating: review.rating().value(),
|
||||||
|
comment: review.comment().map(|c| c.value().to_string()),
|
||||||
|
watched_at: review.watched_at().to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entry_to_dto(entry: &DiaryEntry) -> DiaryEntryDto {
|
||||||
|
DiaryEntryDto {
|
||||||
|
movie: movie_to_dto(entry.movie()),
|
||||||
|
review: review_to_dto(entry.review()),
|
||||||
|
}
|
||||||
|
}
|
||||||
42
crates/presentation/src/mappers/users.rs
Normal file
42
crates/presentation/src/mappers/users.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use application::users::get_profile::PendingFollowerView;
|
||||||
|
use domain::models::UserSummary;
|
||||||
|
use domain::ports::RemoteActorInfo;
|
||||||
|
use template_askama::{RemoteActorData, RemoteActorDisplay, UserSummaryView};
|
||||||
|
|
||||||
|
pub fn user_summary_view(u: &UserSummary) -> UserSummaryView {
|
||||||
|
let name = u.email().split('@').next().unwrap_or("?").to_string();
|
||||||
|
let initial = name.chars().next().unwrap_or('?').to_ascii_uppercase();
|
||||||
|
let avg_display = u
|
||||||
|
.avg_rating
|
||||||
|
.map(|r| format!("{:.1}", r))
|
||||||
|
.unwrap_or_else(|| "\u{2014}".to_string());
|
||||||
|
let avatar_url = u.avatar_path.as_ref().map(|p| format!("/images/{}", p));
|
||||||
|
UserSummaryView {
|
||||||
|
user_id: u.user_id.value(),
|
||||||
|
display_name: name,
|
||||||
|
initial,
|
||||||
|
avg_rating_display: avg_display,
|
||||||
|
total_movies: u.total_movies,
|
||||||
|
avatar_url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remote_actor_display(a: &RemoteActorInfo) -> RemoteActorDisplay {
|
||||||
|
let display = a.display_name.clone().unwrap_or_else(|| a.handle.clone());
|
||||||
|
let initial = display.chars().next().unwrap_or('?').to_ascii_uppercase();
|
||||||
|
RemoteActorDisplay {
|
||||||
|
handle: a.handle.clone(),
|
||||||
|
display_name: display,
|
||||||
|
initial,
|
||||||
|
url: a.url.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pending_follower_data(p: &PendingFollowerView) -> RemoteActorData {
|
||||||
|
RemoteActorData {
|
||||||
|
handle: p.handle.clone(),
|
||||||
|
url: p.url.clone(),
|
||||||
|
display_name: p.display_name.clone(),
|
||||||
|
avatar_url: p.avatar_url.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user