This splits notedeck into: - notedeck - notedeck_chrome - notedeck_columns The `notedeck` crate is the library that `notedeck_chrome` and `notedeck_columns`, use. It contains common functionality related to notedeck apps such as the NoteCache, ImageCache, etc. The `notedeck_chrome` crate is the binary and ui chrome. It is responsible for managing themes, user accounts, signing, data paths, nostrdb, image caches etc. It will eventually have its own ui which has yet to be determined. For now it just manages the browser data, which is passed to apps via a new struct called `AppContext`. `notedeck_columns` is our columns app, with less responsibility now that more things are handled by `notedeck_chrome` There is still much work left to do before this is a proper browser: - process isolation - sandboxing - etc This is the beginning of a new era! We're just getting started. Signed-off-by: William Casarin <jb55@jb55.com>
698 lines
24 KiB
Rust
698 lines
24 KiB
Rust
use enostr::{NoteId, Pubkey};
|
|
use nostrdb::Ndb;
|
|
use serde::{Deserialize, Deserializer};
|
|
use tracing::error;
|
|
|
|
use crate::{
|
|
accounts::AccountsRoute,
|
|
column::{Columns, IntermediaryRoute},
|
|
route::Route,
|
|
timeline::{kind::ListKind, PubkeySource, Timeline, TimelineId, TimelineKind, TimelineRoute},
|
|
ui::add_column::AddColumnRoute,
|
|
Result,
|
|
};
|
|
|
|
use notedeck::{DataPath, DataPathType, Directory};
|
|
|
|
pub static COLUMNS_FILE: &str = "columns.json";
|
|
|
|
fn columns_json(path: &DataPath) -> Option<String> {
|
|
let data_path = path.path(DataPathType::Setting);
|
|
Directory::new(data_path)
|
|
.get_file(COLUMNS_FILE.to_string())
|
|
.ok()
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
enum MigrationTimelineRoute {
|
|
Timeline(u32),
|
|
Thread(String),
|
|
Profile(String),
|
|
Reply(String),
|
|
Quote(String),
|
|
}
|
|
|
|
impl MigrationTimelineRoute {
|
|
fn timeline_route(self) -> Option<TimelineRoute> {
|
|
match self {
|
|
MigrationTimelineRoute::Timeline(id) => {
|
|
Some(TimelineRoute::Timeline(TimelineId::new(id)))
|
|
}
|
|
MigrationTimelineRoute::Thread(note_id_hex) => {
|
|
Some(TimelineRoute::Thread(NoteId::from_hex(¬e_id_hex).ok()?))
|
|
}
|
|
MigrationTimelineRoute::Profile(pubkey_hex) => {
|
|
Some(TimelineRoute::Profile(Pubkey::from_hex(&pubkey_hex).ok()?))
|
|
}
|
|
MigrationTimelineRoute::Reply(note_id_hex) => {
|
|
Some(TimelineRoute::Reply(NoteId::from_hex(¬e_id_hex).ok()?))
|
|
}
|
|
MigrationTimelineRoute::Quote(note_id_hex) => {
|
|
Some(TimelineRoute::Quote(NoteId::from_hex(¬e_id_hex).ok()?))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
enum MigrationRoute {
|
|
Timeline(MigrationTimelineRoute),
|
|
Accounts(MigrationAccountsRoute),
|
|
Relays,
|
|
ComposeNote,
|
|
AddColumn(MigrationAddColumnRoute),
|
|
Support,
|
|
}
|
|
|
|
impl MigrationRoute {
|
|
fn route(self) -> Option<Route> {
|
|
match self {
|
|
MigrationRoute::Timeline(migration_timeline_route) => {
|
|
Some(Route::Timeline(migration_timeline_route.timeline_route()?))
|
|
}
|
|
MigrationRoute::Accounts(migration_accounts_route) => {
|
|
Some(Route::Accounts(migration_accounts_route.accounts_route()))
|
|
}
|
|
MigrationRoute::Relays => Some(Route::Relays),
|
|
MigrationRoute::ComposeNote => Some(Route::ComposeNote),
|
|
MigrationRoute::AddColumn(migration_add_column_route) => Some(Route::AddColumn(
|
|
migration_add_column_route.add_column_route(),
|
|
)),
|
|
MigrationRoute::Support => Some(Route::Support),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
enum MigrationAccountsRoute {
|
|
Accounts,
|
|
AddAccount,
|
|
}
|
|
|
|
impl MigrationAccountsRoute {
|
|
fn accounts_route(self) -> AccountsRoute {
|
|
match self {
|
|
MigrationAccountsRoute::Accounts => AccountsRoute::Accounts,
|
|
MigrationAccountsRoute::AddAccount => AccountsRoute::AddAccount,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
enum MigrationAddColumnRoute {
|
|
Base,
|
|
UndecidedNotification,
|
|
ExternalNotification,
|
|
Hashtag,
|
|
}
|
|
|
|
impl MigrationAddColumnRoute {
|
|
fn add_column_route(self) -> AddColumnRoute {
|
|
match self {
|
|
MigrationAddColumnRoute::Base => AddColumnRoute::Base,
|
|
MigrationAddColumnRoute::UndecidedNotification => AddColumnRoute::UndecidedNotification,
|
|
MigrationAddColumnRoute::ExternalNotification => AddColumnRoute::ExternalNotification,
|
|
MigrationAddColumnRoute::Hashtag => AddColumnRoute::Hashtag,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
struct MigrationColumn {
|
|
routes: Vec<MigrationRoute>,
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for MigrationColumn {
|
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
let routes = Vec::<MigrationRoute>::deserialize(deserializer)?;
|
|
|
|
Ok(MigrationColumn { routes })
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
struct MigrationColumns {
|
|
columns: Vec<MigrationColumn>,
|
|
timelines: Vec<MigrationTimeline>,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, Clone, PartialEq)]
|
|
struct MigrationTimeline {
|
|
id: u32,
|
|
kind: MigrationTimelineKind,
|
|
}
|
|
|
|
impl MigrationTimeline {
|
|
fn into_timeline(self, ndb: &Ndb, deck_user_pubkey: Option<&[u8; 32]>) -> Option<Timeline> {
|
|
self.kind
|
|
.into_timeline_kind()?
|
|
.into_timeline(ndb, deck_user_pubkey)
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug, PartialEq)]
|
|
enum MigrationListKind {
|
|
Contact(MigrationPubkeySource),
|
|
}
|
|
|
|
impl MigrationListKind {
|
|
fn list_kind(self) -> Option<ListKind> {
|
|
match self {
|
|
MigrationListKind::Contact(migration_pubkey_source) => {
|
|
Some(ListKind::Contact(migration_pubkey_source.pubkey_source()?))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug, PartialEq)]
|
|
enum MigrationPubkeySource {
|
|
Explicit(String),
|
|
DeckAuthor,
|
|
}
|
|
|
|
impl MigrationPubkeySource {
|
|
fn pubkey_source(self) -> Option<PubkeySource> {
|
|
match self {
|
|
MigrationPubkeySource::Explicit(hex) => {
|
|
Some(PubkeySource::Explicit(Pubkey::from_hex(hex.as_str()).ok()?))
|
|
}
|
|
MigrationPubkeySource::DeckAuthor => Some(PubkeySource::DeckAuthor),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug, PartialEq)]
|
|
enum MigrationTimelineKind {
|
|
List(MigrationListKind),
|
|
Notifications(MigrationPubkeySource),
|
|
Profile(MigrationPubkeySource),
|
|
Universe,
|
|
Generic,
|
|
Hashtag(String),
|
|
}
|
|
|
|
impl MigrationTimelineKind {
|
|
fn into_timeline_kind(self) -> Option<TimelineKind> {
|
|
match self {
|
|
MigrationTimelineKind::List(migration_list_kind) => {
|
|
Some(TimelineKind::List(migration_list_kind.list_kind()?))
|
|
}
|
|
MigrationTimelineKind::Notifications(migration_pubkey_source) => Some(
|
|
TimelineKind::Notifications(migration_pubkey_source.pubkey_source()?),
|
|
),
|
|
MigrationTimelineKind::Profile(migration_pubkey_source) => Some(TimelineKind::Profile(
|
|
migration_pubkey_source.pubkey_source()?,
|
|
)),
|
|
MigrationTimelineKind::Universe => Some(TimelineKind::Universe),
|
|
MigrationTimelineKind::Generic => Some(TimelineKind::Generic),
|
|
MigrationTimelineKind::Hashtag(hashtag) => Some(TimelineKind::Hashtag(hashtag)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl MigrationColumns {
|
|
fn into_columns(self, ndb: &Ndb, deck_pubkey: Option<&[u8; 32]>) -> Columns {
|
|
let mut columns = Columns::default();
|
|
|
|
for column in self.columns {
|
|
let mut cur_routes = Vec::new();
|
|
for route in column.routes {
|
|
match route {
|
|
MigrationRoute::Timeline(MigrationTimelineRoute::Timeline(timeline_id)) => {
|
|
if let Some(migration_tl) =
|
|
self.timelines.iter().find(|tl| tl.id == timeline_id)
|
|
{
|
|
let tl = migration_tl.clone().into_timeline(ndb, deck_pubkey);
|
|
if let Some(tl) = tl {
|
|
cur_routes.push(IntermediaryRoute::Timeline(tl));
|
|
} else {
|
|
error!("Problem deserializing timeline {:?}", migration_tl);
|
|
}
|
|
}
|
|
}
|
|
MigrationRoute::Timeline(MigrationTimelineRoute::Thread(_thread)) => {}
|
|
_ => {
|
|
if let Some(route) = route.route() {
|
|
cur_routes.push(IntermediaryRoute::Route(route));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !cur_routes.is_empty() {
|
|
columns.insert_intermediary_routes(cur_routes);
|
|
}
|
|
}
|
|
columns
|
|
}
|
|
}
|
|
|
|
fn string_to_columns(
|
|
serialized_columns: String,
|
|
ndb: &Ndb,
|
|
user: Option<&[u8; 32]>,
|
|
) -> Option<Columns> {
|
|
Some(
|
|
deserialize_columns_string(serialized_columns)
|
|
.ok()?
|
|
.into_columns(ndb, user),
|
|
)
|
|
}
|
|
|
|
pub fn deserialize_columns(path: &DataPath, ndb: &Ndb, user: Option<&[u8; 32]>) -> Option<Columns> {
|
|
string_to_columns(columns_json(path)?, ndb, user)
|
|
}
|
|
|
|
fn deserialize_columns_string(serialized_columns: String) -> Result<MigrationColumns> {
|
|
Ok(
|
|
serde_json::from_str::<MigrationColumns>(&serialized_columns)
|
|
.map_err(notedeck::Error::Json)?,
|
|
)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::storage::migration::{
|
|
MigrationColumn, MigrationListKind, MigrationPubkeySource, MigrationRoute,
|
|
MigrationTimeline, MigrationTimelineKind, MigrationTimelineRoute,
|
|
};
|
|
|
|
impl MigrationColumn {
|
|
fn from_route(route: MigrationRoute) -> Self {
|
|
Self {
|
|
routes: vec![route],
|
|
}
|
|
}
|
|
|
|
fn from_routes(routes: Vec<MigrationRoute>) -> Self {
|
|
Self { routes }
|
|
}
|
|
}
|
|
|
|
impl MigrationTimeline {
|
|
fn new(id: u32, kind: MigrationTimelineKind) -> Self {
|
|
Self { id, kind }
|
|
}
|
|
}
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn multi_column() {
|
|
let route = r#"{"columns":[[{"Timeline":{"Timeline":2}}],[{"Timeline":{"Timeline":0}}],[{"Timeline":{"Timeline":1}}]],"timelines":[{"id":0,"kind":{"List":{"Contact":{"Explicit":"aa733081e4f0f79dd43023d8983265593f2b41a988671cfcef3f489b91ad93fe"}}}},{"id":1,"kind":{"Hashtag":"introductions"}},{"id":2,"kind":"Universe"}]}"#; // Multi-column
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
|
|
assert_eq!(migration_cols.columns.len(), 3);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_route(MigrationRoute::Timeline(
|
|
MigrationTimelineRoute::Timeline(2)
|
|
))
|
|
);
|
|
|
|
assert_eq!(
|
|
*migration_cols.columns.get(1).unwrap(),
|
|
MigrationColumn::from_route(MigrationRoute::Timeline(
|
|
MigrationTimelineRoute::Timeline(0)
|
|
))
|
|
);
|
|
|
|
assert_eq!(
|
|
*migration_cols.columns.get(2).unwrap(),
|
|
MigrationColumn::from_route(MigrationRoute::Timeline(
|
|
MigrationTimelineRoute::Timeline(1)
|
|
))
|
|
);
|
|
|
|
assert_eq!(migration_cols.timelines.len(), 3);
|
|
assert_eq!(
|
|
*migration_cols.timelines.first().unwrap(),
|
|
MigrationTimeline::new(
|
|
0,
|
|
MigrationTimelineKind::List(MigrationListKind::Contact(
|
|
MigrationPubkeySource::Explicit(
|
|
"aa733081e4f0f79dd43023d8983265593f2b41a988671cfcef3f489b91ad93fe"
|
|
.to_owned()
|
|
)
|
|
))
|
|
)
|
|
);
|
|
assert_eq!(
|
|
*migration_cols.timelines.get(1).unwrap(),
|
|
MigrationTimeline::new(
|
|
1,
|
|
MigrationTimelineKind::Hashtag("introductions".to_owned())
|
|
)
|
|
);
|
|
|
|
assert_eq!(
|
|
*migration_cols.timelines.get(2).unwrap(),
|
|
MigrationTimeline::new(2, MigrationTimelineKind::Universe)
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn base() {
|
|
let route = r#"{"columns":[[{"AddColumn":"Base"}]],"timelines":[]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_route(MigrationRoute::AddColumn(MigrationAddColumnRoute::Base))
|
|
);
|
|
|
|
assert!(migration_cols.timelines.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn universe() {
|
|
let route = r#"{"columns":[[{"Timeline":{"Timeline":0}}]],"timelines":[{"id":0,"kind":"Universe"}]}"#;
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_route(MigrationRoute::Timeline(
|
|
MigrationTimelineRoute::Timeline(0)
|
|
))
|
|
);
|
|
|
|
assert_eq!(migration_cols.timelines.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.timelines.first().unwrap(),
|
|
MigrationTimeline::new(0, MigrationTimelineKind::Universe)
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn home() {
|
|
let route = r#"{"columns":[[{"Timeline":{"Timeline":2}}]],"timelines":[{"id":2,"kind":{"List":{"Contact":{"Explicit":"aa733081e4f0f79dd43023d8983265593f2b41a988671cfcef3f489b91ad93fe"}}}}]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_route(MigrationRoute::Timeline(
|
|
MigrationTimelineRoute::Timeline(2)
|
|
))
|
|
);
|
|
|
|
assert_eq!(migration_cols.timelines.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.timelines.first().unwrap(),
|
|
MigrationTimeline::new(
|
|
2,
|
|
MigrationTimelineKind::List(MigrationListKind::Contact(
|
|
MigrationPubkeySource::Explicit(
|
|
"aa733081e4f0f79dd43023d8983265593f2b41a988671cfcef3f489b91ad93fe"
|
|
.to_owned()
|
|
)
|
|
))
|
|
)
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn thread() {
|
|
let route = r#"{"columns":[[{"Timeline":{"Timeline":7}},{"Timeline":{"Thread":"fb9b0c62bc91bbe28ca428fc85e310ae38795b94fb910e0f4e12962ced971f25"}}]],"timelines":[{"id":7,"kind":{"List":{"Contact":{"Explicit":"4a0510f26880d40e432f4865cb5714d9d3c200ca6ebb16b418ae6c555f574967"}}}}]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_routes(vec![
|
|
MigrationRoute::Timeline(MigrationTimelineRoute::Timeline(7),),
|
|
MigrationRoute::Timeline(MigrationTimelineRoute::Thread(
|
|
"fb9b0c62bc91bbe28ca428fc85e310ae38795b94fb910e0f4e12962ced971f25".to_owned()
|
|
)),
|
|
])
|
|
);
|
|
|
|
assert_eq!(migration_cols.timelines.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.timelines.first().unwrap(),
|
|
MigrationTimeline::new(
|
|
7,
|
|
MigrationTimelineKind::List(MigrationListKind::Contact(
|
|
MigrationPubkeySource::Explicit(
|
|
"4a0510f26880d40e432f4865cb5714d9d3c200ca6ebb16b418ae6c555f574967"
|
|
.to_owned()
|
|
)
|
|
))
|
|
)
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn profile() {
|
|
let route = r#"{"columns":[[{"Timeline":{"Timeline":7}},{"Timeline":{"Profile":"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"}}]],"timelines":[{"id":7,"kind":{"List":{"Contact":{"Explicit":"4a0510f26880d40e432f4865cb5714d9d3c200ca6ebb16b418ae6c555f574967"}}}}]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_routes(vec![
|
|
MigrationRoute::Timeline(MigrationTimelineRoute::Timeline(7),),
|
|
MigrationRoute::Timeline(MigrationTimelineRoute::Profile(
|
|
"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245".to_owned()
|
|
)),
|
|
])
|
|
);
|
|
|
|
assert_eq!(migration_cols.timelines.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.timelines.first().unwrap(),
|
|
MigrationTimeline::new(
|
|
7,
|
|
MigrationTimelineKind::List(MigrationListKind::Contact(
|
|
MigrationPubkeySource::Explicit(
|
|
"4a0510f26880d40e432f4865cb5714d9d3c200ca6ebb16b418ae6c555f574967"
|
|
.to_owned()
|
|
)
|
|
))
|
|
)
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn your_notifs() {
|
|
let route = r#"{"columns":[[{"Timeline":{"Timeline":5}}]],"timelines":[{"id":5,"kind":{"Notifications":"DeckAuthor"}}]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_route(MigrationRoute::Timeline(
|
|
MigrationTimelineRoute::Timeline(5)
|
|
))
|
|
);
|
|
|
|
assert_eq!(migration_cols.timelines.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.timelines.first().unwrap(),
|
|
MigrationTimeline::new(
|
|
5,
|
|
MigrationTimelineKind::Notifications(MigrationPubkeySource::DeckAuthor)
|
|
)
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn undecided_notifs() {
|
|
let route = r#"{"columns":[[{"AddColumn":"Base"},{"AddColumn":"UndecidedNotification"}]],"timelines":[]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_routes(vec![
|
|
MigrationRoute::AddColumn(MigrationAddColumnRoute::Base),
|
|
MigrationRoute::AddColumn(MigrationAddColumnRoute::UndecidedNotification),
|
|
])
|
|
);
|
|
|
|
assert!(migration_cols.timelines.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn extern_notifs() {
|
|
let route = r#"{"columns":[[{"Timeline":{"Timeline":4}}]],"timelines":[{"id":4,"kind":{"Notifications":{"Explicit":"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"}}}]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_route(MigrationRoute::Timeline(
|
|
MigrationTimelineRoute::Timeline(4)
|
|
))
|
|
);
|
|
|
|
assert_eq!(migration_cols.timelines.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.timelines.first().unwrap(),
|
|
MigrationTimeline::new(
|
|
4,
|
|
MigrationTimelineKind::Notifications(MigrationPubkeySource::Explicit(
|
|
"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245".to_owned()
|
|
))
|
|
)
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn hashtag() {
|
|
let route = r#"{"columns":[[{"Timeline":{"Timeline":6}}]],"timelines":[{"id":6,"kind":{"Hashtag":"notedeck"}}]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_route(MigrationRoute::Timeline(
|
|
MigrationTimelineRoute::Timeline(6)
|
|
))
|
|
);
|
|
|
|
assert_eq!(migration_cols.timelines.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.timelines.first().unwrap(),
|
|
MigrationTimeline::new(6, MigrationTimelineKind::Hashtag("notedeck".to_owned()))
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn support() {
|
|
let route = r#"{"columns":[[{"AddColumn":"Base"},"Support"]],"timelines":[]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_routes(vec![
|
|
MigrationRoute::AddColumn(MigrationAddColumnRoute::Base),
|
|
MigrationRoute::Support
|
|
])
|
|
);
|
|
|
|
assert!(migration_cols.timelines.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn post() {
|
|
let route = r#"{"columns":[[{"AddColumn":"Base"},"ComposeNote"]],"timelines":[]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_routes(vec![
|
|
MigrationRoute::AddColumn(MigrationAddColumnRoute::Base),
|
|
MigrationRoute::ComposeNote
|
|
])
|
|
);
|
|
|
|
assert!(migration_cols.timelines.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn relay() {
|
|
let route = r#"{"columns":[[{"AddColumn":"Base"},"Relays"]],"timelines":[]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_routes(vec![
|
|
MigrationRoute::AddColumn(MigrationAddColumnRoute::Base),
|
|
MigrationRoute::Relays
|
|
])
|
|
);
|
|
|
|
assert!(migration_cols.timelines.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn accounts() {
|
|
let route =
|
|
r#"{"columns":[[{"AddColumn":"Base"},{"Accounts":"Accounts"}]],"timelines":[]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_routes(vec![
|
|
MigrationRoute::AddColumn(MigrationAddColumnRoute::Base),
|
|
MigrationRoute::Accounts(MigrationAccountsRoute::Accounts),
|
|
])
|
|
);
|
|
|
|
assert!(migration_cols.timelines.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn login() {
|
|
let route = r#"{"columns":[[{"AddColumn":"Base"},{"Accounts":"Accounts"},{"Accounts":"AddAccount"}]],"timelines":[]}"#;
|
|
|
|
let deserialized_columns = deserialize_columns_string(route.to_string());
|
|
assert!(deserialized_columns.is_ok());
|
|
|
|
let migration_cols = deserialized_columns.unwrap();
|
|
assert_eq!(migration_cols.columns.len(), 1);
|
|
assert_eq!(
|
|
*migration_cols.columns.first().unwrap(),
|
|
MigrationColumn::from_routes(vec![
|
|
MigrationRoute::AddColumn(MigrationAddColumnRoute::Base),
|
|
MigrationRoute::Accounts(MigrationAccountsRoute::Accounts),
|
|
MigrationRoute::Accounts(MigrationAccountsRoute::AddAccount),
|
|
])
|
|
);
|
|
|
|
assert!(migration_cols.timelines.is_empty());
|
|
}
|
|
}
|