Skip to content

Commit

Permalink
Support Lemmy API 0.18.3 (#21)
Browse files Browse the repository at this point in the history
* Early 0.18.3 support. Bump versions. Switch to using Github for lemmy_api_common.

* Add rough migration code. Update Lemmy BE version.

* Cleanup migration code

* Update README
  • Loading branch information
CMahaff committed Jul 29, 2023
1 parent 53beef5 commit 6139efe
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 38 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
[package]
name = "lasim"
authors = ["Connor Mahaffey"]
version = "0.1.2"
version = "0.2.0"
edition = "2021"
license = "MIT"
publish = false
build = "build.rs"

[dependencies]
slint = "~1.0.2"
lemmy_api_common = "=0.18.1-rc.9"
lemmy_api_common = { git = "https://github.com/LemmyNet/lemmy.git", tag = "0.18.3" }
reqwest = { version = "~0.11.18", features = ["json"] }
futures = "~0.3"
tokio = { version = "~1", features = ["full"] }
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@
- This should go without saying, but obviously both your new and old accounts are still distinct - LASIM simply makes it easier to move from one to the other

## Limitations
- This is alpha software and should be treated as such - always read the log output to verify there were no errors during transfer, and always confirm your new account's settings in the web UI. If you have issues, write a bug here on Github!
- Versions of LASIM only target specific Lemmy BE API versions, which are currently changing rapidly. See the Version Support table.
- As long as the "Profile Version" is the same between LASIM versions, it is possible to use different LASIM versions together to target Lemmy servers running different incompatible API versions.
- At time of writing there is planned support, but no code written, to support migration from older Profile Versions to newer ones.
- Old versions of your LASIM profile are compatible with newer versions of LASIM, but the reverse is not true.
- If your version is not explicitely listed, take the latest LASIM that is available!

## Version Support
| LASIM Version | LASIM Profile Version | Supported Lemmy BE API Version(s) |
| ------------- | --------------------- | --------------------------------- |
| 0.1.\* | 1 | 0.18.1 (rc.9+), 0.18.2, and above |
| 0.1.\* | 1 | 0.18.1 (rc.9+), 0.18.2 |
| 0.2.\* | 2 | 0.18.3 |
21 changes: 4 additions & 17 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

mod lemmy;
mod profile;
mod migrations;

use slint::Weak;
use slint::SharedString;
Expand All @@ -17,8 +18,6 @@ use futures::executor::block_on;

slint::include_modules!();

// TODO: In the future, if needed, support versioning of this file. For now, hard-code.
const PROFILE_FILENAME: &str = "profile_v1.json";
const PANIC_LOG: &str = "error.log";

struct ProcessingInstruction {
Expand Down Expand Up @@ -55,7 +54,8 @@ fn evaluate_two_factor_token(token: &String) -> Result<Option<String>, &str> {
}

fn write_profile(profile_local: &profile::ProfileConfiguration, mut logger: impl FnMut(String)) {
let path = Path::new(PROFILE_FILENAME);
let profile_filename = migrations::profile_migrate::get_latest_profile_name();
let path = Path::new(profile_filename.as_str());
let mut file = match File::create(&path) {
Ok(file) => file,
Err(e) => {
Expand Down Expand Up @@ -132,20 +132,7 @@ async fn process_download(processing_instruction: ProcessingInstruction, mut log
}

fn read_profile() -> Result<profile::ProfileConfiguration, String> {
let path = Path::new(PROFILE_FILENAME);
let profile_json_result = std::fs::read_to_string(path);
let profile_json = match profile_json_result {
Ok(file) => file,
Err(_) => return Err("ERROR: Failed to open profile settings!".to_string()),
};

let profile_local_result: Result<profile::ProfileConfiguration, serde_json::Error> = serde_json::from_slice(profile_json.as_bytes());
let profile_local = match profile_local_result {
Ok(profile) => profile,
Err(e) => return Err(format!("ERROR: Failed to parse profile JSON - {}", e)),
};

return Ok(profile_local);
return migrations::profile_migrate::read_latest_profile();
}

#[tokio::main]
Expand Down
75 changes: 75 additions & 0 deletions src/migrations/migrate_v1_to_v2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use crate::profile::ProfileConfiguration;
use crate::profile::ProfileSettings;
use std::path::Path;

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct ProfileSettingsV1 {
show_nsfw: bool,
show_scores: bool,
theme: String,
default_sort_type: String,
default_listing_type: String,
interface_language: String,
show_avatars: bool,
send_notifications_to_email: bool,
bot_account: bool,
show_bot_accounts: bool,
show_read_posts: bool,
show_new_post_notifs: bool,
discussion_languages: Vec<i32>,
open_links_in_new_tab: bool,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct ProfileConfigurationV1 {
pub blocked_users: Vec<String>,
pub blocked_communities: Vec<String>,
pub followed_communities: Vec<String>,
pub profile_settings: ProfileSettingsV1,
}

const OLD_PROFILE_FILENAME: &str = "profile_v1.json";

pub fn read_profile() -> Result<ProfileConfigurationV1, String> {
let path = Path::new(OLD_PROFILE_FILENAME);
let profile_json_result = std::fs::read_to_string(path);
let profile_json = match profile_json_result {
Ok(file) => file,
Err(_) => return Err(format!("ERROR: Failed to open {}", OLD_PROFILE_FILENAME)),
};

let profile_local_result: Result<ProfileConfigurationV1, serde_json::Error> = serde_json::from_slice(profile_json.as_bytes());
let profile_local = match profile_local_result {
Ok(profile) => profile,
Err(e) => return Err(format!("ERROR: Failed to parse {} JSON - {}", OLD_PROFILE_FILENAME, e)),
};

return Ok(profile_local);
}

pub fn convert_profile(old_profile: ProfileConfigurationV1) -> ProfileConfiguration {
let new_profile = ProfileConfiguration {
blocked_users: old_profile.blocked_users,
blocked_communities: old_profile.blocked_communities,
followed_communities: old_profile.followed_communities,
profile_settings: ProfileSettings {
show_nsfw: old_profile.profile_settings.show_nsfw,
show_scores: old_profile.profile_settings.show_scores,
theme: old_profile.profile_settings.theme,
default_sort_type: old_profile.profile_settings.default_sort_type,
default_listing_type: old_profile.profile_settings.default_listing_type,
interface_language: old_profile.profile_settings.interface_language,
show_avatars: old_profile.profile_settings.show_avatars,
send_notifications_to_email: old_profile.profile_settings.send_notifications_to_email,
bot_account: old_profile.profile_settings.bot_account,
show_bot_accounts: old_profile.profile_settings.show_bot_accounts,
show_read_posts: old_profile.profile_settings.show_read_posts,
show_new_post_notifs: old_profile.profile_settings.show_new_post_notifs,
discussion_languages: old_profile.profile_settings.discussion_languages,
open_links_in_new_tab: old_profile.profile_settings.open_links_in_new_tab,
infinite_scroll_enabled: false,
},
};

return new_profile;
}
2 changes: 2 additions & 0 deletions src/migrations/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod migrate_v1_to_v2;
pub mod profile_migrate;
76 changes: 76 additions & 0 deletions src/migrations/profile_migrate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use crate::profile;
use crate::migrations;
use std::path::Path;

const PROFILE_FILENAME_START: &str = "profile_v";
const PROFILE_FILENAME_END: &str = ".json";
const PROFILE_CURRENT_VERSION: u16 = 2;

pub fn read_latest_profile() -> Result<profile::ProfileConfiguration, String> {
// Identify latest profile version
let mut latest_profile_version: u16 = 0;
let directory_items = std::fs::read_dir("./").unwrap();
for item in directory_items {
if item.is_ok() {
let dir_entry = item.unwrap();
let metadata = std::fs::metadata(dir_entry.path());
if metadata.is_ok_and(|m| m.is_file()) {
let filename = String::from(dir_entry.file_name().to_str().unwrap());
if filename.starts_with(PROFILE_FILENAME_START) && filename.ends_with(PROFILE_FILENAME_END) {
let end_index = filename.find(PROFILE_FILENAME_END).unwrap();
let profile_version_string = filename.get(PROFILE_FILENAME_START.char_indices().count()..end_index);
let profile_version = profile_version_string.unwrap().parse::<u16>().unwrap();

if profile_version > latest_profile_version {
latest_profile_version = profile_version;
}
}
}
}
}

// Convert as necessary
let mut profile_v1: Option<migrations::migrate_v1_to_v2::ProfileConfigurationV1> = None;
let mut profile_v2: Option<profile::ProfileConfiguration> = None;

if latest_profile_version == 1 {
let read_result = migrations::migrate_v1_to_v2::read_profile();
profile_v1 = match read_result {
Ok(profile) => Some(profile),
Err(e) => return Err(e),
};
latest_profile_version = 2;
}

if latest_profile_version == PROFILE_CURRENT_VERSION {
if profile_v1.is_some() {
profile_v2 = Some(migrations::migrate_v1_to_v2::convert_profile(profile_v1.unwrap()));
} else {
let filename = format!("profile_v{}.json", PROFILE_CURRENT_VERSION);
let path = Path::new(filename.as_str());
let profile_json_result = std::fs::read_to_string(path);
let profile_json = match profile_json_result {
Ok(file) => file,
Err(_) => return Err(format!("ERROR: Failed to open {}", filename)),
};

let profile_local_result: Result<profile::ProfileConfiguration, serde_json::Error> = serde_json::from_slice(profile_json.as_bytes());
let profile_local = match profile_local_result {
Ok(profile) => profile,
Err(e) => return Err(format!("ERROR: Failed to parse {} JSON - {}", filename, e)),
};

profile_v2 = Some(profile_local);
}
}

if profile_v2.is_some() {
return Ok(profile_v2.unwrap());
} else {
return Err("ERROR: No saved profiles found. Use download option first!".to_string());
}
}

pub fn get_latest_profile_name() -> String {
return format!("{}{}{}", PROFILE_FILENAME_START, PROFILE_CURRENT_VERSION, PROFILE_FILENAME_END);
}
31 changes: 17 additions & 14 deletions src/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ use crate::lemmy::typecast;

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct ProfileSettings {
show_nsfw: bool,
show_scores: bool,
theme: String,
default_sort_type: String,
default_listing_type: String,
interface_language: String,
show_avatars: bool,
send_notifications_to_email: bool,
bot_account: bool,
show_bot_accounts: bool,
show_read_posts: bool,
show_new_post_notifs: bool,
discussion_languages: Vec<i32>,
open_links_in_new_tab: bool,
pub show_nsfw: bool,
pub show_scores: bool,
pub theme: String,
pub default_sort_type: String,
pub default_listing_type: String,
pub interface_language: String,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub bot_account: bool,
pub show_bot_accounts: bool,
pub show_read_posts: bool,
pub show_new_post_notifs: bool,
pub discussion_languages: Vec<i32>,
pub open_links_in_new_tab: bool,
pub infinite_scroll_enabled: bool,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
Expand Down Expand Up @@ -54,6 +55,7 @@ pub fn construct_settings(profile_settings: &ProfileSettings) -> person::SaveUse
generate_totp_2fa: None, // Don't change
auth: Sensitive::from(""), // This will be inserted before the request is sent
open_links_in_new_tab: Some(profile_settings.open_links_in_new_tab),
infinite_scroll_enabled: Some(profile_settings.infinite_scroll_enabled),
};
}

Expand Down Expand Up @@ -115,6 +117,7 @@ pub fn construct_profile(original_profile: &site::GetSiteResponse) -> ProfileCon
show_new_post_notifs: local_user.show_new_post_notifs,
discussion_languages: typecast::FromAPI::cast_language_array(&my_user.discussion_languages),
open_links_in_new_tab: local_user.open_links_in_new_tab,
infinite_scroll_enabled: local_user.infinite_scroll_enabled,
},
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/ui/app.slint
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ export component App inherits Window {
Text {
font-size: 20px;
font-weight: 900;
text: "LASIM 0.1.2";
text: "LASIM 0.2.0";
}
Text {
text: "Lemmy BE API Version 0.18.1-rc.9+";
text: "Lemmy BE API Version 0.18.3";
}
Text {
text: "Created by Connor Mahaffey";
Expand Down

0 comments on commit 6139efe

Please sign in to comment.