Refactor: Remove unused structs, redundant services, and streamline repository models
This commit is contained in:
@@ -561,18 +561,18 @@ function ScribeContent() {
|
|||||||
try {
|
try {
|
||||||
await tauri.syncUser({
|
await tauri.syncUser({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
firstName: user.name,
|
|
||||||
lastName: user.lastName,
|
|
||||||
username: user.username,
|
username: user.username,
|
||||||
email: user.email
|
email: user.email
|
||||||
});
|
});
|
||||||
} catch (syncError) {
|
} catch (syncError) {
|
||||||
|
console.error('[Page] syncUser failed:', syncError);
|
||||||
errorMessage(t("homePage.errors.syncError"));
|
errorMessage(t("homePage.errors.syncError"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
errorMessage(t("homePage.errors.dbInitError"));
|
errorMessage(t("homePage.errors.dbInitError"));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('[Page] DB init or sync failed:', error);
|
||||||
errorMessage(t("homePage.errors.syncError"));
|
errorMessage(t("homePage.errors.syncError"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,25 +317,15 @@ pub fn initialize_schema(conn: &Connection) -> Result<(), rusqlite::Error> {
|
|||||||
-- Erit Users
|
-- Erit Users
|
||||||
CREATE TABLE IF NOT EXISTS erit_users (
|
CREATE TABLE IF NOT EXISTS erit_users (
|
||||||
user_id TEXT PRIMARY KEY,
|
user_id TEXT PRIMARY KEY,
|
||||||
first_name TEXT NOT NULL,
|
|
||||||
last_name TEXT NOT NULL,
|
|
||||||
username TEXT NOT NULL,
|
username TEXT NOT NULL,
|
||||||
email TEXT NOT NULL,
|
email TEXT NOT NULL,
|
||||||
origin_email TEXT NOT NULL,
|
origin_email TEXT NOT NULL,
|
||||||
origin_username TEXT NOT NULL,
|
origin_username TEXT NOT NULL,
|
||||||
author_name TEXT,
|
author_name TEXT,
|
||||||
origin_author_name TEXT,
|
origin_author_name TEXT,
|
||||||
plateform TEXT NOT NULL,
|
|
||||||
social_id TEXT,
|
social_id TEXT,
|
||||||
user_group INTEGER NOT NULL DEFAULT 4,
|
user_group INTEGER NOT NULL DEFAULT 4,
|
||||||
password TEXT,
|
password TEXT
|
||||||
term_accepted INTEGER NOT NULL DEFAULT 0,
|
|
||||||
verify_code TEXT,
|
|
||||||
reg_date INTEGER NOT NULL,
|
|
||||||
account_verified INTEGER NOT NULL DEFAULT 0,
|
|
||||||
erite_points INTEGER NOT NULL DEFAULT 100,
|
|
||||||
stripe_customer_id TEXT,
|
|
||||||
credits_balance REAL DEFAULT 0
|
|
||||||
);
|
);
|
||||||
|
|
||||||
-- =========================================================================
|
-- =========================================================================
|
||||||
|
|||||||
@@ -24,15 +24,6 @@ pub struct ActProps {
|
|||||||
pub chapters: Option<Vec<ActChapter>>,
|
pub chapters: Option<Vec<ActChapter>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ActStory {
|
|
||||||
pub act_id: i64,
|
|
||||||
pub summary: String,
|
|
||||||
pub chapter_summary: String,
|
|
||||||
pub chapter_goal: String,
|
|
||||||
pub incidents: Vec<IncidentStory>,
|
|
||||||
pub plot_points: Vec<PlotPointStory>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ActChapter {
|
pub struct ActChapter {
|
||||||
@@ -47,13 +38,8 @@ pub struct ActChapter {
|
|||||||
pub goal: String,
|
pub goal: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SyncedActSummary {
|
pub use incident_service::IncidentProps;
|
||||||
pub id: String,
|
pub use plotpoint_service::PlotPointProps;
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use incident_service::{IncidentProps, IncidentStory};
|
|
||||||
pub use plotpoint_service::{PlotPointProps, PlotPointStory};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|||||||
@@ -444,7 +444,7 @@ pub fn export_book(data: ExportBookData, db: State<DbManager>, session: State<Se
|
|||||||
let conn = db_manager.get_connection(&user_id)?;
|
let conn = db_manager.get_connection(&user_id)?;
|
||||||
let book_data = chapter_service::get_complete_book_data_with_selections(conn, &user_id, &data.book_id, data.selections.as_deref(), lang)?;
|
let book_data = chapter_service::get_complete_book_data_with_selections(conn, &user_id, &data.book_id, data.selections.as_deref(), lang)?;
|
||||||
match data.format.as_str() {
|
match data.format.as_str() {
|
||||||
"epub" => { let result = export_service::transform_to_epub(&book_data)?; Ok(result.buffer) },
|
"epub" => { let result = export_service::transform_to_epub(&book_data, lang)?; Ok(result.buffer) },
|
||||||
"pdf" => { let result = export_service::transform_to_pdf(&book_data)?; Ok(result.buffer) },
|
"pdf" => { let result = export_service::transform_to_pdf(&book_data)?; Ok(result.buffer) },
|
||||||
"docx" => { let result = export_service::transform_to_docx(&book_data)?; Ok(result.buffer) },
|
"docx" => { let result = export_service::transform_to_docx(&book_data)?; Ok(result.buffer) },
|
||||||
_ => Err(AppError::Validation(if lang == crate::shared::types::Lang::Fr { "Format non supporté.".to_string() } else { "Unsupported format.".to_string() })),
|
_ => Err(AppError::Validation(if lang == crate::shared::types::Lang::Fr { "Format non supporté.".to_string() } else { "Unsupported format.".to_string() })),
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ pub struct BookQuery {
|
|||||||
pub book_type: String,
|
pub book_type: String,
|
||||||
pub author_id: String,
|
pub author_id: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub hashed_title: String,
|
|
||||||
pub sub_title: Option<String>,
|
pub sub_title: Option<String>,
|
||||||
pub hashed_sub_title: Option<String>,
|
|
||||||
pub summary: Option<String>,
|
pub summary: Option<String>,
|
||||||
pub serie_id: Option<i64>,
|
pub serie_id: Option<i64>,
|
||||||
pub desired_release_date: Option<String>,
|
pub desired_release_date: Option<String>,
|
||||||
@@ -86,8 +84,7 @@ pub fn fetch_books(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Ve
|
|||||||
sub_title: query_row.get(4)?, summary: query_row.get(5)?,
|
sub_title: query_row.get(4)?, summary: query_row.get(5)?,
|
||||||
serie_id: query_row.get(6)?, desired_release_date: query_row.get(7)?,
|
serie_id: query_row.get(6)?, desired_release_date: query_row.get(7)?,
|
||||||
desired_word_count: query_row.get(8)?, words_count: query_row.get(9)?,
|
desired_word_count: query_row.get(8)?, words_count: query_row.get(9)?,
|
||||||
cover_image: query_row.get(10)?, hashed_title: String::new(),
|
cover_image: query_row.get(10)?,
|
||||||
hashed_sub_title: None,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer la liste des livres.".to_string() } else { "Unable to retrieve book list.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer la liste des livres.".to_string() } else { "Unable to retrieve book list.".to_string() }))?
|
||||||
@@ -125,8 +122,7 @@ pub fn fetch_book(conn: &Connection, book_id: &str, user_id: &str, lang: Lang) -
|
|||||||
sub_title: query_row.get(4)?, cover_image: query_row.get(5)?,
|
sub_title: query_row.get(4)?, cover_image: query_row.get(5)?,
|
||||||
desired_release_date: query_row.get(6)?, desired_word_count: query_row.get(7)?,
|
desired_release_date: query_row.get(6)?, desired_word_count: query_row.get(7)?,
|
||||||
words_count: query_row.get(8)?, serie_id: query_row.get(9)?,
|
words_count: query_row.get(8)?, serie_id: query_row.get(9)?,
|
||||||
book_type: String::new(), hashed_title: String::new(),
|
book_type: String::new(),
|
||||||
hashed_sub_title: None,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|error| match error {
|
.map_err(|error| match error {
|
||||||
|
|||||||
@@ -67,34 +67,10 @@ pub struct CompleteBookData {
|
|||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct BookUserInfos {
|
pub struct BookUserInfos {
|
||||||
pub first_name: String,
|
pub username: String,
|
||||||
pub last_name: String,
|
|
||||||
pub author_name: String,
|
pub author_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct SyncedBook {
|
|
||||||
pub id: String,
|
|
||||||
pub book_type: String,
|
|
||||||
pub title: String,
|
|
||||||
pub sub_title: Option<String>,
|
|
||||||
pub last_update: i64,
|
|
||||||
pub chapters: Vec<SyncedChapter>,
|
|
||||||
pub characters: Vec<SyncedCharacter>,
|
|
||||||
pub locations: Vec<SyncedLocation>,
|
|
||||||
pub worlds: Vec<SyncedWorld>,
|
|
||||||
pub incidents: Vec<SyncedIncident>,
|
|
||||||
pub plot_points: Vec<SyncedPlotPoint>,
|
|
||||||
pub issues: Vec<SyncedIssue>,
|
|
||||||
pub act_summaries: Vec<SyncedActSummary>,
|
|
||||||
pub guide_line: Option<SyncedGuideLine>,
|
|
||||||
pub ai_guide_line: Option<SyncedAIGuideLine>,
|
|
||||||
pub book_tools: Option<SyncedBookTools>,
|
|
||||||
pub spells: Vec<SyncedSpell>,
|
|
||||||
pub spell_tags: Vec<SyncedSpellTag>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct BookSyncCompare {
|
pub struct BookSyncCompare {
|
||||||
@@ -115,43 +91,12 @@ pub struct BookSyncCompare {
|
|||||||
pub act_summaries: Vec<String>,
|
pub act_summaries: Vec<String>,
|
||||||
pub guide_line: bool,
|
pub guide_line: bool,
|
||||||
pub ai_guide_line: bool,
|
pub ai_guide_line: bool,
|
||||||
pub book_tools: bool,
|
#[serde(rename = "bookTools")]
|
||||||
|
pub _book_tools: bool,
|
||||||
pub spells: Vec<String>,
|
pub spells: Vec<String>,
|
||||||
pub spell_tags: Vec<String>,
|
pub spell_tags: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct SyncedChapter {
|
|
||||||
pub id: String,
|
|
||||||
pub title: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct SyncedCharacter {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct SyncedLocation {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct SyncedWorld {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SyncedIncident {
|
pub struct SyncedIncident {
|
||||||
@@ -966,8 +911,7 @@ pub fn complete_book_data(conn: &Connection, user_id: &str, book_id: &str, lang:
|
|||||||
summary: if let Some(ref summary) = book_data.summary { decrypt_data_with_user_key(summary, &user_key)? } else { String::new() },
|
summary: if let Some(ref summary) = book_data.summary { decrypt_data_with_user_key(summary, &user_key)? } else { String::new() },
|
||||||
cover_image,
|
cover_image,
|
||||||
user_infos: BookUserInfos {
|
user_infos: BookUserInfos {
|
||||||
first_name: if let Some(ref first_name) = user_infos.first_name { decrypt_data_with_user_key(first_name, &user_key)? } else { String::new() },
|
username: decrypt_data_with_user_key(&user_infos.username, &user_key)?,
|
||||||
last_name: if let Some(ref last_name) = user_infos.last_name { decrypt_data_with_user_key(last_name, &user_key)? } else { String::new() },
|
|
||||||
author_name: if let Some(ref author_name) = user_infos.author_name { decrypt_data_with_user_key(author_name, &user_key)? } else { String::new() },
|
author_name: if let Some(ref author_name) = user_infos.author_name { decrypt_data_with_user_key(author_name, &user_key)? } else { String::new() },
|
||||||
},
|
},
|
||||||
chapters: decrypted_chapters,
|
chapters: decrypted_chapters,
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ pub struct SaveChapterContentData {
|
|||||||
pub version: i64,
|
pub version: i64,
|
||||||
pub content: Value,
|
pub content: Value,
|
||||||
pub total_word_count: i64,
|
pub total_word_count: i64,
|
||||||
pub content_id: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ pub struct ActChapterQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChapterStoryQueryResult {
|
pub struct ChapterStoryQueryResult {
|
||||||
pub chapter_info_id: i64,
|
pub _chapter_info_id: i64,
|
||||||
pub act_id: i64,
|
pub act_id: i64,
|
||||||
pub summary: String,
|
pub summary: String,
|
||||||
pub chapter_summary: String,
|
pub chapter_summary: String,
|
||||||
@@ -47,18 +47,18 @@ pub struct BookChaptersTable {
|
|||||||
pub author_id: String,
|
pub author_id: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub hashed_title: String,
|
pub hashed_title: String,
|
||||||
pub words_count: Option<i64>,
|
pub _words_count: Option<i64>,
|
||||||
pub chapter_order: i64,
|
pub chapter_order: i64,
|
||||||
pub last_update: i64,
|
pub last_update: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BookChapterInfosTable {
|
pub struct BookChapterInfosTable {
|
||||||
pub chapter_info_id: String,
|
pub _chapter_info_id: String,
|
||||||
pub chapter_id: String,
|
pub chapter_id: String,
|
||||||
pub act_id: Option<i64>,
|
pub _act_id: Option<i64>,
|
||||||
pub incident_id: Option<String>,
|
pub _incident_id: Option<String>,
|
||||||
pub plot_point_id: Option<String>,
|
pub _plot_point_id: Option<String>,
|
||||||
pub book_id: String,
|
pub _book_id: String,
|
||||||
pub author_id: String,
|
pub author_id: String,
|
||||||
pub summary: Option<String>,
|
pub summary: Option<String>,
|
||||||
pub goal: Option<String>,
|
pub goal: Option<String>,
|
||||||
@@ -75,7 +75,7 @@ pub struct SyncedChapterResult {
|
|||||||
pub struct SyncedChapterInfoResult {
|
pub struct SyncedChapterInfoResult {
|
||||||
pub chapter_info_id: String,
|
pub chapter_info_id: String,
|
||||||
pub chapter_id: Option<String>,
|
pub chapter_id: Option<String>,
|
||||||
pub book_id: String,
|
pub _book_id: String,
|
||||||
pub last_update: i64,
|
pub last_update: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ pub struct SelectedChapterContentResult {
|
|||||||
pub title: String,
|
pub title: String,
|
||||||
pub chapter_order: i64,
|
pub chapter_order: i64,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub version: i64,
|
pub _version: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
@@ -307,7 +307,7 @@ pub fn fetch_chapter_story(conn: &Connection, user_id: &str, chapter_id: &str, l
|
|||||||
let rows = statement
|
let rows = statement
|
||||||
.query_map(params![chapter_id, user_id], |query_row| {
|
.query_map(params![chapter_id, user_id], |query_row| {
|
||||||
Ok(ChapterStoryQueryResult {
|
Ok(ChapterStoryQueryResult {
|
||||||
chapter_info_id: query_row.get(0)?, act_id: query_row.get(1)?,
|
_chapter_info_id: query_row.get(0)?, act_id: query_row.get(1)?,
|
||||||
summary: query_row.get::<_, Option<String>>(2)?.unwrap_or_default(),
|
summary: query_row.get::<_, Option<String>>(2)?.unwrap_or_default(),
|
||||||
chapter_summary: query_row.get::<_, Option<String>>(3)?.unwrap_or_default(),
|
chapter_summary: query_row.get::<_, Option<String>>(3)?.unwrap_or_default(),
|
||||||
chapter_goal: query_row.get::<_, Option<String>>(4)?.unwrap_or_default(),
|
chapter_goal: query_row.get::<_, Option<String>>(4)?.unwrap_or_default(),
|
||||||
@@ -393,7 +393,7 @@ pub fn fetch_book_chapters(conn: &Connection, user_id: &str, book_id: &str, lang
|
|||||||
Ok(BookChaptersTable {
|
Ok(BookChaptersTable {
|
||||||
chapter_id: query_row.get(0)?, book_id: query_row.get(1)?,
|
chapter_id: query_row.get(0)?, book_id: query_row.get(1)?,
|
||||||
author_id: query_row.get(2)?, title: query_row.get(3)?,
|
author_id: query_row.get(2)?, title: query_row.get(3)?,
|
||||||
hashed_title: query_row.get(4)?, words_count: query_row.get(5)?,
|
hashed_title: query_row.get(4)?, _words_count: query_row.get(5)?,
|
||||||
chapter_order: query_row.get(6)?, last_update: query_row.get(7)?,
|
chapter_order: query_row.get(6)?, last_update: query_row.get(7)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -413,9 +413,9 @@ pub fn fetch_book_chapter_infos(conn: &Connection, user_id: &str, chapter_id: &s
|
|||||||
let rows = statement
|
let rows = statement
|
||||||
.query_map(params![user_id, chapter_id], |query_row| {
|
.query_map(params![user_id, chapter_id], |query_row| {
|
||||||
Ok(BookChapterInfosTable {
|
Ok(BookChapterInfosTable {
|
||||||
chapter_info_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
|
_chapter_info_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
|
||||||
act_id: query_row.get(2)?, incident_id: query_row.get(3)?,
|
_act_id: query_row.get(2)?, _incident_id: query_row.get(3)?,
|
||||||
plot_point_id: query_row.get(4)?, book_id: query_row.get(5)?,
|
_plot_point_id: query_row.get(4)?, _book_id: query_row.get(5)?,
|
||||||
author_id: query_row.get(6)?, summary: query_row.get(7)?,
|
author_id: query_row.get(6)?, summary: query_row.get(7)?,
|
||||||
goal: query_row.get(8)?, last_update: query_row.get(9)?,
|
goal: query_row.get(8)?, last_update: query_row.get(9)?,
|
||||||
})
|
})
|
||||||
@@ -457,7 +457,7 @@ pub fn fetch_synced_chapter_infos(conn: &Connection, user_id: &str, lang: Lang)
|
|||||||
.query_map(params![user_id], |query_row| {
|
.query_map(params![user_id], |query_row| {
|
||||||
Ok(SyncedChapterInfoResult {
|
Ok(SyncedChapterInfoResult {
|
||||||
chapter_info_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
|
chapter_info_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
|
||||||
book_id: query_row.get(2)?, last_update: query_row.get(3)?,
|
_book_id: query_row.get(2)?, last_update: query_row.get(3)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les infos des chapitres synchronisés.".to_string() } else { "Unable to retrieve synced chapter infos.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les infos des chapitres synchronisés.".to_string() } else { "Unable to retrieve synced chapter infos.".to_string() }))?
|
||||||
@@ -500,7 +500,7 @@ pub fn fetch_complete_chapter_by_id(conn: &Connection, chapter_id: &str, lang: L
|
|||||||
Ok(BookChaptersTable {
|
Ok(BookChaptersTable {
|
||||||
chapter_id: query_row.get(0)?, book_id: query_row.get(1)?,
|
chapter_id: query_row.get(0)?, book_id: query_row.get(1)?,
|
||||||
author_id: query_row.get(2)?, title: query_row.get(3)?,
|
author_id: query_row.get(2)?, title: query_row.get(3)?,
|
||||||
hashed_title: query_row.get(4)?, words_count: query_row.get(5)?,
|
hashed_title: query_row.get(4)?, _words_count: query_row.get(5)?,
|
||||||
chapter_order: query_row.get(6)?, last_update: query_row.get(7)?,
|
chapter_order: query_row.get(6)?, last_update: query_row.get(7)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -520,9 +520,9 @@ pub fn fetch_complete_chapter_info_by_id(conn: &Connection, chapter_info_id: &st
|
|||||||
let rows = statement
|
let rows = statement
|
||||||
.query_map(params![chapter_info_id], |query_row| {
|
.query_map(params![chapter_info_id], |query_row| {
|
||||||
Ok(BookChapterInfosTable {
|
Ok(BookChapterInfosTable {
|
||||||
chapter_info_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
|
_chapter_info_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
|
||||||
act_id: query_row.get(2)?, incident_id: query_row.get(3)?,
|
_act_id: query_row.get(2)?, _incident_id: query_row.get(3)?,
|
||||||
plot_point_id: query_row.get(4)?, book_id: query_row.get(5)?,
|
_plot_point_id: query_row.get(4)?, _book_id: query_row.get(5)?,
|
||||||
author_id: query_row.get(6)?, summary: query_row.get(7)?,
|
author_id: query_row.get(6)?, summary: query_row.get(7)?,
|
||||||
goal: query_row.get(8)?, last_update: query_row.get(9)?,
|
goal: query_row.get(8)?, last_update: query_row.get(9)?,
|
||||||
})
|
})
|
||||||
@@ -581,7 +581,7 @@ pub fn fetch_selected_chapters_content(conn: &Connection, book_id: &str, selecti
|
|||||||
Ok(SelectedChapterContentResult {
|
Ok(SelectedChapterContentResult {
|
||||||
chapter_id: query_row.get(0)?, title: query_row.get(1)?,
|
chapter_id: query_row.get(0)?, title: query_row.get(1)?,
|
||||||
chapter_order: query_row.get(2)?, content: query_row.get(3)?,
|
chapter_order: query_row.get(2)?, content: query_row.get(3)?,
|
||||||
version: query_row.get(4)?,
|
_version: query_row.get(4)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu des chapitres sélectionnés.".to_string() } else { "Unable to retrieve selected chapters content.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu des chapitres sélectionnés.".to_string() } else { "Unable to retrieve selected chapters content.".to_string() }))?
|
||||||
|
|||||||
@@ -24,10 +24,7 @@ pub struct ChapterContent {
|
|||||||
|
|
||||||
pub struct ChapterContentData {
|
pub struct ChapterContentData {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub chapter_order: i64,
|
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub words_count: i64,
|
|
||||||
pub version: i64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
@@ -47,24 +44,6 @@ pub struct CompanionContent {
|
|||||||
pub words_count: i64,
|
pub words_count: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SyncedChapter {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
pub contents: Vec<SyncedChapterContent>,
|
|
||||||
pub info: Option<SyncedChapterInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SyncedChapterContent {
|
|
||||||
pub id: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SyncedChapterInfo {
|
|
||||||
pub id: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CompleteChapterContent {
|
pub struct CompleteChapterContent {
|
||||||
@@ -601,10 +580,7 @@ pub fn get_chapters_or_sheet(book_chapters: &[CompleteChapterContent]) -> Vec<Ch
|
|||||||
let parsed_content: Value = serde_json::from_str(&sheet.content).unwrap_or(Value::Null);
|
let parsed_content: Value = serde_json::from_str(&sheet.content).unwrap_or(Value::Null);
|
||||||
processed_chapters.push(ChapterContentData {
|
processed_chapters.push(ChapterContentData {
|
||||||
title: sheet.title.clone(),
|
title: sheet.title.clone(),
|
||||||
chapter_order: sheet.order,
|
|
||||||
content: html_to_text(&tip_tap_to_html(&parsed_content)),
|
content: html_to_text(&tip_tap_to_html(&parsed_content)),
|
||||||
words_count: 0,
|
|
||||||
version: sheet.version.unwrap_or(0),
|
|
||||||
});
|
});
|
||||||
} else if regular_chapter.is_some() {
|
} else if regular_chapter.is_some() {
|
||||||
for chapter_data in book_chapters {
|
for chapter_data in book_chapters {
|
||||||
@@ -612,10 +588,7 @@ pub fn get_chapters_or_sheet(book_chapters: &[CompleteChapterContent]) -> Vec<Ch
|
|||||||
let parsed_content: Value = serde_json::from_str(&chapter_data.content).unwrap_or(Value::Null);
|
let parsed_content: Value = serde_json::from_str(&chapter_data.content).unwrap_or(Value::Null);
|
||||||
processed_chapters.push(ChapterContentData {
|
processed_chapters.push(ChapterContentData {
|
||||||
title: chapter_data.title.clone(),
|
title: chapter_data.title.clone(),
|
||||||
chapter_order: chapter_data.order,
|
|
||||||
content: html_to_text(&tip_tap_to_html(&parsed_content)),
|
content: html_to_text(&tip_tap_to_html(&parsed_content)),
|
||||||
words_count: 0,
|
|
||||||
version: chapter_data.version.unwrap_or(0),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ pub struct BookChapterContentTable {
|
|||||||
pub author_id: String,
|
pub author_id: String,
|
||||||
pub version: i64,
|
pub version: i64,
|
||||||
pub content: Option<String>,
|
pub content: Option<String>,
|
||||||
pub words_count: i64,
|
pub _words_count: i64,
|
||||||
pub time_on_it: i64,
|
pub _time_on_it: i64,
|
||||||
pub last_update: i64,
|
pub last_update: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,8 +188,8 @@ pub fn fetch_book_chapter_contents(conn: &Connection, user_id: &str, chapter_id:
|
|||||||
Ok(BookChapterContentTable {
|
Ok(BookChapterContentTable {
|
||||||
content_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
|
content_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
|
||||||
author_id: query_row.get(2)?, version: query_row.get(3)?,
|
author_id: query_row.get(2)?, version: query_row.get(3)?,
|
||||||
content: query_row.get(4)?, words_count: query_row.get(5)?,
|
content: query_row.get(4)?, _words_count: query_row.get(5)?,
|
||||||
time_on_it: query_row.get(6)?, last_update: query_row.get(7)?,
|
_time_on_it: query_row.get(6)?, last_update: query_row.get(7)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu des chapitres.".to_string() } else { "Unable to retrieve chapter contents.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu des chapitres.".to_string() } else { "Unable to retrieve chapter contents.".to_string() }))?
|
||||||
@@ -261,8 +261,8 @@ pub fn fetch_complete_chapter_content_by_id(conn: &Connection, content_id: &str,
|
|||||||
Ok(BookChapterContentTable {
|
Ok(BookChapterContentTable {
|
||||||
content_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
|
content_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
|
||||||
author_id: query_row.get(2)?, version: query_row.get(3)?,
|
author_id: query_row.get(2)?, version: query_row.get(3)?,
|
||||||
content: query_row.get(4)?, words_count: query_row.get(5)?,
|
content: query_row.get(4)?, _words_count: query_row.get(5)?,
|
||||||
time_on_it: query_row.get(6)?, last_update: query_row.get(7)?,
|
_time_on_it: query_row.get(6)?, last_update: query_row.get(7)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu de chapitre complet.".to_string() } else { "Unable to retrieve complete chapter content.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu de chapitre complet.".to_string() } else { "Unable to retrieve complete chapter content.".to_string() }))?
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ fn get_session(session: &State<SessionState>) -> Result<(String, Lang), AppError
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GetCharacterListData {
|
pub struct GetCharacterListData {
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
pub enabled: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -106,19 +106,6 @@ pub struct CharacterAttribute {
|
|||||||
pub values: Vec<Attribute>,
|
pub values: Vec<Attribute>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SyncedCharacter {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
pub attributes: Vec<SyncedCharacterAttribute>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SyncedCharacterAttribute {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves a list of all characters for a specific book.
|
/// Retrieves a list of all characters for a specific book.
|
||||||
/// Decrypts character data using the user's encryption key.
|
/// Decrypts character data using the user's encryption key.
|
||||||
/// * `conn` - Database connection
|
/// * `conn` - Database connection
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use serde_json::Value;
|
|||||||
use crate::domains::book::service::{CompleteBookData, CompleteChapterContent};
|
use crate::domains::book::service::{CompleteBookData, CompleteChapterContent};
|
||||||
use crate::domains::chapter::service::{get_chapters_or_sheet, tip_tap_to_html, ChapterContentData};
|
use crate::domains::chapter::service::{get_chapters_or_sheet, tip_tap_to_html, ChapterContentData};
|
||||||
use crate::error::{AppError, AppResult};
|
use crate::error::{AppError, AppResult};
|
||||||
|
use crate::shared::types::Lang;
|
||||||
|
|
||||||
pub const MAIN_STYLE: &str = r#"h1 {
|
pub const MAIN_STYLE: &str = r#"h1 {
|
||||||
font-size: 24px !important;
|
font-size: 24px !important;
|
||||||
@@ -25,7 +26,6 @@ p {
|
|||||||
|
|
||||||
pub struct ExportResult {
|
pub struct ExportResult {
|
||||||
pub buffer: Vec<u8>,
|
pub buffer: Vec<u8>,
|
||||||
pub file_name: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transforms book data into a DOCX document.
|
/// Transforms book data into a DOCX document.
|
||||||
@@ -33,7 +33,6 @@ pub struct ExportResult {
|
|||||||
/// Returns the DOCX buffer and filename.
|
/// Returns the DOCX buffer and filename.
|
||||||
pub fn transform_to_docx(book_data: &CompleteBookData) -> AppResult<ExportResult> {
|
pub fn transform_to_docx(book_data: &CompleteBookData) -> AppResult<ExportResult> {
|
||||||
let book_title: &str = &book_data.title;
|
let book_title: &str = &book_data.title;
|
||||||
let filename: String = format!("{}.docx", book_title);
|
|
||||||
|
|
||||||
let mut docx: Docx = Docx::new();
|
let mut docx: Docx = Docx::new();
|
||||||
|
|
||||||
@@ -92,7 +91,7 @@ pub fn transform_to_docx(book_data: &CompleteBookData) -> AppResult<ExportResult
|
|||||||
.pack(&mut Cursor::new(&mut buffer))
|
.pack(&mut Cursor::new(&mut buffer))
|
||||||
.map_err(|error| AppError::Internal(format!("DOCX generation failed: {}", error)))?;
|
.map_err(|error| AppError::Internal(format!("DOCX generation failed: {}", error)))?;
|
||||||
|
|
||||||
Ok(ExportResult { buffer, file_name: filename })
|
Ok(ExportResult { buffer })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transforms book data into a PDF document.
|
/// Transforms book data into a PDF document.
|
||||||
@@ -100,7 +99,6 @@ pub fn transform_to_docx(book_data: &CompleteBookData) -> AppResult<ExportResult
|
|||||||
/// Returns the PDF buffer and filename.
|
/// Returns the PDF buffer and filename.
|
||||||
pub fn transform_to_pdf(book_data: &CompleteBookData) -> AppResult<ExportResult> {
|
pub fn transform_to_pdf(book_data: &CompleteBookData) -> AppResult<ExportResult> {
|
||||||
let book_title: &str = &book_data.title;
|
let book_title: &str = &book_data.title;
|
||||||
let filename: String = format!("{}.pdf", book_title);
|
|
||||||
|
|
||||||
let (pdf_document, page_index, layer_index) = PdfDocument::new(book_title, Mm(210.0), Mm(297.0), "Title Page");
|
let (pdf_document, page_index, layer_index) = PdfDocument::new(book_title, Mm(210.0), Mm(297.0), "Title Page");
|
||||||
let font = pdf_document
|
let font = pdf_document
|
||||||
@@ -150,13 +148,13 @@ pub fn transform_to_pdf(book_data: &CompleteBookData) -> AppResult<ExportResult>
|
|||||||
.save_to_bytes()
|
.save_to_bytes()
|
||||||
.map_err(|error| AppError::Internal(format!("PDF generation failed: {}", error)))?;
|
.map_err(|error| AppError::Internal(format!("PDF generation failed: {}", error)))?;
|
||||||
|
|
||||||
Ok(ExportResult { buffer, file_name: filename })
|
Ok(ExportResult { buffer })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transforms book data into an EPUB document.
|
/// Transforms book data into an EPUB document.
|
||||||
/// * `book_data` - The complete book data to export
|
/// * `book_data` - The complete book data to export
|
||||||
/// Returns the EPUB buffer and filename.
|
/// Returns the EPUB buffer and filename.
|
||||||
pub fn transform_to_epub(book_data: &CompleteBookData) -> AppResult<ExportResult> {
|
pub fn transform_to_epub(book_data: &CompleteBookData, lang: Lang) -> AppResult<ExportResult> {
|
||||||
let book_title: &str = &book_data.title;
|
let book_title: &str = &book_data.title;
|
||||||
let book_id: &str = &book_data.book_id;
|
let book_id: &str = &book_data.book_id;
|
||||||
|
|
||||||
@@ -179,8 +177,11 @@ pub fn transform_to_epub(book_data: &CompleteBookData) -> AppResult<ExportResult
|
|||||||
epub_builder
|
epub_builder
|
||||||
.metadata("identifier", &format!("urn:uuid:{}", book_id))
|
.metadata("identifier", &format!("urn:uuid:{}", book_id))
|
||||||
.map_err(|error| AppError::Internal(format!("EPUB metadata error: {}", error)))?;
|
.map_err(|error| AppError::Internal(format!("EPUB metadata error: {}", error)))?;
|
||||||
|
if book_data.user_infos.author_name.is_empty() {
|
||||||
|
return Err(AppError::Validation(if lang == Lang::Fr { "Veuillez définir votre nom d'auteur avant d'exporter.".to_string() } else { "Please set your author name before exporting.".to_string() }));
|
||||||
|
}
|
||||||
epub_builder
|
epub_builder
|
||||||
.metadata("author", &format!("{} {}", &book_data.user_infos.first_name, &book_data.user_infos.last_name))
|
.metadata("author", &book_data.user_infos.author_name)
|
||||||
.map_err(|error| AppError::Internal(format!("EPUB metadata error: {}", error)))?;
|
.map_err(|error| AppError::Internal(format!("EPUB metadata error: {}", error)))?;
|
||||||
epub_builder
|
epub_builder
|
||||||
.metadata("publisher", "ERitors Scribe")
|
.metadata("publisher", "ERitors Scribe")
|
||||||
@@ -247,5 +248,5 @@ pub fn transform_to_epub(book_data: &CompleteBookData) -> AppResult<ExportResult
|
|||||||
.generate(&mut epub_buffer)
|
.generate(&mut epub_buffer)
|
||||||
.map_err(|error| AppError::Internal(format!("EPUB generation failed: {}", error)))?;
|
.map_err(|error| AppError::Internal(format!("EPUB generation failed: {}", error)))?;
|
||||||
|
|
||||||
Ok(ExportResult { buffer: epub_buffer, file_name: format!("{}.epub", book_title) })
|
Ok(ExportResult { buffer: epub_buffer })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,18 +58,18 @@ pub struct GuideLineQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct GuideLineAIQuery {
|
pub struct GuideLineAIQuery {
|
||||||
pub user_id: String,
|
pub _user_id: String,
|
||||||
pub book_id: String,
|
pub _book_id: String,
|
||||||
pub global_resume: Option<String>,
|
pub global_resume: Option<String>,
|
||||||
pub themes: Option<String>,
|
pub themes: Option<String>,
|
||||||
pub verbe_tense: Option<i64>,
|
pub verbe_tense: Option<i64>,
|
||||||
pub narrative_type: Option<i64>,
|
pub narrative_type: Option<i64>,
|
||||||
pub langue: Option<i64>,
|
pub langue: Option<i64>,
|
||||||
pub dialogue_type: Option<i64>,
|
pub dialogue_type: Option<i64>,
|
||||||
pub tone: Option<String>,
|
pub _tone: Option<String>,
|
||||||
pub atmosphere: Option<String>,
|
pub atmosphere: Option<String>,
|
||||||
pub current_resume: Option<String>,
|
pub current_resume: Option<String>,
|
||||||
pub meta: String,
|
pub _meta: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches the guideline for a specific book.
|
/// Fetches the guideline for a specific book.
|
||||||
@@ -211,8 +211,8 @@ pub fn fetch_guide_line_ai(conn: &Connection, user_id: &str, book_id: &str, lang
|
|||||||
global_resume: query_row.get(2)?, atmosphere: query_row.get(3)?,
|
global_resume: query_row.get(2)?, atmosphere: query_row.get(3)?,
|
||||||
verbe_tense: query_row.get(4)?, langue: query_row.get(5)?,
|
verbe_tense: query_row.get(4)?, langue: query_row.get(5)?,
|
||||||
themes: query_row.get(6)?, current_resume: query_row.get(7)?,
|
themes: query_row.get(6)?, current_resume: query_row.get(7)?,
|
||||||
user_id: user_id.to_string(), book_id: book_id.to_string(),
|
_user_id: user_id.to_string(), _book_id: book_id.to_string(),
|
||||||
tone: None, meta: String::new(),
|
_tone: None, _meta: String::new(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|error| match error {
|
.map_err(|error| match error {
|
||||||
|
|||||||
@@ -8,16 +8,6 @@ use crate::error::{AppError, AppResult};
|
|||||||
use crate::helpers::timestamp_in_seconds;
|
use crate::helpers::timestamp_in_seconds;
|
||||||
use crate::shared::types::Lang;
|
use crate::shared::types::Lang;
|
||||||
|
|
||||||
/// Represents the synced guideline data for a book.
|
|
||||||
pub struct SyncedGuideLine {
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the synced AI guideline data for a book.
|
|
||||||
pub struct SyncedAIGuideLine {
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the decrypted guideline properties for a book.
|
/// Represents the decrypted guideline properties for a book.
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|||||||
@@ -10,21 +10,6 @@ use crate::error::AppResult;
|
|||||||
use crate::helpers::{create_unique_id, timestamp_in_seconds};
|
use crate::helpers::{create_unique_id, timestamp_in_seconds};
|
||||||
use crate::shared::types::Lang;
|
use crate::shared::types::Lang;
|
||||||
|
|
||||||
/// Represents the story details of an incident within a chapter.
|
|
||||||
pub struct IncidentStory {
|
|
||||||
pub incident_title: String,
|
|
||||||
pub incident_summary: String,
|
|
||||||
pub chapter_summary: String,
|
|
||||||
pub chapter_goal: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a synced incident with minimal information for comparison.
|
|
||||||
pub struct SyncedIncident {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct IncidentProps {
|
pub struct IncidentProps {
|
||||||
|
|||||||
@@ -9,13 +9,6 @@ use crate::error::AppResult;
|
|||||||
use crate::helpers::{create_unique_id, timestamp_in_seconds};
|
use crate::helpers::{create_unique_id, timestamp_in_seconds};
|
||||||
use crate::shared::types::Lang;
|
use crate::shared::types::Lang;
|
||||||
|
|
||||||
/// Represents a synced issue with its metadata.
|
|
||||||
pub struct SyncedIssue {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct IssueProps {
|
pub struct IssueProps {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ fn get_session(session: &State<SessionState>) -> Result<(String, Lang), AppError
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GetAllLocationsData {
|
pub struct GetAllLocationsData {
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
pub enabled: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -24,13 +24,6 @@ pub struct LocationElementQueryResult {
|
|||||||
pub element_description: Option<String>,
|
pub element_description: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LocationByTagResult {
|
|
||||||
pub element_name: String,
|
|
||||||
pub element_description: Option<String>,
|
|
||||||
pub sub_elem_name: Option<String>,
|
|
||||||
pub sub_elem_description: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BookLocationTable {
|
pub struct BookLocationTable {
|
||||||
pub loc_id: String,
|
pub loc_id: String,
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
@@ -349,51 +342,6 @@ pub fn fetch_location_tags(conn: &Connection, user_id: &str, book_id: &str, lang
|
|||||||
/// * `conn` - Database connection
|
/// * `conn` - Database connection
|
||||||
/// * `user_id` - The user's unique identifier
|
/// * `user_id` - The user's unique identifier
|
||||||
/// * `locations` - An array of location tag IDs to search for
|
/// * `locations` - An array of location tag IDs to search for
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns an array of locations matching the provided tags.
|
|
||||||
/// Errors if no tags are provided or no locations are found.
|
|
||||||
pub fn fetch_locations_by_tags(conn: &Connection, user_id: &str, locations: &[String], lang: Lang) -> AppResult<Vec<LocationByTagResult>> {
|
|
||||||
if locations.is_empty() {
|
|
||||||
return Err(AppError::Validation(if lang == Lang::Fr { "Aucun tag fourni.".to_string() } else { "No tags provided.".to_string() }));
|
|
||||||
}
|
|
||||||
|
|
||||||
let location_placeholders: String = locations.iter().map(|_| "?").collect::<Vec<_>>().join(",");
|
|
||||||
let query = format!("SELECT el.element_name, el.element_description, se.sub_elem_name, se.sub_elem_description FROM location_element AS el LEFT JOIN location_sub_element AS se ON el.element_id = se.element_id WHERE el.user_id = ?1 AND (el.element_id IN ({}) OR se.sub_element_id IN ({}))", location_placeholders, location_placeholders);
|
|
||||||
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare(&query)
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les emplacements par tags.".to_string() } else { "Unable to retrieve locations by tags.".to_string() }))?;
|
|
||||||
|
|
||||||
let mut param_values: Vec<Box<dyn rusqlite::types::ToSql>> = Vec::new();
|
|
||||||
param_values.push(Box::new(user_id.to_string()));
|
|
||||||
for location in locations {
|
|
||||||
param_values.push(Box::new(location.clone()));
|
|
||||||
}
|
|
||||||
for location in locations {
|
|
||||||
param_values.push(Box::new(location.clone()));
|
|
||||||
}
|
|
||||||
let param_refs: Vec<&dyn rusqlite::types::ToSql> = param_values.iter().map(|parameter| parameter.as_ref() as &dyn rusqlite::types::ToSql).collect();
|
|
||||||
|
|
||||||
let locations_by_tags = statement
|
|
||||||
.query_map(param_refs.as_slice(), |query_row| {
|
|
||||||
Ok(LocationByTagResult {
|
|
||||||
element_name: query_row.get(0)?,
|
|
||||||
element_description: query_row.get(1)?,
|
|
||||||
sub_elem_name: query_row.get(2)?,
|
|
||||||
sub_elem_description: query_row.get(3)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les emplacements par tags.".to_string() } else { "Unable to retrieve locations by tags.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les emplacements par tags.".to_string() } else { "Unable to retrieve locations by tags.".to_string() }))?;
|
|
||||||
|
|
||||||
if locations_by_tags.is_empty() {
|
|
||||||
return Err(AppError::NotFound(if lang == Lang::Fr { "Aucun emplacement trouvé avec ces tags.".to_string() } else { "No locations found with these tags.".to_string() }));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(locations_by_tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if a location exists in the database.
|
/// Checks if a location exists in the database.
|
||||||
/// * `conn` - Database connection
|
/// * `conn` - Database connection
|
||||||
/// * `user_id` - The user's unique identifier
|
/// * `user_id` - The user's unique identifier
|
||||||
|
|||||||
@@ -41,29 +41,6 @@ pub struct LocationListResponse {
|
|||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synced location sub-element (lightweight, for comparison).
|
|
||||||
pub struct SyncedLocationSubElement {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Synced location element (lightweight, for comparison).
|
|
||||||
pub struct SyncedLocationElement {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
pub sub_elements: Vec<SyncedLocationSubElement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Synced location (lightweight, for comparison).
|
|
||||||
pub struct SyncedLocation {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
pub elements: Vec<SyncedLocationElement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves all locations for a given user and book.
|
/// Retrieves all locations for a given user and book.
|
||||||
/// * `conn` - Database connection
|
/// * `conn` - Database connection
|
||||||
/// * `user_id` - The user's unique identifier
|
/// * `user_id` - The user's unique identifier
|
||||||
@@ -383,80 +360,3 @@ pub fn get_location_tags(conn: &Connection, user_id: &str, book_id: &str, lang:
|
|||||||
Ok(sub_elements)
|
Ok(sub_elements)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves location elements filtered by specific tag IDs.
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `user_id` - The user's unique identifier
|
|
||||||
/// * `locations` - Array of location tag IDs to filter by
|
|
||||||
/// * `lang` - The language for error messages
|
|
||||||
/// Returns an array of elements with their associated sub-elements.
|
|
||||||
pub fn get_locations_by_tags(conn: &Connection, user_id: &str, locations: &[String], lang: Lang) -> AppResult<Vec<Element>> {
|
|
||||||
let location_tag_records: Vec<repo::LocationByTagResult> = repo::fetch_locations_by_tags(conn, user_id, locations, lang)?;
|
|
||||||
if location_tag_records.is_empty() {
|
|
||||||
return Ok(vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let user_key: String = get_user_encryption_key(user_id)?;
|
|
||||||
let mut location_elements: Vec<Element> = Vec::new();
|
|
||||||
|
|
||||||
for record in &location_tag_records {
|
|
||||||
let element_index: Option<usize> = location_elements.iter().position(|elem| elem.name == record.element_name);
|
|
||||||
|
|
||||||
let element_idx: usize = match element_index {
|
|
||||||
Some(idx) => idx,
|
|
||||||
None => {
|
|
||||||
let decrypted_name: String = decrypt_data_with_user_key(&record.element_name, &user_key)?;
|
|
||||||
let decrypted_description: String = if let Some(ref element_description) = record.element_description {
|
|
||||||
decrypt_data_with_user_key(element_description, &user_key)?
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
location_elements.push(Element {
|
|
||||||
id: String::new(),
|
|
||||||
name: decrypted_name,
|
|
||||||
description: decrypted_description,
|
|
||||||
sub_elements: vec![],
|
|
||||||
});
|
|
||||||
location_elements.len() - 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(ref sub_elem_name) = record.sub_elem_name {
|
|
||||||
let sub_element_exists: bool = location_elements[element_idx].sub_elements.iter().any(|sub| sub.name == *sub_elem_name);
|
|
||||||
if !sub_element_exists {
|
|
||||||
let decrypted_name: String = decrypt_data_with_user_key(sub_elem_name, &user_key)?;
|
|
||||||
let decrypted_description: String = if let Some(ref sub_elem_description) = record.sub_elem_description {
|
|
||||||
decrypt_data_with_user_key(sub_elem_description, &user_key)?
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
location_elements[element_idx].sub_elements.push(SubElement {
|
|
||||||
id: String::new(),
|
|
||||||
name: decrypted_name,
|
|
||||||
description: decrypted_description,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(location_elements)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a formatted description string from an array of location elements.
|
|
||||||
/// * `locations` - Array of location elements to describe
|
|
||||||
/// Returns a formatted string with location names and descriptions.
|
|
||||||
pub fn locations_description(locations: &[Element]) -> String {
|
|
||||||
locations
|
|
||||||
.iter()
|
|
||||||
.map(|location| {
|
|
||||||
let mut description_fields: Vec<String> = Vec::new();
|
|
||||||
if !location.name.is_empty() {
|
|
||||||
description_fields.push(format!("Nom : {}", location.name));
|
|
||||||
}
|
|
||||||
if !location.description.is_empty() {
|
|
||||||
description_fields.push(format!("Description : {}", location.description));
|
|
||||||
}
|
|
||||||
description_fields.join("\n")
|
|
||||||
})
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n\n")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -31,10 +31,7 @@ pub struct OfflineModeStatus {
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct OfflineModeData {
|
pub struct OfflineModeData {}
|
||||||
pub enabled: bool,
|
|
||||||
pub sync_interval_days: Option<i64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ pub struct BookPlotPointsTable {
|
|||||||
pub summary: Option<String>,
|
pub summary: Option<String>,
|
||||||
pub linked_incident_id: Option<String>,
|
pub linked_incident_id: Option<String>,
|
||||||
pub author_id: String,
|
pub author_id: String,
|
||||||
pub book_id: String,
|
pub _book_id: String,
|
||||||
pub last_update: i64,
|
pub last_update: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ pub fn fetch_book_plot_points(conn: &Connection, user_id: &str, book_id: &str, l
|
|||||||
summary: query_row.get(3)?,
|
summary: query_row.get(3)?,
|
||||||
linked_incident_id: query_row.get(4)?,
|
linked_incident_id: query_row.get(4)?,
|
||||||
author_id: query_row.get(5)?,
|
author_id: query_row.get(5)?,
|
||||||
book_id: query_row.get(6)?,
|
_book_id: query_row.get(6)?,
|
||||||
last_update: query_row.get(7)?,
|
last_update: query_row.get(7)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -235,7 +235,7 @@ pub fn fetch_complete_plot_point_by_id(conn: &Connection, plot_point_id: &str, l
|
|||||||
summary: query_row.get(3)?,
|
summary: query_row.get(3)?,
|
||||||
linked_incident_id: query_row.get(4)?,
|
linked_incident_id: query_row.get(4)?,
|
||||||
author_id: query_row.get(5)?,
|
author_id: query_row.get(5)?,
|
||||||
book_id: query_row.get(6)?,
|
_book_id: query_row.get(6)?,
|
||||||
last_update: query_row.get(7)?,
|
last_update: query_row.get(7)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -10,14 +10,6 @@ use crate::error::AppResult;
|
|||||||
use crate::helpers::{create_unique_id, timestamp_in_seconds};
|
use crate::helpers::{create_unique_id, timestamp_in_seconds};
|
||||||
use crate::shared::types::Lang;
|
use crate::shared::types::Lang;
|
||||||
|
|
||||||
/// Represents the story details associated with a plot point.
|
|
||||||
pub struct PlotPointStory {
|
|
||||||
pub plot_title: String,
|
|
||||||
pub plot_summary: String,
|
|
||||||
pub chapter_summary: String,
|
|
||||||
pub chapter_goal: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct PlotPointProps {
|
pub struct PlotPointProps {
|
||||||
@@ -28,13 +20,6 @@ pub struct PlotPointProps {
|
|||||||
pub chapters: Option<Vec<ActChapter>>,
|
pub chapters: Option<Vec<ActChapter>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a synced plot point with minimal information.
|
|
||||||
pub struct SyncedPlotPoint {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves all plot points for a specific book with their associated chapters.
|
/// Retrieves all plot points for a specific book with their associated chapters.
|
||||||
/// Decrypts plot point titles and summaries using the user's encryption key.
|
/// Decrypts plot point titles and summaries using the user's encryption key.
|
||||||
/// * `conn` - Database connection
|
/// * `conn` - Database connection
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ use crate::shared::types::Lang;
|
|||||||
|
|
||||||
pub struct SeriesResult {
|
pub struct SeriesResult {
|
||||||
pub series_id: String,
|
pub series_id: String,
|
||||||
pub user_id: String,
|
pub _user_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub hashed_name: String,
|
pub _hashed_name: String,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub cover_image: Option<String>,
|
pub cover_image: Option<String>,
|
||||||
pub last_update: i64,
|
pub _last_update: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SeriesBookResult {
|
pub struct SeriesBookResult {
|
||||||
pub series_id: String,
|
pub _series_id: String,
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
pub book_order: i64,
|
pub book_order: i64,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
@@ -105,10 +105,10 @@ pub fn fetch_series_by_id(conn: &Connection, user_id: &str, series_id: &str, lan
|
|||||||
let series = statement
|
let series = statement
|
||||||
.query_row(params![series_id, user_id], |query_row| {
|
.query_row(params![series_id, user_id], |query_row| {
|
||||||
Ok(SeriesResult {
|
Ok(SeriesResult {
|
||||||
series_id: query_row.get(0)?, user_id: query_row.get(1)?,
|
series_id: query_row.get(0)?, _user_id: query_row.get(1)?,
|
||||||
name: query_row.get(2)?, hashed_name: query_row.get(3)?,
|
name: query_row.get(2)?, _hashed_name: query_row.get(3)?,
|
||||||
description: query_row.get(4)?, cover_image: query_row.get(5)?,
|
description: query_row.get(4)?, cover_image: query_row.get(5)?,
|
||||||
last_update: query_row.get(6)?,
|
_last_update: query_row.get(6)?,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ pub fn fetch_series_books(conn: &Connection, user_id: &str, series_id: &str, lan
|
|||||||
let books = statement
|
let books = statement
|
||||||
.query_map(params![series_id, user_id], |query_row| {
|
.query_map(params![series_id, user_id], |query_row| {
|
||||||
Ok(SeriesBookResult {
|
Ok(SeriesBookResult {
|
||||||
series_id: query_row.get(0)?, book_id: query_row.get(1)?,
|
_series_id: query_row.get(0)?, book_id: query_row.get(1)?,
|
||||||
book_order: query_row.get(2)?, title: query_row.get(3)?,
|
book_order: query_row.get(2)?, title: query_row.get(3)?,
|
||||||
cover_image: query_row.get(4)?,
|
cover_image: query_row.get(4)?,
|
||||||
})
|
})
|
||||||
@@ -403,32 +403,6 @@ pub fn fetch_synced_series_books(conn: &Connection, user_id: &str, lang: Lang) -
|
|||||||
Ok(books)
|
Ok(books)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches a complete series by ID for sync.
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `series_id` - The unique identifier of the series
|
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns an array containing the series.
|
|
||||||
pub fn fetch_complete_series_by_id(conn: &Connection, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT series_id, user_id, name, hashed_name, description, cover_image, last_update FROM book_series WHERE series_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer la série complète.".to_string() } else { "Unable to retrieve complete series.".to_string() }))?;
|
|
||||||
|
|
||||||
let series = statement
|
|
||||||
.query_map(params![series_id], |query_row| {
|
|
||||||
Ok(SeriesTableResult {
|
|
||||||
series_id: query_row.get(0)?, user_id: query_row.get(1)?,
|
|
||||||
name: query_row.get(2)?, hashed_name: query_row.get(3)?,
|
|
||||||
description: query_row.get(4)?, cover_image: query_row.get(5)?,
|
|
||||||
last_update: query_row.get(6)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer la série complète.".to_string() } else { "Unable to retrieve complete series.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer la série complète.".to_string() } else { "Unable to retrieve complete series.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(series)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts a series for sync purposes.
|
/// Inserts a series for sync purposes.
|
||||||
/// * `conn` - Database connection
|
/// * `conn` - Database connection
|
||||||
/// * `series_id` - The unique identifier of the series
|
/// * `series_id` - The unique identifier of the series
|
||||||
@@ -498,27 +472,6 @@ pub fn insert_sync_series_book(conn: &Connection, series_id: &str, book_id: &str
|
|||||||
Ok(insert_result > 0)
|
Ok(insert_result > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a series-book relationship exists.
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `series_id` - The unique identifier of the series
|
|
||||||
/// * `book_id` - The unique identifier of the book
|
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns true if the relationship exists.
|
|
||||||
pub fn is_series_book_exist(conn: &Connection, series_id: &str, book_id: &str, lang: Lang) -> AppResult<bool> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT 1 FROM series_books WHERE series_id = ?1 AND book_id = ?2")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier la liaison série-livre.".to_string() } else { "Unable to check series-book.".to_string() }))?;
|
|
||||||
|
|
||||||
let exists = statement
|
|
||||||
.query_row(params![series_id, book_id], |_query_row| Ok(true));
|
|
||||||
|
|
||||||
match exists {
|
|
||||||
Ok(_) => Ok(true),
|
|
||||||
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(false),
|
|
||||||
Err(_) => Err(AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier la liaison série-livre.".to_string() } else { "Unable to check series-book.".to_string() })),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if a series exists for a user (alias for is_series_exist).
|
/// Checks if a series exists for a user (alias for is_series_exist).
|
||||||
/// * `conn` - Database connection
|
/// * `conn` - Database connection
|
||||||
/// * `user_id` - The unique identifier of the user
|
/// * `user_id` - The unique identifier of the user
|
||||||
|
|||||||
@@ -9,15 +9,6 @@ use crate::error::{AppError, AppResult};
|
|||||||
use crate::helpers::{create_unique_id, timestamp_in_seconds};
|
use crate::helpers::{create_unique_id, timestamp_in_seconds};
|
||||||
use crate::shared::types::Lang;
|
use crate::shared::types::Lang;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct SeriesProps {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub description: String,
|
|
||||||
pub cover_image: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SeriesDetailProps {
|
pub struct SeriesDetailProps {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ pub struct SeriesCharacterResult {
|
|||||||
|
|
||||||
pub struct SeriesCharacterAttributeResult {
|
pub struct SeriesCharacterAttributeResult {
|
||||||
pub attr_id: String,
|
pub attr_id: String,
|
||||||
pub attribute_name: String,
|
pub _attribute_name: String,
|
||||||
pub attribute_value: String,
|
pub attribute_value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ pub struct SyncedSeriesCharacterAttributeResult {
|
|||||||
pub fn fetch_characters(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharacterResult>> {
|
pub fn fetch_characters(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharacterResult>> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
.prepare("SELECT character_id, first_name, last_name, nickname, age, gender, species, nationality, status, title, category, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color FROM series_characters WHERE series_id = ?1 AND user_id = ?2")
|
.prepare("SELECT character_id, first_name, last_name, nickname, age, gender, species, nationality, status, title, category, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color FROM series_characters WHERE series_id = ?1 AND user_id = ?2")
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les personnages de la s\u{00e9}rie.".to_string() } else { "Unable to retrieve series characters.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages de la série.".to_string() } else { "Unable to retrieve series characters.".to_string() }))?;
|
||||||
|
|
||||||
let characters = statement
|
let characters = statement
|
||||||
.query_map(params![series_id, user_id], |query_row| {
|
.query_map(params![series_id, user_id], |query_row| {
|
||||||
@@ -107,9 +107,9 @@ pub fn fetch_characters(conn: &Connection, user_id: &str, series_id: &str, lang:
|
|||||||
notes: query_row.get(18)?, color: query_row.get(19)?,
|
notes: query_row.get(18)?, color: query_row.get(19)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les personnages de la s\u{00e9}rie.".to_string() } else { "Unable to retrieve series characters.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages de la série.".to_string() } else { "Unable to retrieve series characters.".to_string() }))?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les personnages de la s\u{00e9}rie.".to_string() } else { "Unable to retrieve series characters.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages de la série.".to_string() } else { "Unable to retrieve series characters.".to_string() }))?;
|
||||||
|
|
||||||
Ok(characters)
|
Ok(characters)
|
||||||
}
|
}
|
||||||
@@ -234,7 +234,7 @@ pub fn update_character(
|
|||||||
"UPDATE series_characters SET first_name = ?1, last_name = ?2, nickname = ?3, age = ?4, gender = ?5, species = ?6, nationality = ?7, status = ?8, title = ?9, category = ?10, image = ?11, role = ?12, biography = ?13, history = ?14, speech_pattern = ?15, catchphrase = ?16, residence = ?17, notes = ?18, color = ?19, last_update = ?20 WHERE character_id = ?21 AND user_id = ?22",
|
"UPDATE series_characters SET first_name = ?1, last_name = ?2, nickname = ?3, age = ?4, gender = ?5, species = ?6, nationality = ?7, status = ?8, title = ?9, category = ?10, image = ?11, role = ?12, biography = ?13, history = ?14, speech_pattern = ?15, catchphrase = ?16, residence = ?17, notes = ?18, color = ?19, last_update = ?20 WHERE character_id = ?21 AND user_id = ?22",
|
||||||
params![encrypted_name, encrypted_last_name, encrypted_nickname, encrypted_age, encrypted_gender, encrypted_species, encrypted_nationality, encrypted_status, encrypted_title, encrypted_category, encrypted_image, encrypted_role, encrypted_biography, encrypted_history, encrypted_speech_pattern, encrypted_catchphrase, encrypted_residence, encrypted_notes, encrypted_color, last_update, character_id, user_id],
|
params![encrypted_name, encrypted_last_name, encrypted_nickname, encrypted_age, encrypted_gender, encrypted_species, encrypted_nationality, encrypted_status, encrypted_title, encrypted_category, encrypted_image, encrypted_role, encrypted_biography, encrypted_history, encrypted_speech_pattern, encrypted_catchphrase, encrypted_residence, encrypted_notes, encrypted_color, last_update, character_id, user_id],
|
||||||
)
|
)
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre \u{00e0} jour le personnage.".to_string() } else { "Unable to update character.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le personnage.".to_string() } else { "Unable to update character.".to_string() }))?;
|
||||||
|
|
||||||
Ok(update_result > 0)
|
Ok(update_result > 0)
|
||||||
}
|
}
|
||||||
@@ -281,15 +281,15 @@ pub fn delete_attribute(conn: &Connection, user_id: &str, attribute_id: &str, la
|
|||||||
pub fn fetch_attributes(conn: &Connection, character_id: &str, user_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharacterAttributeResult>> {
|
pub fn fetch_attributes(conn: &Connection, character_id: &str, user_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharacterAttributeResult>> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
.prepare("SELECT attr_id, attribute_name, attribute_value FROM series_characters_attributes WHERE character_id = ?1 AND user_id = ?2")
|
.prepare("SELECT attr_id, attribute_name, attribute_value FROM series_characters_attributes WHERE character_id = ?1 AND user_id = ?2")
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs.".to_string() } else { "Unable to retrieve attributes.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs.".to_string() } else { "Unable to retrieve attributes.".to_string() }))?;
|
||||||
|
|
||||||
let attributes = statement
|
let attributes = statement
|
||||||
.query_map(params![character_id, user_id], |query_row| {
|
.query_map(params![character_id, user_id], |query_row| {
|
||||||
Ok(SeriesCharacterAttributeResult { attr_id: query_row.get(0)?, attribute_name: query_row.get(1)?, attribute_value: query_row.get(2)? })
|
Ok(SeriesCharacterAttributeResult { attr_id: query_row.get(0)?, _attribute_name: query_row.get(1)?, attribute_value: query_row.get(2)? })
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs.".to_string() } else { "Unable to retrieve attributes.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs.".to_string() } else { "Unable to retrieve attributes.".to_string() }))?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs.".to_string() } else { "Unable to retrieve attributes.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs.".to_string() } else { "Unable to retrieve attributes.".to_string() }))?;
|
||||||
|
|
||||||
Ok(attributes)
|
Ok(attributes)
|
||||||
}
|
}
|
||||||
@@ -303,12 +303,12 @@ pub fn fetch_attributes(conn: &Connection, character_id: &str, user_id: &str, la
|
|||||||
pub fn is_character_exist(conn: &Connection, user_id: &str, character_id: &str, lang: Lang) -> AppResult<bool> {
|
pub fn is_character_exist(conn: &Connection, user_id: &str, character_id: &str, lang: Lang) -> AppResult<bool> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
.prepare("SELECT 1 FROM series_characters WHERE character_id = ?1 AND user_id = ?2")
|
.prepare("SELECT 1 FROM series_characters WHERE character_id = ?1 AND user_id = ?2")
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de v\u{00e9}rifier l'existence du personnage.".to_string() } else { "Unable to check character existence.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du personnage.".to_string() } else { "Unable to check character existence.".to_string() }))?;
|
||||||
|
|
||||||
let existence_check = statement
|
let existence_check = statement
|
||||||
.query_row(params![character_id, user_id], |_query_row| Ok(true))
|
.query_row(params![character_id, user_id], |_query_row| Ok(true))
|
||||||
.optional()
|
.optional()
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de v\u{00e9}rifier l'existence du personnage.".to_string() } else { "Unable to check character existence.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du personnage.".to_string() } else { "Unable to check character existence.".to_string() }))?;
|
||||||
|
|
||||||
Ok(existence_check.is_some())
|
Ok(existence_check.is_some())
|
||||||
}
|
}
|
||||||
@@ -322,7 +322,7 @@ pub fn is_character_exist(conn: &Connection, user_id: &str, character_id: &str,
|
|||||||
pub fn fetch_series_characters_table(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharactersTableResult>> {
|
pub fn fetch_series_characters_table(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharactersTableResult>> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
.prepare("SELECT character_id, series_id, user_id, first_name, last_name, nickname, age, gender, species, nationality, status, title, category, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update FROM series_characters WHERE series_id = ?1 AND user_id = ?2")
|
.prepare("SELECT character_id, series_id, user_id, first_name, last_name, nickname, age, gender, species, nationality, status, title, category, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update FROM series_characters WHERE series_id = ?1 AND user_id = ?2")
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les personnages pour sync.".to_string() } else { "Unable to retrieve characters for sync.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages pour sync.".to_string() } else { "Unable to retrieve characters for sync.".to_string() }))?;
|
||||||
|
|
||||||
let characters = statement
|
let characters = statement
|
||||||
.query_map(params![series_id, user_id], |query_row| {
|
.query_map(params![series_id, user_id], |query_row| {
|
||||||
@@ -341,39 +341,13 @@ pub fn fetch_series_characters_table(conn: &Connection, user_id: &str, series_id
|
|||||||
last_update: query_row.get(22)?,
|
last_update: query_row.get(22)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les personnages pour sync.".to_string() } else { "Unable to retrieve characters for sync.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages pour sync.".to_string() } else { "Unable to retrieve characters for sync.".to_string() }))?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les personnages pour sync.".to_string() } else { "Unable to retrieve characters for sync.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages pour sync.".to_string() } else { "Unable to retrieve characters for sync.".to_string() }))?;
|
||||||
|
|
||||||
Ok(characters)
|
Ok(characters)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches all attributes for a character for sync.
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `user_id` - The unique identifier of the user
|
|
||||||
/// * `character_id` - The unique identifier of the character
|
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns an array of full attribute table results.
|
|
||||||
pub fn fetch_series_character_attributes_table(conn: &Connection, user_id: &str, character_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharacterAttributesTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update FROM series_characters_attributes WHERE character_id = ?1 AND user_id = ?2")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs pour sync.".to_string() } else { "Unable to retrieve attributes for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
let attributes = statement
|
|
||||||
.query_map(params![character_id, user_id], |query_row| {
|
|
||||||
Ok(SeriesCharacterAttributesTableResult {
|
|
||||||
attr_id: query_row.get(0)?, character_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, attribute_name: query_row.get(3)?,
|
|
||||||
attribute_value: query_row.get(4)?, last_update: query_row.get(5)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs pour sync.".to_string() } else { "Unable to retrieve attributes for sync.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs pour sync.".to_string() } else { "Unable to retrieve attributes for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(attributes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all series characters for a user for sync comparison.
|
/// Fetches all series characters for a user for sync comparison.
|
||||||
/// * `conn` - Database connection
|
/// * `conn` - Database connection
|
||||||
/// * `user_id` - The unique identifier of the user
|
/// * `user_id` - The unique identifier of the user
|
||||||
@@ -382,15 +356,15 @@ pub fn fetch_series_character_attributes_table(conn: &Connection, user_id: &str,
|
|||||||
pub fn fetch_synced_series_characters(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedSeriesCharacterResult>> {
|
pub fn fetch_synced_series_characters(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedSeriesCharacterResult>> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
.prepare("SELECT character_id, series_id, first_name, last_update FROM series_characters WHERE user_id = ?1")
|
.prepare("SELECT character_id, series_id, first_name, last_update FROM series_characters WHERE user_id = ?1")
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les personnages de s\u{00e9}rie pour sync.".to_string() } else { "Unable to retrieve series characters for sync.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages de série pour sync.".to_string() } else { "Unable to retrieve series characters for sync.".to_string() }))?;
|
||||||
|
|
||||||
let characters = statement
|
let characters = statement
|
||||||
.query_map(params![user_id], |query_row| {
|
.query_map(params![user_id], |query_row| {
|
||||||
Ok(SyncedSeriesCharacterResult { character_id: query_row.get(0)?, series_id: query_row.get(1)?, first_name: query_row.get(2)?, last_update: query_row.get(3)? })
|
Ok(SyncedSeriesCharacterResult { character_id: query_row.get(0)?, series_id: query_row.get(1)?, first_name: query_row.get(2)?, last_update: query_row.get(3)? })
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les personnages de s\u{00e9}rie pour sync.".to_string() } else { "Unable to retrieve series characters for sync.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages de série pour sync.".to_string() } else { "Unable to retrieve series characters for sync.".to_string() }))?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les personnages de s\u{00e9}rie pour sync.".to_string() } else { "Unable to retrieve series characters for sync.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages de série pour sync.".to_string() } else { "Unable to retrieve series characters for sync.".to_string() }))?;
|
||||||
|
|
||||||
Ok(characters)
|
Ok(characters)
|
||||||
}
|
}
|
||||||
@@ -403,74 +377,15 @@ pub fn fetch_synced_series_characters(conn: &Connection, user_id: &str, lang: La
|
|||||||
pub fn fetch_synced_series_character_attributes(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedSeriesCharacterAttributeResult>> {
|
pub fn fetch_synced_series_character_attributes(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedSeriesCharacterAttributeResult>> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
.prepare("SELECT attr_id, character_id, attribute_name, last_update FROM series_characters_attributes WHERE user_id = ?1")
|
.prepare("SELECT attr_id, character_id, attribute_name, last_update FROM series_characters_attributes WHERE user_id = ?1")
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs de personnage pour sync.".to_string() } else { "Unable to retrieve character attributes for sync.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs de personnage pour sync.".to_string() } else { "Unable to retrieve character attributes for sync.".to_string() }))?;
|
||||||
|
|
||||||
let attributes = statement
|
let attributes = statement
|
||||||
.query_map(params![user_id], |query_row| {
|
.query_map(params![user_id], |query_row| {
|
||||||
Ok(SyncedSeriesCharacterAttributeResult { attr_id: query_row.get(0)?, character_id: query_row.get(1)?, attribute_name: query_row.get(2)?, last_update: query_row.get(3)? })
|
Ok(SyncedSeriesCharacterAttributeResult { attr_id: query_row.get(0)?, character_id: query_row.get(1)?, attribute_name: query_row.get(2)?, last_update: query_row.get(3)? })
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs de personnage pour sync.".to_string() } else { "Unable to retrieve character attributes for sync.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs de personnage pour sync.".to_string() } else { "Unable to retrieve character attributes for sync.".to_string() }))?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs de personnage pour sync.".to_string() } else { "Unable to retrieve character attributes for sync.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs de personnage pour sync.".to_string() } else { "Unable to retrieve character attributes for sync.".to_string() }))?;
|
||||||
|
|
||||||
Ok(attributes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches a complete character by ID for sync.
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `character_id` - The unique identifier of the character
|
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns an array of full character table results.
|
|
||||||
pub fn fetch_complete_character_by_id(conn: &Connection, character_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharactersTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT character_id, series_id, user_id, first_name, last_name, nickname, age, gender, species, nationality, status, title, category, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update FROM series_characters WHERE character_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer le personnage complet.".to_string() } else { "Unable to retrieve complete character.".to_string() }))?;
|
|
||||||
|
|
||||||
let characters = statement
|
|
||||||
.query_map(params![character_id], |query_row| {
|
|
||||||
Ok(SeriesCharactersTableResult {
|
|
||||||
character_id: query_row.get(0)?, series_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, first_name: query_row.get(3)?,
|
|
||||||
last_name: query_row.get(4)?, nickname: query_row.get(5)?,
|
|
||||||
age: query_row.get(6)?, gender: query_row.get(7)?,
|
|
||||||
species: query_row.get(8)?, nationality: query_row.get(9)?,
|
|
||||||
status: query_row.get(10)?, title: query_row.get(11)?,
|
|
||||||
category: query_row.get(12)?, image: query_row.get(13)?,
|
|
||||||
role: query_row.get(14)?, biography: query_row.get(15)?,
|
|
||||||
history: query_row.get(16)?, speech_pattern: query_row.get(17)?,
|
|
||||||
catchphrase: query_row.get(18)?, residence: query_row.get(19)?,
|
|
||||||
notes: query_row.get(20)?, color: query_row.get(21)?,
|
|
||||||
last_update: query_row.get(22)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer le personnage complet.".to_string() } else { "Unable to retrieve complete character.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer le personnage complet.".to_string() } else { "Unable to retrieve complete character.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(characters)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches a complete character attribute by ID for sync.
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `attr_id` - The unique identifier of the attribute
|
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns an array of full attribute table results.
|
|
||||||
pub fn fetch_complete_attribute_by_id(conn: &Connection, attr_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharacterAttributesTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update FROM series_characters_attributes WHERE attr_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer l'attribut complet.".to_string() } else { "Unable to retrieve complete attribute.".to_string() }))?;
|
|
||||||
|
|
||||||
let attributes = statement
|
|
||||||
.query_map(params![attr_id], |query_row| {
|
|
||||||
Ok(SeriesCharacterAttributesTableResult {
|
|
||||||
attr_id: query_row.get(0)?, character_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, attribute_name: query_row.get(3)?,
|
|
||||||
attribute_value: query_row.get(4)?, last_update: query_row.get(5)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer l'attribut complet.".to_string() } else { "Unable to retrieve complete attribute.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer l'attribut complet.".to_string() } else { "Unable to retrieve complete attribute.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(attributes)
|
Ok(attributes)
|
||||||
}
|
}
|
||||||
@@ -515,7 +430,7 @@ pub fn insert_sync_series_character(
|
|||||||
"INSERT INTO series_characters (character_id, series_id, user_id, first_name, last_name, nickname, age, gender, species, nationality, status, category, title, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23) ON CONFLICT(character_id) DO UPDATE SET first_name = excluded.first_name, last_name = excluded.last_name, nickname = excluded.nickname, age = excluded.age, gender = excluded.gender, species = excluded.species, nationality = excluded.nationality, status = excluded.status, category = excluded.category, title = excluded.title, image = excluded.image, role = excluded.role, biography = excluded.biography, history = excluded.history, speech_pattern = excluded.speech_pattern, catchphrase = excluded.catchphrase, residence = excluded.residence, notes = excluded.notes, color = excluded.color, last_update = excluded.last_update",
|
"INSERT INTO series_characters (character_id, series_id, user_id, first_name, last_name, nickname, age, gender, species, nationality, status, category, title, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23) ON CONFLICT(character_id) DO UPDATE SET first_name = excluded.first_name, last_name = excluded.last_name, nickname = excluded.nickname, age = excluded.age, gender = excluded.gender, species = excluded.species, nationality = excluded.nationality, status = excluded.status, category = excluded.category, title = excluded.title, image = excluded.image, role = excluded.role, biography = excluded.biography, history = excluded.history, speech_pattern = excluded.speech_pattern, catchphrase = excluded.catchphrase, residence = excluded.residence, notes = excluded.notes, color = excluded.color, last_update = excluded.last_update",
|
||||||
params![character_id, series_id, user_id, first_name, last_name, nickname, age, gender, species, nationality, status, category, title, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update],
|
params![character_id, series_id, user_id, first_name, last_name, nickname, age, gender, species, nationality, status, category, title, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update],
|
||||||
)
|
)
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ins\u{00e9}rer le personnage pour sync.".to_string() } else { "Unable to insert character for sync.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer le personnage pour sync.".to_string() } else { "Unable to insert character for sync.".to_string() }))?;
|
||||||
|
|
||||||
Ok(insert_result > 0)
|
Ok(insert_result > 0)
|
||||||
}
|
}
|
||||||
@@ -559,7 +474,7 @@ pub fn update_sync_series_character(
|
|||||||
"UPDATE series_characters SET first_name = ?1, last_name = ?2, nickname = ?3, age = ?4, gender = ?5, species = ?6, nationality = ?7, status = ?8, category = ?9, title = ?10, image = ?11, role = ?12, biography = ?13, history = ?14, speech_pattern = ?15, catchphrase = ?16, residence = ?17, notes = ?18, color = ?19, last_update = ?20 WHERE character_id = ?21 AND user_id = ?22",
|
"UPDATE series_characters SET first_name = ?1, last_name = ?2, nickname = ?3, age = ?4, gender = ?5, species = ?6, nationality = ?7, status = ?8, category = ?9, title = ?10, image = ?11, role = ?12, biography = ?13, history = ?14, speech_pattern = ?15, catchphrase = ?16, residence = ?17, notes = ?18, color = ?19, last_update = ?20 WHERE character_id = ?21 AND user_id = ?22",
|
||||||
params![first_name, last_name, nickname, age, gender, species, nationality, status, category, title, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update, character_id, user_id],
|
params![first_name, last_name, nickname, age, gender, species, nationality, status, category, title, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update, character_id, user_id],
|
||||||
)
|
)
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre \u{00e0} jour le personnage pour sync.".to_string() } else { "Unable to update character for sync.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le personnage pour sync.".to_string() } else { "Unable to update character for sync.".to_string() }))?;
|
||||||
|
|
||||||
Ok(update_result > 0)
|
Ok(update_result > 0)
|
||||||
}
|
}
|
||||||
@@ -583,7 +498,7 @@ pub fn insert_sync_series_character_attribute(
|
|||||||
"INSERT INTO series_characters_attributes (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6) ON CONFLICT(attr_id) DO UPDATE SET attribute_name = excluded.attribute_name, attribute_value = excluded.attribute_value, last_update = excluded.last_update",
|
"INSERT INTO series_characters_attributes (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6) ON CONFLICT(attr_id) DO UPDATE SET attribute_name = excluded.attribute_name, attribute_value = excluded.attribute_value, last_update = excluded.last_update",
|
||||||
params![attr_id, character_id, user_id, attribute_name, attribute_value, last_update],
|
params![attr_id, character_id, user_id, attribute_name, attribute_value, last_update],
|
||||||
)
|
)
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ins\u{00e9}rer l'attribut pour sync.".to_string() } else { "Unable to insert attribute for sync.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer l'attribut pour sync.".to_string() } else { "Unable to insert attribute for sync.".to_string() }))?;
|
||||||
|
|
||||||
Ok(insert_result > 0)
|
Ok(insert_result > 0)
|
||||||
}
|
}
|
||||||
@@ -597,12 +512,12 @@ pub fn insert_sync_series_character_attribute(
|
|||||||
pub fn is_attribute_exist(conn: &Connection, user_id: &str, attr_id: &str, lang: Lang) -> AppResult<bool> {
|
pub fn is_attribute_exist(conn: &Connection, user_id: &str, attr_id: &str, lang: Lang) -> AppResult<bool> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
.prepare("SELECT 1 FROM series_characters_attributes WHERE attr_id = ?1 AND user_id = ?2")
|
.prepare("SELECT 1 FROM series_characters_attributes WHERE attr_id = ?1 AND user_id = ?2")
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de v\u{00e9}rifier l'existence de l'attribut.".to_string() } else { "Unable to check attribute existence.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'attribut.".to_string() } else { "Unable to check attribute existence.".to_string() }))?;
|
||||||
|
|
||||||
let existence_check = statement
|
let existence_check = statement
|
||||||
.query_row(params![attr_id, user_id], |_query_row| Ok(true))
|
.query_row(params![attr_id, user_id], |_query_row| Ok(true))
|
||||||
.optional()
|
.optional()
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de v\u{00e9}rifier l'existence de l'attribut.".to_string() } else { "Unable to check attribute existence.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'attribut.".to_string() } else { "Unable to check attribute existence.".to_string() }))?;
|
||||||
|
|
||||||
Ok(existence_check.is_some())
|
Ok(existence_check.is_some())
|
||||||
}
|
}
|
||||||
@@ -625,80 +540,11 @@ pub fn update_sync_series_character_attribute(
|
|||||||
"UPDATE series_characters_attributes SET attribute_name = ?1, attribute_value = ?2, last_update = ?3 WHERE attr_id = ?4 AND user_id = ?5",
|
"UPDATE series_characters_attributes SET attribute_name = ?1, attribute_value = ?2, last_update = ?3 WHERE attr_id = ?4 AND user_id = ?5",
|
||||||
params![attribute_name, attribute_value, last_update, attr_id, user_id],
|
params![attribute_name, attribute_value, last_update, attr_id, user_id],
|
||||||
)
|
)
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre \u{00e0} jour l'attribut pour sync.".to_string() } else { "Unable to update attribute for sync.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour l'attribut pour sync.".to_string() } else { "Unable to update attribute for sync.".to_string() }))?;
|
||||||
|
|
||||||
Ok(update_result > 0)
|
Ok(update_result > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches all characters for a series for sync (without user filter).
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `series_id` - The unique identifier of the series
|
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns an array of full character table results.
|
|
||||||
pub fn fetch_characters_table_for_sync(conn: &Connection, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharactersTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT character_id, series_id, user_id, first_name, last_name, nickname, age, gender, species, nationality, status, title, category, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update FROM series_characters WHERE series_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les personnages pour sync.".to_string() } else { "Unable to retrieve characters for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
let characters = statement
|
|
||||||
.query_map(params![series_id], |query_row| {
|
|
||||||
Ok(SeriesCharactersTableResult {
|
|
||||||
character_id: query_row.get(0)?, series_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, first_name: query_row.get(3)?,
|
|
||||||
last_name: query_row.get(4)?, nickname: query_row.get(5)?,
|
|
||||||
age: query_row.get(6)?, gender: query_row.get(7)?,
|
|
||||||
species: query_row.get(8)?, nationality: query_row.get(9)?,
|
|
||||||
status: query_row.get(10)?, title: query_row.get(11)?,
|
|
||||||
category: query_row.get(12)?, image: query_row.get(13)?,
|
|
||||||
role: query_row.get(14)?, biography: query_row.get(15)?,
|
|
||||||
history: query_row.get(16)?, speech_pattern: query_row.get(17)?,
|
|
||||||
catchphrase: query_row.get(18)?, residence: query_row.get(19)?,
|
|
||||||
notes: query_row.get(20)?, color: query_row.get(21)?,
|
|
||||||
last_update: query_row.get(22)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les personnages pour sync.".to_string() } else { "Unable to retrieve characters for sync.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les personnages pour sync.".to_string() } else { "Unable to retrieve characters for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(characters)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all character attributes for a series for sync (without user filter).
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `series_id` - The unique identifier of the series
|
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns an array of full attribute table results.
|
|
||||||
pub fn fetch_character_attributes_table_for_sync(conn: &Connection, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharacterAttributesTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT sca.attr_id, sca.character_id, sca.user_id, sca.attribute_name, sca.attribute_value, sca.last_update FROM series_characters_attributes sca INNER JOIN series_characters sc ON sca.character_id = sc.character_id WHERE sc.series_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs pour sync.".to_string() } else { "Unable to retrieve attributes for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
let attributes = statement
|
|
||||||
.query_map(params![series_id], |query_row| {
|
|
||||||
Ok(SeriesCharacterAttributesTableResult {
|
|
||||||
attr_id: query_row.get(0)?, character_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, attribute_name: query_row.get(3)?,
|
|
||||||
attribute_value: query_row.get(4)?, last_update: query_row.get(5)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs pour sync.".to_string() } else { "Unable to retrieve attributes for sync.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs pour sync.".to_string() } else { "Unable to retrieve attributes for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(attributes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all characters for a series (alias for fetch_series_characters_table).
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `user_id` - The unique identifier of the user
|
|
||||||
/// * `series_id` - The unique identifier of the series
|
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns an array of full character table results.
|
|
||||||
pub fn fetch_series_characters(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharactersTableResult>> {
|
|
||||||
fetch_series_characters_table(conn, user_id, series_id, lang)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all character attributes for a series by series ID.
|
/// Fetches all character attributes for a series by series ID.
|
||||||
/// * `conn` - Database connection
|
/// * `conn` - Database connection
|
||||||
/// * `user_id` - The unique identifier of the user
|
/// * `user_id` - The unique identifier of the user
|
||||||
@@ -708,7 +554,7 @@ pub fn fetch_series_characters(conn: &Connection, user_id: &str, series_id: &str
|
|||||||
pub fn fetch_series_character_attributes_by_series_id(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharacterAttributesTableResult>> {
|
pub fn fetch_series_character_attributes_by_series_id(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharacterAttributesTableResult>> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
.prepare("SELECT sca.attr_id, sca.character_id, sca.user_id, sca.attribute_name, sca.attribute_value, sca.last_update FROM series_characters_attributes sca INNER JOIN series_characters sc ON sca.character_id = sc.character_id WHERE sc.series_id = ?1 AND sc.user_id = ?2")
|
.prepare("SELECT sca.attr_id, sca.character_id, sca.user_id, sca.attribute_name, sca.attribute_value, sca.last_update FROM series_characters_attributes sca INNER JOIN series_characters sc ON sca.character_id = sc.character_id WHERE sc.series_id = ?1 AND sc.user_id = ?2")
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs par s\u{00e9}rie.".to_string() } else { "Unable to retrieve attributes by series.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs par série.".to_string() } else { "Unable to retrieve attributes by series.".to_string() }))?;
|
||||||
|
|
||||||
let attributes = statement
|
let attributes = statement
|
||||||
.query_map(params![series_id, user_id], |query_row| {
|
.query_map(params![series_id, user_id], |query_row| {
|
||||||
@@ -718,9 +564,9 @@ pub fn fetch_series_character_attributes_by_series_id(conn: &Connection, user_id
|
|||||||
attribute_value: query_row.get(4)?, last_update: query_row.get(5)?,
|
attribute_value: query_row.get(4)?, last_update: query_row.get(5)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs par s\u{00e9}rie.".to_string() } else { "Unable to retrieve attributes by series.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs par série.".to_string() } else { "Unable to retrieve attributes by series.".to_string() }))?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de r\u{00e9}cup\u{00e9}rer les attributs par s\u{00e9}rie.".to_string() } else { "Unable to retrieve attributes by series.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs par série.".to_string() } else { "Unable to retrieve attributes by series.".to_string() }))?;
|
||||||
|
|
||||||
Ok(attributes)
|
Ok(attributes)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ pub struct SeriesLocationResult {
|
|||||||
|
|
||||||
pub struct SeriesLocationElementResult {
|
pub struct SeriesLocationElementResult {
|
||||||
pub element_id: String,
|
pub element_id: String,
|
||||||
pub location_id: String,
|
pub _location_id: String,
|
||||||
pub element_name: String,
|
pub element_name: String,
|
||||||
pub element_description: String,
|
pub element_description: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SeriesLocationSubElementResult {
|
pub struct SeriesLocationSubElementResult {
|
||||||
pub sub_element_id: String,
|
pub sub_element_id: String,
|
||||||
pub element_id: String,
|
pub _element_id: String,
|
||||||
pub sub_elem_name: String,
|
pub sub_elem_name: String,
|
||||||
pub sub_elem_description: String,
|
pub sub_elem_description: String,
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ pub fn fetch_elements(conn: &Connection, user_id: &str, location_id: &str, lang:
|
|||||||
|
|
||||||
let elements = statement
|
let elements = statement
|
||||||
.query_map(params![user_id, location_id], |query_row| {
|
.query_map(params![user_id, location_id], |query_row| {
|
||||||
Ok(SeriesLocationElementResult { element_id: query_row.get(0)?, location_id: query_row.get(1)?, element_name: query_row.get(2)?, element_description: query_row.get(3)? })
|
Ok(SeriesLocationElementResult { element_id: query_row.get(0)?, _location_id: query_row.get(1)?, element_name: query_row.get(2)?, element_description: query_row.get(3)? })
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments.".to_string() } else { "Unable to retrieve elements.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments.".to_string() } else { "Unable to retrieve elements.".to_string() }))?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
@@ -114,7 +114,7 @@ pub fn fetch_sub_elements(conn: &Connection, user_id: &str, element_id: &str, la
|
|||||||
|
|
||||||
let sub_elements = statement
|
let sub_elements = statement
|
||||||
.query_map(params![user_id, element_id], |query_row| {
|
.query_map(params![user_id, element_id], |query_row| {
|
||||||
Ok(SeriesLocationSubElementResult { sub_element_id: query_row.get(0)?, element_id: query_row.get(1)?, sub_elem_name: query_row.get(2)?, sub_elem_description: query_row.get(3)? })
|
Ok(SeriesLocationSubElementResult { sub_element_id: query_row.get(0)?, _element_id: query_row.get(1)?, sub_elem_name: query_row.get(2)?, sub_elem_description: query_row.get(3)? })
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments.".to_string() } else { "Unable to retrieve sub-elements.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments.".to_string() } else { "Unable to retrieve sub-elements.".to_string() }))?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
@@ -326,18 +326,6 @@ pub fn insert_sync_location(
|
|||||||
Ok(insert_result > 0)
|
Ok(insert_result > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a series location for sync.
|
|
||||||
pub fn update_sync_location(conn: &Connection, user_id: &str, location_id: &str, loc_name: &str, loc_original_name: &str, last_update: i64, lang: Lang) -> AppResult<bool> {
|
|
||||||
let update_result = conn
|
|
||||||
.execute(
|
|
||||||
"UPDATE series_locations SET loc_name = ?1, loc_original_name = ?2, last_update = ?3 WHERE loc_id = ?4 AND user_id = ?5",
|
|
||||||
params![loc_name, loc_original_name, last_update, location_id, user_id],
|
|
||||||
)
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le lieu pour sync.".to_string() } else { "Unable to update location for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(update_result > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts a series location element for sync.
|
/// Inserts a series location element for sync.
|
||||||
pub fn insert_sync_location_element(
|
pub fn insert_sync_location_element(
|
||||||
conn: &Connection, element_id: &str, location_id: &str, user_id: &str,
|
conn: &Connection, element_id: &str, location_id: &str, user_id: &str,
|
||||||
@@ -353,21 +341,6 @@ pub fn insert_sync_location_element(
|
|||||||
Ok(insert_result > 0)
|
Ok(insert_result > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a series location element for sync.
|
|
||||||
pub fn update_sync_location_element(
|
|
||||||
conn: &Connection, user_id: &str, element_id: &str, element_name: &str,
|
|
||||||
original_name: &str, element_description: Option<&str>, last_update: i64, lang: Lang,
|
|
||||||
) -> AppResult<bool> {
|
|
||||||
let update_result = conn
|
|
||||||
.execute(
|
|
||||||
"UPDATE series_location_elements SET element_name = ?1, original_name = ?2, element_description = ?3, last_update = ?4 WHERE element_id = ?5 AND user_id = ?6",
|
|
||||||
params![element_name, original_name, element_description, last_update, element_id, user_id],
|
|
||||||
)
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour l'élément de lieu pour sync.".to_string() } else { "Unable to update location element for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(update_result > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts a series location sub-element for sync.
|
/// Inserts a series location sub-element for sync.
|
||||||
pub fn insert_sync_location_sub_element(
|
pub fn insert_sync_location_sub_element(
|
||||||
conn: &Connection, sub_element_id: &str, element_id: &str, user_id: &str,
|
conn: &Connection, sub_element_id: &str, element_id: &str, user_id: &str,
|
||||||
@@ -383,88 +356,6 @@ pub fn insert_sync_location_sub_element(
|
|||||||
Ok(insert_result > 0)
|
Ok(insert_result > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a series location sub-element for sync.
|
|
||||||
pub fn update_sync_location_sub_element(
|
|
||||||
conn: &Connection, user_id: &str, sub_element_id: &str, sub_elem_name: &str,
|
|
||||||
original_name: &str, sub_elem_description: Option<&str>, last_update: i64, lang: Lang,
|
|
||||||
) -> AppResult<bool> {
|
|
||||||
let update_result = conn
|
|
||||||
.execute(
|
|
||||||
"UPDATE series_location_sub_elements SET sub_elem_name = ?1, original_name = ?2, sub_elem_description = ?3, last_update = ?4 WHERE sub_element_id = ?5 AND user_id = ?6",
|
|
||||||
params![sub_elem_name, original_name, sub_elem_description, last_update, sub_element_id, user_id],
|
|
||||||
)
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le sous-élément pour sync.".to_string() } else { "Unable to update sub-element for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(update_result > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all locations for a series for sync (without user filter).
|
|
||||||
pub fn fetch_locations_table_for_sync(conn: &Connection, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesLocationsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT loc_id, series_id, user_id, loc_name, loc_original_name, last_update FROM series_locations WHERE series_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux pour sync.".to_string() } else { "Unable to retrieve locations for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
let locations = statement
|
|
||||||
.query_map(params![series_id], |query_row| {
|
|
||||||
Ok(SeriesLocationsTableResult {
|
|
||||||
loc_id: query_row.get(0)?, series_id: query_row.get(1)?, user_id: query_row.get(2)?,
|
|
||||||
loc_name: query_row.get(3)?, loc_original_name: query_row.get(4)?, last_update: query_row.get(5)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux pour sync.".to_string() } else { "Unable to retrieve locations for sync.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux pour sync.".to_string() } else { "Unable to retrieve locations for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(locations)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all location elements for a series for sync (without user filter).
|
|
||||||
pub fn fetch_location_elements_table_for_sync(conn: &Connection, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesLocationElementsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT sle.element_id, sle.location_id, sle.user_id, sle.element_name, sle.original_name, sle.element_description, sle.last_update FROM series_location_elements sle INNER JOIN series_locations sl ON sle.location_id = sl.loc_id WHERE sl.series_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu pour sync.".to_string() } else { "Unable to retrieve location elements for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
let elements = statement
|
|
||||||
.query_map(params![series_id], |query_row| {
|
|
||||||
Ok(SeriesLocationElementsTableResult {
|
|
||||||
element_id: query_row.get(0)?, location_id: query_row.get(1)?, user_id: query_row.get(2)?,
|
|
||||||
element_name: query_row.get(3)?, original_name: query_row.get(4)?,
|
|
||||||
element_description: query_row.get(5)?, last_update: query_row.get(6)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu pour sync.".to_string() } else { "Unable to retrieve location elements for sync.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu pour sync.".to_string() } else { "Unable to retrieve location elements for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(elements)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all location sub-elements for a series for sync (without user filter).
|
|
||||||
pub fn fetch_location_sub_elements_table_for_sync(conn: &Connection, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesLocationSubElementsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT slse.sub_element_id, slse.element_id, slse.user_id, slse.sub_elem_name, slse.original_name, slse.sub_elem_description, slse.last_update FROM series_location_sub_elements slse INNER JOIN series_location_elements sle ON slse.element_id = sle.element_id INNER JOIN series_locations sl ON sle.location_id = sl.loc_id WHERE sl.series_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu pour sync.".to_string() } else { "Unable to retrieve location sub-elements for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
let sub_elements = statement
|
|
||||||
.query_map(params![series_id], |query_row| {
|
|
||||||
Ok(SeriesLocationSubElementsTableResult {
|
|
||||||
sub_element_id: query_row.get(0)?, element_id: query_row.get(1)?, user_id: query_row.get(2)?,
|
|
||||||
sub_elem_name: query_row.get(3)?, original_name: query_row.get(4)?,
|
|
||||||
sub_elem_description: query_row.get(5)?, last_update: query_row.get(6)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu pour sync.".to_string() } else { "Unable to retrieve location sub-elements for sync.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu pour sync.".to_string() } else { "Unable to retrieve location sub-elements for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(sub_elements)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all locations for a series (alias for fetch_series_locations_table).
|
|
||||||
pub fn fetch_series_locations(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesLocationsTableResult>> {
|
|
||||||
fetch_series_locations_table(conn, user_id, series_id, lang)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all location elements for a series by series ID.
|
/// Fetches all location elements for a series by series ID.
|
||||||
pub fn fetch_series_location_elements_by_series_id(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesLocationElementsTableResult>> {
|
pub fn fetch_series_location_elements_by_series_id(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesLocationElementsTableResult>> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::shared::types::Lang;
|
|||||||
|
|
||||||
pub struct SeriesSpellResult {
|
pub struct SeriesSpellResult {
|
||||||
pub spell_id: String,
|
pub spell_id: String,
|
||||||
pub series_id: String,
|
pub _series_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub appearance: String,
|
pub appearance: String,
|
||||||
@@ -71,7 +71,7 @@ pub fn fetch_spells(conn: &Connection, user_id: &str, series_id: &str, lang: Lan
|
|||||||
let spells = statement
|
let spells = statement
|
||||||
.query_map(params![user_id, series_id], |query_row| {
|
.query_map(params![user_id, series_id], |query_row| {
|
||||||
Ok(SeriesSpellResult {
|
Ok(SeriesSpellResult {
|
||||||
spell_id: query_row.get(0)?, series_id: query_row.get(1)?,
|
spell_id: query_row.get(0)?, _series_id: query_row.get(1)?,
|
||||||
name: query_row.get(2)?, description: query_row.get(3)?,
|
name: query_row.get(2)?, description: query_row.get(3)?,
|
||||||
appearance: query_row.get(4)?, tags: query_row.get(5)?,
|
appearance: query_row.get(4)?, tags: query_row.get(5)?,
|
||||||
power_level: query_row.get(6)?, components: query_row.get(7)?,
|
power_level: query_row.get(6)?, components: query_row.get(7)?,
|
||||||
@@ -94,7 +94,7 @@ pub fn fetch_spell_by_id(conn: &Connection, user_id: &str, spell_id: &str, lang:
|
|||||||
let spell = statement
|
let spell = statement
|
||||||
.query_row(params![user_id, spell_id], |query_row| {
|
.query_row(params![user_id, spell_id], |query_row| {
|
||||||
Ok(SeriesSpellResult {
|
Ok(SeriesSpellResult {
|
||||||
spell_id: query_row.get(0)?, series_id: query_row.get(1)?,
|
spell_id: query_row.get(0)?, _series_id: query_row.get(1)?,
|
||||||
name: query_row.get(2)?, description: query_row.get(3)?,
|
name: query_row.get(2)?, description: query_row.get(3)?,
|
||||||
appearance: query_row.get(4)?, tags: query_row.get(5)?,
|
appearance: query_row.get(4)?, tags: query_row.get(5)?,
|
||||||
power_level: query_row.get(6)?, components: query_row.get(7)?,
|
power_level: query_row.get(6)?, components: query_row.get(7)?,
|
||||||
@@ -310,55 +310,6 @@ pub fn fetch_synced_series_spell_tags(conn: &Connection, user_id: &str, lang: La
|
|||||||
Ok(tags)
|
Ok(tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches a complete spell by ID for sync.
|
|
||||||
pub fn fetch_spell_table_by_id(conn: &Connection, user_id: &str, spell_id: &str, lang: Lang) -> AppResult<Option<SeriesSpellsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT spell_id, series_id, user_id, name, name_hash, description, appearance, tags, power_level, components, limitations, notes, last_update FROM series_spells WHERE spell_id = ?1 AND user_id = ?2")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le sort complet.".to_string() } else { "Unable to retrieve complete spell.".to_string() }))?;
|
|
||||||
|
|
||||||
let spell = statement
|
|
||||||
.query_row(params![spell_id, user_id], |query_row| {
|
|
||||||
Ok(SeriesSpellsTableResult {
|
|
||||||
spell_id: query_row.get(0)?, series_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, name: query_row.get(3)?,
|
|
||||||
name_hash: query_row.get(4)?, description: query_row.get(5)?,
|
|
||||||
appearance: query_row.get(6)?, tags: query_row.get(7)?,
|
|
||||||
power_level: query_row.get(8)?, components: query_row.get(9)?,
|
|
||||||
limitations: query_row.get(10)?, notes: query_row.get(11)?,
|
|
||||||
last_update: query_row.get(12)?,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
match spell {
|
|
||||||
Ok(spell) => Ok(Some(spell)),
|
|
||||||
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
|
||||||
Err(_) => Err(AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le sort complet.".to_string() } else { "Unable to retrieve complete spell.".to_string() })),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches a complete spell tag by ID for sync.
|
|
||||||
pub fn fetch_spell_tag_table_by_id(conn: &Connection, user_id: &str, tag_id: &str, lang: Lang) -> AppResult<Option<SeriesSpellTagsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT tag_id, series_id, user_id, name, hashed_name, color, last_update FROM series_spell_tags WHERE tag_id = ?1 AND user_id = ?2")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le tag complet.".to_string() } else { "Unable to retrieve complete tag.".to_string() }))?;
|
|
||||||
|
|
||||||
let tag = statement
|
|
||||||
.query_row(params![tag_id, user_id], |query_row| {
|
|
||||||
Ok(SeriesSpellTagsTableResult {
|
|
||||||
tag_id: query_row.get(0)?, series_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, name: query_row.get(3)?,
|
|
||||||
hashed_name: query_row.get(4)?, color: query_row.get(5)?,
|
|
||||||
last_update: query_row.get(6)?,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
match tag {
|
|
||||||
Ok(tag) => Ok(Some(tag)),
|
|
||||||
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
|
||||||
Err(_) => Err(AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le tag complet.".to_string() } else { "Unable to retrieve complete tag.".to_string() })),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if a spell tag exists.
|
/// Checks if a spell tag exists.
|
||||||
pub fn is_spell_tag_exist(conn: &Connection, user_id: &str, tag_id: &str, lang: Lang) -> AppResult<bool> {
|
pub fn is_spell_tag_exist(conn: &Connection, user_id: &str, tag_id: &str, lang: Lang) -> AppResult<bool> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
@@ -390,22 +341,6 @@ pub fn insert_sync_spell(
|
|||||||
Ok(insert_result > 0)
|
Ok(insert_result > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a series spell for sync.
|
|
||||||
pub fn update_sync_spell(
|
|
||||||
conn: &Connection, user_id: &str, spell_id: &str, name: &str, name_hash: &str, description: &str,
|
|
||||||
appearance: &str, tags: &str, power_level: Option<&str>, components: Option<&str>,
|
|
||||||
limitations: Option<&str>, notes: Option<&str>, last_update: i64, lang: Lang,
|
|
||||||
) -> AppResult<bool> {
|
|
||||||
let update_result = conn
|
|
||||||
.execute(
|
|
||||||
"UPDATE series_spells SET name = ?1, name_hash = ?2, description = ?3, appearance = ?4, tags = ?5, power_level = ?6, components = ?7, limitations = ?8, notes = ?9, last_update = ?10 WHERE spell_id = ?11 AND user_id = ?12",
|
|
||||||
params![name, name_hash, description, appearance, tags, power_level, components, limitations, notes, last_update, spell_id, user_id],
|
|
||||||
)
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le sort pour sync.".to_string() } else { "Unable to update spell for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(update_result > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts a series spell tag for sync.
|
/// Inserts a series spell tag for sync.
|
||||||
pub fn insert_sync_spell_tag(
|
pub fn insert_sync_spell_tag(
|
||||||
conn: &Connection, tag_id: &str, series_id: &str, user_id: &str, name: &str,
|
conn: &Connection, tag_id: &str, series_id: &str, user_id: &str, name: &str,
|
||||||
@@ -421,78 +356,6 @@ pub fn insert_sync_spell_tag(
|
|||||||
Ok(insert_result > 0)
|
Ok(insert_result > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a series spell tag for sync.
|
|
||||||
pub fn update_sync_spell_tag(
|
|
||||||
conn: &Connection, user_id: &str, tag_id: &str, name: &str,
|
|
||||||
hashed_name: &str, color: Option<&str>, last_update: i64, lang: Lang,
|
|
||||||
) -> AppResult<bool> {
|
|
||||||
let update_result = conn
|
|
||||||
.execute(
|
|
||||||
"UPDATE series_spell_tags SET name = ?1, hashed_name = ?2, color = ?3, last_update = ?4 WHERE tag_id = ?5 AND user_id = ?6",
|
|
||||||
params![name, hashed_name, color, last_update, tag_id, user_id],
|
|
||||||
)
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le tag pour sync.".to_string() } else { "Unable to update tag for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(update_result > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all spells for a series for sync (without user filter).
|
|
||||||
pub fn fetch_spells_table_for_sync(conn: &Connection, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesSpellsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT spell_id, series_id, user_id, name, name_hash, description, appearance, tags, power_level, components, limitations, notes, last_update FROM series_spells WHERE series_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sorts pour sync.".to_string() } else { "Unable to retrieve spells for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
let spells = statement
|
|
||||||
.query_map(params![series_id], |query_row| {
|
|
||||||
Ok(SeriesSpellsTableResult {
|
|
||||||
spell_id: query_row.get(0)?, series_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, name: query_row.get(3)?,
|
|
||||||
name_hash: query_row.get(4)?, description: query_row.get(5)?,
|
|
||||||
appearance: query_row.get(6)?, tags: query_row.get(7)?,
|
|
||||||
power_level: query_row.get(8)?, components: query_row.get(9)?,
|
|
||||||
limitations: query_row.get(10)?, notes: query_row.get(11)?,
|
|
||||||
last_update: query_row.get(12)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sorts pour sync.".to_string() } else { "Unable to retrieve spells for sync.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sorts pour sync.".to_string() } else { "Unable to retrieve spells for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(spells)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all spell tags for a series for sync (without user filter).
|
|
||||||
pub fn fetch_spell_tags_table_for_sync(conn: &Connection, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesSpellTagsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT tag_id, series_id, user_id, name, hashed_name, color, last_update FROM series_spell_tags WHERE series_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags de sort pour sync.".to_string() } else { "Unable to retrieve spell tags for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
let tags = statement
|
|
||||||
.query_map(params![series_id], |query_row| {
|
|
||||||
Ok(SeriesSpellTagsTableResult {
|
|
||||||
tag_id: query_row.get(0)?, series_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, name: query_row.get(3)?,
|
|
||||||
hashed_name: query_row.get(4)?, color: query_row.get(5)?,
|
|
||||||
last_update: query_row.get(6)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags de sort pour sync.".to_string() } else { "Unable to retrieve spell tags for sync.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags de sort pour sync.".to_string() } else { "Unable to retrieve spell tags for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all spells for a series (alias for fetch_series_spells_table).
|
|
||||||
pub fn fetch_series_spells(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesSpellsTableResult>> {
|
|
||||||
fetch_series_spells_table(conn, user_id, series_id, lang)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all spell tags for a series (alias for fetch_series_spell_tags_table).
|
|
||||||
pub fn fetch_series_spell_tags(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesSpellTagsTableResult>> {
|
|
||||||
fetch_series_spell_tags_table(conn, user_id, series_id, lang)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if a series spell exists (alias for is_spell_exist).
|
/// Checks if a series spell exists (alias for is_spell_exist).
|
||||||
pub fn series_spell_exists(conn: &Connection, user_id: &str, spell_id: &str, lang: Lang) -> AppResult<bool> {
|
pub fn series_spell_exists(conn: &Connection, user_id: &str, spell_id: &str, lang: Lang) -> AppResult<bool> {
|
||||||
is_spell_exist(conn, user_id, spell_id, lang)
|
is_spell_exist(conn, user_id, spell_id, lang)
|
||||||
@@ -503,53 +366,6 @@ pub fn series_spell_tag_exists(conn: &Connection, user_id: &str, tag_id: &str, l
|
|||||||
is_spell_tag_exist(conn, user_id, tag_id, lang)
|
is_spell_tag_exist(conn, user_id, tag_id, lang)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches a complete spell by ID for sync (array format).
|
|
||||||
pub fn fetch_complete_spell_by_id(conn: &Connection, spell_id: &str, lang: Lang) -> AppResult<Vec<SeriesSpellsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT spell_id, series_id, user_id, name, name_hash, description, appearance, tags, power_level, components, limitations, notes, last_update FROM series_spells WHERE spell_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le sort complet.".to_string() } else { "Unable to retrieve complete spell.".to_string() }))?;
|
|
||||||
|
|
||||||
let spells = statement
|
|
||||||
.query_map(params![spell_id], |query_row| {
|
|
||||||
Ok(SeriesSpellsTableResult {
|
|
||||||
spell_id: query_row.get(0)?, series_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, name: query_row.get(3)?,
|
|
||||||
name_hash: query_row.get(4)?, description: query_row.get(5)?,
|
|
||||||
appearance: query_row.get(6)?, tags: query_row.get(7)?,
|
|
||||||
power_level: query_row.get(8)?, components: query_row.get(9)?,
|
|
||||||
limitations: query_row.get(10)?, notes: query_row.get(11)?,
|
|
||||||
last_update: query_row.get(12)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le sort complet.".to_string() } else { "Unable to retrieve complete spell.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le sort complet.".to_string() } else { "Unable to retrieve complete spell.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(spells)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches a complete spell tag by ID for sync (array format).
|
|
||||||
pub fn fetch_complete_spell_tag_by_id(conn: &Connection, tag_id: &str, lang: Lang) -> AppResult<Vec<SeriesSpellTagsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT tag_id, series_id, user_id, name, hashed_name, color, last_update FROM series_spell_tags WHERE tag_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le tag complet.".to_string() } else { "Unable to retrieve complete tag.".to_string() }))?;
|
|
||||||
|
|
||||||
let tags = statement
|
|
||||||
.query_map(params![tag_id], |query_row| {
|
|
||||||
Ok(SeriesSpellTagsTableResult {
|
|
||||||
tag_id: query_row.get(0)?, series_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, name: query_row.get(3)?,
|
|
||||||
hashed_name: query_row.get(4)?, color: query_row.get(5)?,
|
|
||||||
last_update: query_row.get(6)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le tag complet.".to_string() } else { "Unable to retrieve complete tag.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le tag complet.".to_string() } else { "Unable to retrieve complete tag.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts a series spell for sync (alias with compatible signature).
|
/// Inserts a series spell for sync (alias with compatible signature).
|
||||||
pub fn insert_sync_series_spell(
|
pub fn insert_sync_series_spell(
|
||||||
conn: &Connection, spell_id: &str, series_id: &str, user_id: &str, name: &str, name_hash: &str,
|
conn: &Connection, spell_id: &str, series_id: &str, user_id: &str, name: &str, name_hash: &str,
|
||||||
|
|||||||
@@ -180,28 +180,6 @@ pub fn fetch_series_worlds_table(conn: &Connection, user_id: &str, series_id: &s
|
|||||||
Ok(worlds)
|
Ok(worlds)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches all elements for a world for sync.
|
|
||||||
pub fn fetch_series_world_elements_table(conn: &Connection, user_id: &str, world_id: &str, lang: Lang) -> AppResult<Vec<SeriesWorldElementsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT element_id, world_id, user_id, element_type, name, original_name, description, last_update FROM series_world_elements WHERE world_id = ?1 AND user_id = ?2")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de monde pour sync.".to_string() } else { "Unable to retrieve world elements for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
let elements = statement
|
|
||||||
.query_map(params![world_id, user_id], |query_row| {
|
|
||||||
Ok(SeriesWorldElementsTableResult {
|
|
||||||
element_id: query_row.get(0)?, world_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, element_type: query_row.get(3)?,
|
|
||||||
name: query_row.get(4)?, original_name: query_row.get(5)?,
|
|
||||||
description: query_row.get(6)?, last_update: query_row.get(7)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de monde pour sync.".to_string() } else { "Unable to retrieve world elements for sync.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de monde pour sync.".to_string() } else { "Unable to retrieve world elements for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(elements)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all series worlds for a user for sync comparison.
|
/// Fetches all series worlds for a user for sync comparison.
|
||||||
pub fn fetch_synced_series_worlds(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedSeriesWorldResult>> {
|
pub fn fetch_synced_series_worlds(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedSeriesWorldResult>> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
@@ -236,52 +214,6 @@ pub fn fetch_synced_series_world_elements(conn: &Connection, user_id: &str, lang
|
|||||||
Ok(elements)
|
Ok(elements)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches a complete world by ID for sync.
|
|
||||||
pub fn fetch_complete_world_by_id(conn: &Connection, world_id: &str, lang: Lang) -> AppResult<Vec<SeriesWorldsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT world_id, series_id, user_id, name, hashed_name, history, politics, economy, religion, languages, last_update FROM series_worlds WHERE world_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le monde complet.".to_string() } else { "Unable to retrieve complete world.".to_string() }))?;
|
|
||||||
|
|
||||||
let worlds = statement
|
|
||||||
.query_map(params![world_id], |query_row| {
|
|
||||||
Ok(SeriesWorldsTableResult {
|
|
||||||
world_id: query_row.get(0)?, series_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, name: query_row.get(3)?,
|
|
||||||
hashed_name: query_row.get(4)?, history: query_row.get(5)?,
|
|
||||||
politics: query_row.get(6)?, economy: query_row.get(7)?,
|
|
||||||
religion: query_row.get(8)?, languages: query_row.get(9)?,
|
|
||||||
last_update: query_row.get(10)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le monde complet.".to_string() } else { "Unable to retrieve complete world.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le monde complet.".to_string() } else { "Unable to retrieve complete world.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(worlds)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches a complete world element by ID for sync.
|
|
||||||
pub fn fetch_complete_world_element_by_id(conn: &Connection, element_id: &str, lang: Lang) -> AppResult<Vec<SeriesWorldElementsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT element_id, world_id, user_id, element_type, name, original_name, description, last_update FROM series_world_elements WHERE element_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer l'élément de monde complet.".to_string() } else { "Unable to retrieve complete world element.".to_string() }))?;
|
|
||||||
|
|
||||||
let elements = statement
|
|
||||||
.query_map(params![element_id], |query_row| {
|
|
||||||
Ok(SeriesWorldElementsTableResult {
|
|
||||||
element_id: query_row.get(0)?, world_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, element_type: query_row.get(3)?,
|
|
||||||
name: query_row.get(4)?, original_name: query_row.get(5)?,
|
|
||||||
description: query_row.get(6)?, last_update: query_row.get(7)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer l'élément de monde complet.".to_string() } else { "Unable to retrieve complete world element.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer l'élément de monde complet.".to_string() } else { "Unable to retrieve complete world element.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(elements)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if a world exists.
|
/// Checks if a world exists.
|
||||||
pub fn is_world_exist(conn: &Connection, user_id: &str, world_id: &str, lang: Lang) -> AppResult<bool> {
|
pub fn is_world_exist(conn: &Connection, user_id: &str, world_id: &str, lang: Lang) -> AppResult<bool> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
@@ -324,22 +256,6 @@ pub fn insert_sync_world(
|
|||||||
Ok(insert_result > 0)
|
Ok(insert_result > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a series world for sync.
|
|
||||||
pub fn update_sync_world(
|
|
||||||
conn: &Connection, user_id: &str, world_id: &str, name: &str, hashed_name: &str,
|
|
||||||
history: Option<&str>, politics: Option<&str>, economy: Option<&str>, religion: Option<&str>,
|
|
||||||
languages: Option<&str>, last_update: i64, lang: Lang,
|
|
||||||
) -> AppResult<bool> {
|
|
||||||
let update_result = conn
|
|
||||||
.execute(
|
|
||||||
"UPDATE series_worlds SET name = ?1, hashed_name = ?2, history = ?3, politics = ?4, economy = ?5, religion = ?6, languages = ?7, last_update = ?8 WHERE world_id = ?9 AND user_id = ?10",
|
|
||||||
params![name, hashed_name, history, politics, economy, religion, languages, last_update, world_id, user_id],
|
|
||||||
)
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le monde pour sync.".to_string() } else { "Unable to update world for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(update_result > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts a series world element for sync.
|
/// Inserts a series world element for sync.
|
||||||
pub fn insert_sync_world_element(
|
pub fn insert_sync_world_element(
|
||||||
conn: &Connection, element_id: &str, world_id: &str, user_id: &str, element_type: i64,
|
conn: &Connection, element_id: &str, world_id: &str, user_id: &str, element_type: i64,
|
||||||
@@ -355,72 +271,6 @@ pub fn insert_sync_world_element(
|
|||||||
Ok(insert_result > 0)
|
Ok(insert_result > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a series world element for sync.
|
|
||||||
pub fn update_sync_world_element(
|
|
||||||
conn: &Connection, user_id: &str, element_id: &str, element_type: i64, name: &str,
|
|
||||||
original_name: &str, description: Option<&str>, last_update: i64, lang: Lang,
|
|
||||||
) -> AppResult<bool> {
|
|
||||||
let update_result = conn
|
|
||||||
.execute(
|
|
||||||
"UPDATE series_world_elements SET element_type = ?1, name = ?2, original_name = ?3, description = ?4, last_update = ?5 WHERE element_id = ?6 AND user_id = ?7",
|
|
||||||
params![element_type, name, original_name, description, last_update, element_id, user_id],
|
|
||||||
)
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour l'élément de monde pour sync.".to_string() } else { "Unable to update world element for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(update_result > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all worlds for a series for sync (without user filter).
|
|
||||||
pub fn fetch_worlds_table_for_sync(conn: &Connection, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesWorldsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT world_id, series_id, user_id, name, hashed_name, history, politics, economy, religion, languages, last_update FROM series_worlds WHERE series_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les mondes pour sync.".to_string() } else { "Unable to retrieve worlds for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
let worlds = statement
|
|
||||||
.query_map(params![series_id], |query_row| {
|
|
||||||
Ok(SeriesWorldsTableResult {
|
|
||||||
world_id: query_row.get(0)?, series_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, name: query_row.get(3)?,
|
|
||||||
hashed_name: query_row.get(4)?, history: query_row.get(5)?,
|
|
||||||
politics: query_row.get(6)?, economy: query_row.get(7)?,
|
|
||||||
religion: query_row.get(8)?, languages: query_row.get(9)?,
|
|
||||||
last_update: query_row.get(10)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les mondes pour sync.".to_string() } else { "Unable to retrieve worlds for sync.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les mondes pour sync.".to_string() } else { "Unable to retrieve worlds for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(worlds)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all world elements for a series for sync (via series_worlds join).
|
|
||||||
pub fn fetch_world_elements_table_for_sync(conn: &Connection, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesWorldElementsTableResult>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT swe.element_id, swe.world_id, swe.user_id, swe.element_type, swe.name, swe.original_name, swe.description, swe.last_update FROM series_world_elements swe INNER JOIN series_worlds sw ON swe.world_id = sw.world_id WHERE sw.series_id = ?1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de monde pour sync.".to_string() } else { "Unable to retrieve world elements for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
let elements = statement
|
|
||||||
.query_map(params![series_id], |query_row| {
|
|
||||||
Ok(SeriesWorldElementsTableResult {
|
|
||||||
element_id: query_row.get(0)?, world_id: query_row.get(1)?,
|
|
||||||
user_id: query_row.get(2)?, element_type: query_row.get(3)?,
|
|
||||||
name: query_row.get(4)?, original_name: query_row.get(5)?,
|
|
||||||
description: query_row.get(6)?, last_update: query_row.get(7)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de monde pour sync.".to_string() } else { "Unable to retrieve world elements for sync.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de monde pour sync.".to_string() } else { "Unable to retrieve world elements for sync.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(elements)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all worlds for a series (alias for fetch_series_worlds_table).
|
|
||||||
pub fn fetch_series_worlds(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesWorldsTableResult>> {
|
|
||||||
fetch_series_worlds_table(conn, user_id, series_id, lang)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches all world elements for a series by series ID.
|
/// Fetches all world elements for a series by series ID.
|
||||||
pub fn fetch_series_world_elements_by_series_id(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesWorldElementsTableResult>> {
|
pub fn fetch_series_world_elements_by_series_id(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesWorldElementsTableResult>> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ fn get_session(session: &State<SessionState>) -> Result<(String, Lang), AppError
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GetSpellListData {
|
pub struct GetSpellListData {
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
pub enabled: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::shared::types::Lang;
|
|||||||
|
|
||||||
pub struct SpellResult {
|
pub struct SpellResult {
|
||||||
pub spell_id: String,
|
pub spell_id: String,
|
||||||
pub book_id: String,
|
pub _book_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub appearance: Option<String>,
|
pub appearance: Option<String>,
|
||||||
@@ -54,7 +54,7 @@ pub fn fetch_spells(conn: &Connection, user_id: &str, book_id: &str, lang: Lang)
|
|||||||
let spells = statement
|
let spells = statement
|
||||||
.query_map(params![user_id, book_id], |query_row| {
|
.query_map(params![user_id, book_id], |query_row| {
|
||||||
Ok(SpellResult {
|
Ok(SpellResult {
|
||||||
spell_id: query_row.get(0)?, book_id: query_row.get(1)?,
|
spell_id: query_row.get(0)?, _book_id: query_row.get(1)?,
|
||||||
name: query_row.get(2)?, description: query_row.get(3)?,
|
name: query_row.get(2)?, description: query_row.get(3)?,
|
||||||
appearance: query_row.get(4)?, tags: query_row.get(5)?,
|
appearance: query_row.get(4)?, tags: query_row.get(5)?,
|
||||||
power_level: query_row.get(6)?, components: query_row.get(7)?,
|
power_level: query_row.get(6)?, components: query_row.get(7)?,
|
||||||
@@ -83,7 +83,7 @@ pub fn fetch_spell_by_id(conn: &Connection, user_id: &str, spell_id: &str, lang:
|
|||||||
let spells = statement
|
let spells = statement
|
||||||
.query_map(params![user_id, spell_id], |query_row| {
|
.query_map(params![user_id, spell_id], |query_row| {
|
||||||
Ok(SpellResult {
|
Ok(SpellResult {
|
||||||
spell_id: query_row.get(0)?, book_id: query_row.get(1)?,
|
spell_id: query_row.get(0)?, _book_id: query_row.get(1)?,
|
||||||
name: query_row.get(2)?, description: query_row.get(3)?,
|
name: query_row.get(2)?, description: query_row.get(3)?,
|
||||||
appearance: query_row.get(4)?, tags: query_row.get(5)?,
|
appearance: query_row.get(4)?, tags: query_row.get(5)?,
|
||||||
power_level: query_row.get(6)?, components: query_row.get(7)?,
|
power_level: query_row.get(6)?, components: query_row.get(7)?,
|
||||||
|
|||||||
@@ -52,18 +52,6 @@ pub struct SpellListResponse {
|
|||||||
pub tags: Vec<SpellTagProps>,
|
pub tags: Vec<SpellTagProps>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SyncedSpell {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SyncedSpellTag {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves all spell tags for a specific book.
|
/// Retrieves all spell tags for a specific book.
|
||||||
/// * `conn` - Database connection
|
/// * `conn` - Database connection
|
||||||
/// * `user_id` - The unique identifier of the user
|
/// * `user_id` - The unique identifier of the user
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::shared::types::Lang;
|
|||||||
|
|
||||||
pub struct SpellTagResult {
|
pub struct SpellTagResult {
|
||||||
pub tag_id: String,
|
pub tag_id: String,
|
||||||
pub book_id: String,
|
pub _book_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub color: Option<String>,
|
pub color: Option<String>,
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@ pub fn fetch_spell_tags(conn: &Connection, user_id: &str, book_id: &str, lang: L
|
|||||||
let rows = statement
|
let rows = statement
|
||||||
.query_map(params![user_id, book_id], |query_row| {
|
.query_map(params![user_id, book_id], |query_row| {
|
||||||
Ok(SpellTagResult {
|
Ok(SpellTagResult {
|
||||||
tag_id: query_row.get(0)?, book_id: query_row.get(1)?,
|
tag_id: query_row.get(0)?, _book_id: query_row.get(1)?,
|
||||||
name: query_row.get(2)?, color: query_row.get(3)?,
|
name: query_row.get(2)?, color: query_row.get(3)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -143,30 +143,6 @@ pub struct SyncedBookFull {
|
|||||||
pub spell_tags: Vec<SyncedSpellTag>,
|
pub spell_tags: Vec<SyncedSpellTag>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct SyncCharacterData {
|
|
||||||
pub first_name: String,
|
|
||||||
pub last_name: Option<String>,
|
|
||||||
pub nickname: Option<String>,
|
|
||||||
pub age: Option<String>,
|
|
||||||
pub gender: Option<String>,
|
|
||||||
pub species: Option<String>,
|
|
||||||
pub nationality: Option<String>,
|
|
||||||
pub status: Option<String>,
|
|
||||||
pub category: String,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub image: Option<String>,
|
|
||||||
pub role: Option<String>,
|
|
||||||
pub biography: Option<String>,
|
|
||||||
pub history: Option<String>,
|
|
||||||
pub speech_pattern: Option<String>,
|
|
||||||
pub catchphrase: Option<String>,
|
|
||||||
pub residence: Option<String>,
|
|
||||||
pub notes: Option<String>,
|
|
||||||
pub color: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves a complete book with all its associated entities for synchronization.
|
/// Retrieves a complete book with all its associated entities for synchronization.
|
||||||
/// Decrypts all encrypted fields using the user's encryption key.
|
/// Decrypts all encrypted fields using the user's encryption key.
|
||||||
/// * `conn` - Database connection
|
/// * `conn` - Database connection
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod repo;
|
pub mod repo;
|
||||||
pub mod service;
|
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ use crate::error::{AppError, AppResult};
|
|||||||
use crate::shared::types::Lang;
|
use crate::shared::types::Lang;
|
||||||
|
|
||||||
pub struct RemovedItemRecord {
|
pub struct RemovedItemRecord {
|
||||||
pub removal_id: String,
|
pub _removal_id: String,
|
||||||
pub table_name: String,
|
pub table_name: String,
|
||||||
pub entity_id: String,
|
pub entity_id: String,
|
||||||
pub book_id: Option<String>,
|
pub book_id: Option<String>,
|
||||||
pub user_id: String,
|
pub _user_id: String,
|
||||||
pub deleted_at: i64,
|
pub deleted_at: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,9 +51,9 @@ pub fn get_deletions_since(conn: &Connection, user_id: &str, since: i64, lang: L
|
|||||||
let records = statement
|
let records = statement
|
||||||
.query_map(params![user_id, since], |query_row| {
|
.query_map(params![user_id, since], |query_row| {
|
||||||
Ok(RemovedItemRecord {
|
Ok(RemovedItemRecord {
|
||||||
removal_id: query_row.get(0)?, table_name: query_row.get(1)?,
|
_removal_id: query_row.get(0)?, table_name: query_row.get(1)?,
|
||||||
entity_id: query_row.get(2)?, book_id: query_row.get(3)?,
|
entity_id: query_row.get(2)?, book_id: query_row.get(3)?,
|
||||||
user_id: query_row.get(4)?, deleted_at: query_row.get(5)?,
|
_user_id: query_row.get(4)?, deleted_at: query_row.get(5)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les suppressions.".to_string() } else { "Unable to retrieve deletions.".to_string() }))?
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les suppressions.".to_string() } else { "Unable to retrieve deletions.".to_string() }))?
|
||||||
@@ -63,63 +63,3 @@ pub fn get_deletions_since(conn: &Connection, user_id: &str, since: i64, lang: L
|
|||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if an entity was previously deleted.
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `table_name` - The table name
|
|
||||||
/// * `entity_id` - The entity ID
|
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns true if the entity was deleted locally.
|
|
||||||
pub fn was_deleted(conn: &Connection, table_name: &str, entity_id: &str, lang: Lang) -> AppResult<bool> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT 1 FROM removed_items WHERE table_name = ?1 AND entity_id = ?2 LIMIT 1")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier si l'élément a été supprimé.".to_string() } else { "Unable to check if item was deleted.".to_string() }))?;
|
|
||||||
|
|
||||||
let exists = statement
|
|
||||||
.query_row(params![table_name, entity_id], |_query_row| Ok(true))
|
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
Ok(exists)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves all tracked deletions for a specific book.
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `user_id` - The user ID
|
|
||||||
/// * `book_id` - The book ID
|
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns array of removed item records for that book.
|
|
||||||
pub fn get_deletions_for_book(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<RemovedItemRecord>> {
|
|
||||||
let mut statement = conn
|
|
||||||
.prepare("SELECT removal_id, table_name, entity_id, book_id, user_id, deleted_at FROM removed_items WHERE user_id = ?1 AND book_id = ?2")
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les suppressions pour ce livre.".to_string() } else { "Unable to retrieve deletions for this book.".to_string() }))?;
|
|
||||||
|
|
||||||
let records = statement
|
|
||||||
.query_map(params![user_id, book_id], |query_row| {
|
|
||||||
Ok(RemovedItemRecord {
|
|
||||||
removal_id: query_row.get(0)?, table_name: query_row.get(1)?,
|
|
||||||
entity_id: query_row.get(2)?, book_id: query_row.get(3)?,
|
|
||||||
user_id: query_row.get(4)?, deleted_at: query_row.get(5)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les suppressions pour ce livre.".to_string() } else { "Unable to retrieve deletions for this book.".to_string() }))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les suppressions pour ce livre.".to_string() } else { "Unable to retrieve deletions for this book.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(records)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clears all deletion records for a user.
|
|
||||||
/// WARNING: Only use this when wiping user data completely.
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `user_id` - The user ID
|
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns true if cleared successfully.
|
|
||||||
pub fn clear_all_for_user(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<bool> {
|
|
||||||
let delete_result = conn
|
|
||||||
.execute(
|
|
||||||
"DELETE FROM removed_items WHERE user_id = ?1",
|
|
||||||
params![user_id],
|
|
||||||
)
|
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer les enregistrements de suppression.".to_string() } else { "Unable to clear deletion records.".to_string() }))?;
|
|
||||||
|
|
||||||
Ok(delete_result > 0)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
use rusqlite::Connection;
|
|
||||||
|
|
||||||
use crate::domains::tombstone::repo;
|
|
||||||
use crate::error::AppResult;
|
|
||||||
use crate::helpers::create_unique_id;
|
|
||||||
use crate::shared::types::Lang;
|
|
||||||
|
|
||||||
/// Records a deleted item for sync tracking.
|
|
||||||
/// Must be called BEFORE the actual deletion from the source table.
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `user_id` - The unique identifier of the user
|
|
||||||
/// * `book_id` - The book ID (None for series items)
|
|
||||||
/// * `table_name` - The name of the table from which the item is deleted
|
|
||||||
/// * `entity_id` - The UUID of the deleted entity
|
|
||||||
/// * `deleted_at` - The timestamp of deletion (from UI via timestamp_in_seconds())
|
|
||||||
/// * `lang` - The language for error messages ("fr" or "en")
|
|
||||||
/// Returns true if the record was inserted successfully.
|
|
||||||
pub fn delete_tracker(
|
|
||||||
conn: &Connection, user_id: &str, book_id: Option<&str>, table_name: &str,
|
|
||||||
entity_id: &str, deleted_at: i64, lang: Lang,
|
|
||||||
) -> AppResult<bool> {
|
|
||||||
let removal_id: String = create_unique_id(None);
|
|
||||||
|
|
||||||
repo::insert(conn, &removal_id, table_name, entity_id, book_id, user_id, deleted_at, lang)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ use crate::domains::user::service;
|
|||||||
use crate::error::AppError;
|
use crate::error::AppError;
|
||||||
use crate::shared::session::SessionState;
|
use crate::shared::session::SessionState;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct UpdateUserData {
|
||||||
|
pub username: String,
|
||||||
|
pub email: String,
|
||||||
|
pub author_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct InitUserData {
|
pub struct InitUserData {
|
||||||
@@ -25,8 +33,6 @@ pub struct InitUserResult {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SyncUserData {
|
pub struct SyncUserData {
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
pub first_name: String,
|
|
||||||
pub last_name: String,
|
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
}
|
}
|
||||||
@@ -35,14 +41,10 @@ pub struct SyncUserData {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UserInfoResponse {
|
pub struct UserInfoResponse {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
|
||||||
pub last_name: String,
|
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub account_verified: bool,
|
|
||||||
pub author_name: String,
|
pub author_name: String,
|
||||||
pub group_id: i64,
|
pub group_id: i64,
|
||||||
pub terms_accepted: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -62,6 +64,11 @@ pub fn init_user(data: InitUserData, db: State<DbManager>, session: State<Sessio
|
|||||||
|
|
||||||
key_manager::set_last_user_id(&data.user_id)?;
|
key_manager::set_last_user_id(&data.user_id)?;
|
||||||
|
|
||||||
|
if key_manager::get_user_encryption_key(&data.user_id).is_err() {
|
||||||
|
let encryption_key = encryption::generate_user_encryption_key(&data.user_id)?;
|
||||||
|
key_manager::set_user_encryption_key(&data.user_id, &encryption_key)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(InitUserResult { success: true, error: None })
|
Ok(InitUserResult { success: true, error: None })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,14 +146,10 @@ pub fn get_user_info(db: State<DbManager>, session: State<SessionState>) -> Resu
|
|||||||
|
|
||||||
Ok(UserInfoResponse {
|
Ok(UserInfoResponse {
|
||||||
id: user_info.id,
|
id: user_info.id,
|
||||||
name: user_info.name,
|
|
||||||
last_name: user_info.last_name,
|
|
||||||
username: user_info.username,
|
username: user_info.username,
|
||||||
email: user_info.email,
|
email: user_info.email,
|
||||||
account_verified: user_info.account_verified,
|
|
||||||
author_name: user_info.author_name,
|
author_name: user_info.author_name,
|
||||||
group_id: user_info.group_id,
|
group_id: user_info.group_id,
|
||||||
terms_accepted: user_info.terms_accepted,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,6 +163,20 @@ pub fn sync_user(data: SyncUserData, db: State<DbManager>, session: State<Sessio
|
|||||||
db_manager.initialize(&data.user_id)?;
|
db_manager.initialize(&data.user_id)?;
|
||||||
let conn = db_manager.get_connection(&data.user_id)?;
|
let conn = db_manager.get_connection(&data.user_id)?;
|
||||||
|
|
||||||
service::add_user(conn, &data.user_id, &data.first_name, &data.last_name, &data.username, &data.email, "", lang)?;
|
service::add_user(conn, &data.user_id, &data.username, &data.email, lang)?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn update_user_info(data: UpdateUserData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
|
||||||
|
let session_guard = session.lock().map_err(|e| AppError::Internal(format!("Session lock failed: {}", e)))?;
|
||||||
|
let user_id = session_guard.get_user_id().map_err(|e| AppError::Auth(e))?.to_string();
|
||||||
|
let lang = session_guard.lang;
|
||||||
|
drop(session_guard);
|
||||||
|
|
||||||
|
let user_key = key_manager::get_user_encryption_key(&user_id)?;
|
||||||
|
let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
|
||||||
|
let conn = db_manager.get_connection(&user_id)?;
|
||||||
|
|
||||||
|
service::update_user_infos(conn, &user_key, &user_id, &data.username, &data.email, data.author_name.as_deref(), lang)
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,45 +4,18 @@ use crate::error::{AppError, AppResult};
|
|||||||
use crate::shared::types::Lang;
|
use crate::shared::types::Lang;
|
||||||
|
|
||||||
pub struct UserInfosQueryResponse {
|
pub struct UserInfosQueryResponse {
|
||||||
pub first_name: String,
|
|
||||||
pub last_name: String,
|
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub plateform: String,
|
|
||||||
pub term_accepted: i64,
|
|
||||||
pub account_verified: i64,
|
|
||||||
pub author_name: Option<String>,
|
pub author_name: Option<String>,
|
||||||
pub rite_points: i64,
|
|
||||||
pub user_group: i64,
|
pub user_group: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UserAccountQuery {
|
pub struct UserAccountQuery {
|
||||||
pub first_name: Option<String>,
|
|
||||||
pub last_name: Option<String>,
|
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub author_name: Option<String>,
|
pub author_name: Option<String>,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CredentialResponse {
|
|
||||||
pub valid: bool,
|
|
||||||
pub message: Option<String>,
|
|
||||||
pub user: Option<UserResponse>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UserResponse {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_name: String,
|
|
||||||
pub username: String,
|
|
||||||
pub email: String,
|
|
||||||
pub account_verified: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GuideTourResult {
|
|
||||||
pub step_tour: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts a new user into the database.
|
/// Inserts a new user into the database.
|
||||||
/// * `conn` - Database connection
|
/// * `conn` - Database connection
|
||||||
/// * `user_id` - The unique identifier for the user
|
/// * `user_id` - The unique identifier for the user
|
||||||
@@ -56,21 +29,18 @@ pub struct GuideTourResult {
|
|||||||
/// Returns the user's UUID if insertion was successful.
|
/// Returns the user's UUID if insertion was successful.
|
||||||
/// Errors if the user cannot be registered.
|
/// Errors if the user cannot be registered.
|
||||||
pub fn insert_user(
|
pub fn insert_user(
|
||||||
conn: &Connection, user_id: &str, first_name: &str, last_name: &str, username: &str,
|
conn: &Connection, user_id: &str, username: &str, origin_username: &str,
|
||||||
origin_username: &str, email: &str, origin_email: &str, lang: Lang,
|
email: &str, origin_email: &str, lang: Lang,
|
||||||
) -> AppResult<String> {
|
) -> AppResult<String> {
|
||||||
let reg_date = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|duration| duration.as_millis() as i64).unwrap_or(0);
|
|
||||||
|
|
||||||
// Try INSERT first; if user already exists, UPDATE their info instead
|
|
||||||
let insert_result = conn.execute(
|
let insert_result = conn.execute(
|
||||||
"INSERT OR IGNORE INTO erit_users (user_id, first_name, last_name, username, email, origin_email, origin_username, plateform, term_accepted, account_verified, reg_date) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11)",
|
"INSERT OR IGNORE INTO erit_users (user_id, username, email, origin_email, origin_username) VALUES (?1,?2,?3,?4,?5)",
|
||||||
params![user_id, first_name, last_name, username, email, origin_email, origin_username, "desktop", 0, 1, reg_date],
|
params![user_id, username, email, origin_email, origin_username],
|
||||||
).map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'enregistrer l'utilisateur.".to_string() } else { "Unable to register user.".to_string() }))?;
|
).map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'enregistrer l'utilisateur.".to_string() } else { "Unable to register user.".to_string() }))?;
|
||||||
|
|
||||||
if insert_result == 0 {
|
if insert_result == 0 {
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"UPDATE erit_users SET first_name = ?1, last_name = ?2, username = ?3, email = ?4, origin_email = ?5, origin_username = ?6 WHERE user_id = ?7",
|
"UPDATE erit_users SET username = ?1, email = ?2, origin_email = ?3, origin_username = ?4 WHERE user_id = ?5",
|
||||||
params![first_name, last_name, username, email, origin_email, origin_username, user_id],
|
params![username, email, origin_email, origin_username, user_id],
|
||||||
).map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour l'utilisateur.".to_string() } else { "Unable to update user.".to_string() }))?;
|
).map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour l'utilisateur.".to_string() } else { "Unable to update user.".to_string() }))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,17 +55,14 @@ pub fn insert_user(
|
|||||||
/// Errors if the user is not found or cannot be retrieved.
|
/// Errors if the user is not found or cannot be retrieved.
|
||||||
pub fn fetch_user_infos(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<UserInfosQueryResponse> {
|
pub fn fetch_user_infos(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<UserInfosQueryResponse> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
.prepare("SELECT first_name, last_name, username, email, plateform, term_accepted, account_verified, author_name, erite_points AS rite_points, user_group FROM erit_users WHERE user_id = ?1")
|
.prepare("SELECT username, email, author_name, user_group FROM erit_users WHERE user_id = ?1")
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les informations utilisateur.".to_string() } else { "Unable to retrieve user information.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les informations utilisateur.".to_string() } else { "Unable to retrieve user information.".to_string() }))?;
|
||||||
|
|
||||||
let user_info = statement
|
let user_info = statement
|
||||||
.query_row(params![user_id], |query_row| {
|
.query_row(params![user_id], |query_row| {
|
||||||
Ok(UserInfosQueryResponse {
|
Ok(UserInfosQueryResponse {
|
||||||
first_name: query_row.get(0)?, last_name: query_row.get(1)?,
|
username: query_row.get(0)?, email: query_row.get(1)?,
|
||||||
username: query_row.get(2)?, email: query_row.get(3)?,
|
author_name: query_row.get(2)?, user_group: query_row.get(3)?,
|
||||||
plateform: query_row.get(4)?, term_accepted: query_row.get(5)?,
|
|
||||||
account_verified: query_row.get(6)?, author_name: query_row.get(7)?,
|
|
||||||
rite_points: query_row.get(8)?, user_group: query_row.get(9)?,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|error| match error {
|
.map_err(|error| match error {
|
||||||
@@ -121,13 +88,13 @@ pub fn fetch_user_infos(conn: &Connection, user_id: &str, lang: Lang) -> AppResu
|
|||||||
/// Returns true if the update was successful, false otherwise.
|
/// Returns true if the update was successful, false otherwise.
|
||||||
/// Errors if the update fails.
|
/// Errors if the update fails.
|
||||||
pub fn update_user_infos(
|
pub fn update_user_infos(
|
||||||
conn: &Connection, user_id: &str, first_name: &str, last_name: &str, username: &str,
|
conn: &Connection, user_id: &str, username: &str, origin_username: &str,
|
||||||
origin_username: &str, email: &str, origin_email: &str, original_author_name: &str, author_name: &str, lang: Lang,
|
email: &str, origin_email: &str, original_author_name: &str, author_name: &str, lang: Lang,
|
||||||
) -> AppResult<bool> {
|
) -> AppResult<bool> {
|
||||||
let update_result = conn
|
let update_result = conn
|
||||||
.execute(
|
.execute(
|
||||||
"UPDATE erit_users SET first_name = ?1, last_name = ?2, username = ?3, email = ?4, origin_username = ?5, origin_author_name = ?6, author_name = ?7 WHERE user_id = ?8 AND origin_email = ?9",
|
"UPDATE erit_users SET username = ?1, email = ?2, origin_username = ?3, origin_author_name = ?4, author_name = ?5 WHERE user_id = ?6 AND origin_email = ?7",
|
||||||
params![first_name, last_name, username, email, origin_username, original_author_name, author_name, user_id, origin_email],
|
params![username, email, origin_username, original_author_name, author_name, user_id, origin_email],
|
||||||
)
|
)
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour les informations utilisateur.".to_string() } else { "Unable to update user information.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour les informations utilisateur.".to_string() } else { "Unable to update user information.".to_string() }))?;
|
||||||
|
|
||||||
@@ -142,12 +109,12 @@ pub fn update_user_infos(
|
|||||||
/// Errors if the account is not found or cannot be retrieved.
|
/// Errors if the account is not found or cannot be retrieved.
|
||||||
pub fn fetch_account_information(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<UserAccountQuery> {
|
pub fn fetch_account_information(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<UserAccountQuery> {
|
||||||
let mut statement = conn
|
let mut statement = conn
|
||||||
.prepare("SELECT first_name, last_name, username, author_name, email FROM erit_users WHERE user_id = ?1")
|
.prepare("SELECT username, author_name, email FROM erit_users WHERE user_id = ?1")
|
||||||
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les informations du compte.".to_string() } else { "Unable to retrieve account information.".to_string() }))?;
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les informations du compte.".to_string() } else { "Unable to retrieve account information.".to_string() }))?;
|
||||||
|
|
||||||
let account_info = statement
|
let account_info = statement
|
||||||
.query_row(params![user_id], |query_row| {
|
.query_row(params![user_id], |query_row| {
|
||||||
Ok(UserAccountQuery { first_name: query_row.get(0)?, last_name: query_row.get(1)?, username: query_row.get(2)?, author_name: query_row.get(3)?, email: query_row.get(4)? })
|
Ok(UserAccountQuery { username: query_row.get(0)?, author_name: query_row.get(1)?, email: query_row.get(2)? })
|
||||||
})
|
})
|
||||||
.map_err(|error| match error {
|
.map_err(|error| match error {
|
||||||
rusqlite::Error::QueryReturnedNoRows => AppError::NotFound(if lang == Lang::Fr { "Compte non trouvé.".to_string() } else { "Account not found.".to_string() }),
|
rusqlite::Error::QueryReturnedNoRows => AppError::NotFound(if lang == Lang::Fr { "Compte non trouvé.".to_string() } else { "Account not found.".to_string() }),
|
||||||
|
|||||||
@@ -6,116 +6,46 @@ use crate::domains::user::repo;
|
|||||||
use crate::error::AppResult;
|
use crate::error::AppResult;
|
||||||
use crate::shared::types::Lang;
|
use crate::shared::types::Lang;
|
||||||
|
|
||||||
pub struct UserAccount {
|
|
||||||
pub first_name: String,
|
|
||||||
pub last_name: String,
|
|
||||||
pub username: String,
|
|
||||||
pub author_name: String,
|
|
||||||
pub email: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GuideTour {
|
|
||||||
pub key: String,
|
|
||||||
pub value: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BookSummary {
|
|
||||||
pub book_id: String,
|
|
||||||
pub title: String,
|
|
||||||
pub sub_title: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UserInfoResponse {
|
pub struct UserInfoResponse {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
|
||||||
pub last_name: String,
|
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub account_verified: bool,
|
|
||||||
pub author_name: String,
|
pub author_name: String,
|
||||||
pub group_id: i64,
|
pub group_id: i64,
|
||||||
pub terms_accepted: bool,
|
|
||||||
pub guide_tour: Vec<GuideTour>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves complete user information including associated books.
|
/// Retrieves complete user information including associated books.
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `user_id` - The unique identifier of the user to fetch
|
|
||||||
/// * `lang` - The language for error messages
|
|
||||||
/// Returns the complete user information response.
|
|
||||||
/// Errors if the user is not found or decryption fails.
|
|
||||||
pub fn return_user_infos(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<UserInfoResponse> {
|
pub fn return_user_infos(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<UserInfoResponse> {
|
||||||
let user_infos_data: repo::UserInfosQueryResponse = repo::fetch_user_infos(conn, user_id, lang)?;
|
let user_infos_data: repo::UserInfosQueryResponse = repo::fetch_user_infos(conn, user_id, lang)?;
|
||||||
let user_encryption_key: String = get_user_encryption_key(user_id)?;
|
let user_encryption_key: String = get_user_encryption_key(user_id)?;
|
||||||
|
|
||||||
let first_name: String = decrypt_data_with_user_key(&user_infos_data.first_name, &user_encryption_key)?;
|
|
||||||
let last_name: String = decrypt_data_with_user_key(&user_infos_data.last_name, &user_encryption_key)?;
|
|
||||||
let username: String = decrypt_data_with_user_key(&user_infos_data.username, &user_encryption_key)?;
|
let username: String = decrypt_data_with_user_key(&user_infos_data.username, &user_encryption_key)?;
|
||||||
let email: String = decrypt_data_with_user_key(&user_infos_data.email, &user_encryption_key)?;
|
let email: String = decrypt_data_with_user_key(&user_infos_data.email, &user_encryption_key)?;
|
||||||
let account_verified: bool = user_infos_data.account_verified == 1;
|
|
||||||
let author_name: String = if let Some(ref author_name_val) = user_infos_data.author_name { decrypt_data_with_user_key(author_name_val, &user_encryption_key)? } else { String::new() };
|
let author_name: String = if let Some(ref author_name_val) = user_infos_data.author_name { decrypt_data_with_user_key(author_name_val, &user_encryption_key)? } else { String::new() };
|
||||||
let group_id: i64 = user_infos_data.user_group;
|
|
||||||
let terms_accepted: bool = user_infos_data.term_accepted == 1;
|
|
||||||
let guide_tour_status: Vec<GuideTour> = vec![];
|
|
||||||
|
|
||||||
Ok(UserInfoResponse {
|
Ok(UserInfoResponse {
|
||||||
id: user_id.to_string(),
|
id: user_id.to_string(),
|
||||||
name: first_name,
|
username, email, author_name,
|
||||||
last_name,
|
group_id: user_infos_data.user_group,
|
||||||
username,
|
|
||||||
email,
|
|
||||||
account_verified,
|
|
||||||
author_name,
|
|
||||||
group_id,
|
|
||||||
terms_accepted,
|
|
||||||
guide_tour: guide_tour_status,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new user in the database with encrypted personal information.
|
/// Creates a new user in the database with encrypted personal information.
|
||||||
/// * `conn` - Database connection
|
pub fn add_user(conn: &Connection, user_id: &str, username: &str, email: &str, lang: Lang) -> AppResult<String> {
|
||||||
/// * `user_id` - The unique identifier for the new user
|
|
||||||
/// * `first_name` - The user's first name (will be encrypted)
|
|
||||||
/// * `last_name` - The user's last name (will be encrypted)
|
|
||||||
/// * `username` - The user's username (will be encrypted and hashed)
|
|
||||||
/// * `email` - The user's email address (will be encrypted and hashed)
|
|
||||||
/// * `not_encrypt_password` - The user's password in plain text (unused in current implementation)
|
|
||||||
/// * `lang` - The preferred language for the user
|
|
||||||
/// Returns the created user's identifier.
|
|
||||||
/// Errors if encryption or insertion fails.
|
|
||||||
pub fn add_user(
|
|
||||||
conn: &Connection, user_id: &str, first_name: &str, last_name: &str, username: &str,
|
|
||||||
email: &str, _not_encrypt_password: &str, lang: Lang,
|
|
||||||
) -> AppResult<String> {
|
|
||||||
let user_encryption_key: String = get_user_encryption_key(user_id)?;
|
let user_encryption_key: String = get_user_encryption_key(user_id)?;
|
||||||
let encrypted_first_name: String = encrypt_data_with_user_key(first_name, &user_encryption_key)?;
|
|
||||||
let encrypted_last_name: String = encrypt_data_with_user_key(last_name, &user_encryption_key)?;
|
|
||||||
let encrypted_username: String = encrypt_data_with_user_key(username, &user_encryption_key)?;
|
let encrypted_username: String = encrypt_data_with_user_key(username, &user_encryption_key)?;
|
||||||
let encrypted_email: String = encrypt_data_with_user_key(email, &user_encryption_key)?;
|
let encrypted_email: String = encrypt_data_with_user_key(email, &user_encryption_key)?;
|
||||||
let hashed_email: String = hash_element(email);
|
let hashed_email: String = hash_element(email);
|
||||||
let hashed_username: String = hash_element(username);
|
let hashed_username: String = hash_element(username);
|
||||||
|
|
||||||
repo::insert_user(conn, user_id, &encrypted_first_name, &encrypted_last_name, &encrypted_username, &hashed_username, &encrypted_email, &hashed_email, lang)
|
repo::insert_user(conn, user_id, &encrypted_username, &hashed_username, &encrypted_email, &hashed_email, lang)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates an existing user's profile information in the database.
|
/// Updates an existing user's profile information in the database.
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `user_key` - The encryption key for the user's data
|
|
||||||
/// * `user_id` - The unique identifier of the user to update
|
|
||||||
/// * `first_name` - The updated first name (will be encrypted)
|
|
||||||
/// * `last_name` - The updated last name (will be encrypted)
|
|
||||||
/// * `username` - The updated username (will be encrypted and hashed)
|
|
||||||
/// * `email` - The updated email address (will be encrypted and hashed)
|
|
||||||
/// * `author_name` - The optional author/pen name (will be encrypted and hashed if provided)
|
|
||||||
/// * `lang` - The preferred language for the user
|
|
||||||
/// Returns true if the update was successful.
|
|
||||||
/// Errors if encryption or update fails.
|
|
||||||
pub fn update_user_infos(
|
pub fn update_user_infos(
|
||||||
conn: &Connection, user_key: &str, user_id: &str, first_name: &str, last_name: &str,
|
conn: &Connection, user_key: &str, user_id: &str,
|
||||||
username: &str, email: &str, author_name: Option<&str>, lang: Lang,
|
username: &str, email: &str, author_name: Option<&str>, lang: Lang,
|
||||||
) -> AppResult<bool> {
|
) -> AppResult<bool> {
|
||||||
let encrypted_first_name: String = encrypt_data_with_user_key(first_name, user_key)?;
|
|
||||||
let encrypted_last_name: String = encrypt_data_with_user_key(last_name, user_key)?;
|
|
||||||
let encrypted_username: String = encrypt_data_with_user_key(username, user_key)?;
|
let encrypted_username: String = encrypt_data_with_user_key(username, user_key)?;
|
||||||
let encrypted_email: String = encrypt_data_with_user_key(email, user_key)?;
|
let encrypted_email: String = encrypt_data_with_user_key(email, user_key)?;
|
||||||
let hashed_email: String = hash_element(email);
|
let hashed_email: String = hash_element(email);
|
||||||
@@ -127,30 +57,5 @@ pub fn update_user_infos(
|
|||||||
hashed_author_name = hash_element(author_name_val);
|
hashed_author_name = hash_element(author_name_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
repo::update_user_infos(conn, user_id, &encrypted_first_name, &encrypted_last_name, &encrypted_username, &hashed_username, &encrypted_email, &hashed_email, &hashed_author_name, &encrypted_author_name, lang)
|
repo::update_user_infos(conn, user_id, &encrypted_username, &hashed_username, &encrypted_email, &hashed_email, &hashed_author_name, &encrypted_author_name, lang)
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves and decrypts the user's account information from the database.
|
|
||||||
/// * `conn` - Database connection
|
|
||||||
/// * `user_id` - The unique identifier of the user
|
|
||||||
/// * `lang` - The language for error messages
|
|
||||||
/// Returns the decrypted user account information.
|
|
||||||
/// Errors if the user is not found or decryption fails.
|
|
||||||
pub fn get_user_account_information(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<UserAccount> {
|
|
||||||
let account_data: repo::UserAccountQuery = repo::fetch_account_information(conn, user_id, lang)?;
|
|
||||||
let user_encryption_key: String = get_user_encryption_key(user_id)?;
|
|
||||||
|
|
||||||
let decrypted_first_name: String = if let Some(ref first_name) = account_data.first_name { decrypt_data_with_user_key(first_name, &user_encryption_key)? } else { String::new() };
|
|
||||||
let decrypted_last_name: String = if let Some(ref last_name) = account_data.last_name { decrypt_data_with_user_key(last_name, &user_encryption_key)? } else { String::new() };
|
|
||||||
let decrypted_username: String = decrypt_data_with_user_key(&account_data.username, &user_encryption_key)?;
|
|
||||||
let decrypted_author_name: String = if let Some(ref author_name) = account_data.author_name { decrypt_data_with_user_key(author_name, &user_encryption_key)? } else { String::new() };
|
|
||||||
let decrypted_email: String = decrypt_data_with_user_key(&account_data.email, &user_encryption_key)?;
|
|
||||||
|
|
||||||
Ok(UserAccount {
|
|
||||||
first_name: decrypted_first_name,
|
|
||||||
last_name: decrypted_last_name,
|
|
||||||
username: decrypted_username,
|
|
||||||
author_name: decrypted_author_name,
|
|
||||||
email: decrypted_email,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,23 +10,6 @@ use crate::error::{AppError, AppResult};
|
|||||||
use crate::helpers::{create_unique_id, timestamp_in_seconds};
|
use crate::helpers::{create_unique_id, timestamp_in_seconds};
|
||||||
use crate::shared::types::Lang;
|
use crate::shared::types::Lang;
|
||||||
|
|
||||||
/// Represents a synced world with its elements for synchronization.
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct SyncedWorld {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
pub elements: Vec<SyncedWorldElement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a synced world element for synchronization.
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct SyncedWorldElement {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub last_update: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a single world element with its properties.
|
/// Represents a single world element with its properties.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct WorldElement {
|
pub struct WorldElement {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use chrono::Utc;
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
|
|
||||||
@@ -20,20 +19,6 @@ pub fn create_unique_id(existing_id: Option<&str>) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current date as an ISO 8601 string.
|
|
||||||
/// Equivalent to TS `System.getCurrentDate()`.
|
|
||||||
pub fn get_current_date() -> String {
|
|
||||||
Utc::now().to_rfc3339()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts an ISO date string to a MySQL-compatible date (YYYY-MM-DD).
|
|
||||||
/// Equivalent to TS `System.dateToMySqlDate()`.
|
|
||||||
pub fn date_to_mysql_date(iso_date_string: &str) -> String {
|
|
||||||
match chrono::DateTime::parse_from_rfc3339(iso_date_string) {
|
|
||||||
Ok(date_object) => date_object.format("%Y-%m-%d").to_string(),
|
|
||||||
Err(_) => iso_date_string.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts HTML content to plain text by stripping tags and decoding entities.
|
/// Converts HTML content to plain text by stripping tags and decoding entities.
|
||||||
/// Equivalent to TS `System.htmlToText()`.
|
/// Equivalent to TS `System.htmlToText()`.
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ pub fn run() {
|
|||||||
domains::user::commands::get_platform,
|
domains::user::commands::get_platform,
|
||||||
domains::user::commands::get_user_info,
|
domains::user::commands::get_user_info,
|
||||||
domains::user::commands::sync_user,
|
domains::user::commands::sync_user,
|
||||||
|
domains::user::commands::update_user_info,
|
||||||
// ─── Offline ───────────────────────────────────
|
// ─── Offline ───────────────────────────────────
|
||||||
domains::offline::commands::offline_pin_set,
|
domains::offline::commands::offline_pin_set,
|
||||||
domains::offline::commands::offline_pin_verify,
|
domains::offline::commands::offline_pin_verify,
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
pub mod ai_models;
|
|
||||||
pub mod session;
|
pub mod session;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|||||||
Reference in New Issue
Block a user