fix: push to sign in screen when logout (#3127)

* fix: push to sign in screen when logout

* chore: show historical login users

* chore: open historical user

* chore: show historical user

* chore: reload app widget with unique key

* chore: add tooltip for user history
This commit is contained in:
Nathan.fooo
2023-08-07 22:24:04 +08:00
committed by GitHub
parent a3bea472bf
commit 3c04b72932
35 changed files with 528 additions and 123 deletions

View File

@ -6,6 +6,7 @@ use flowy_user_deps::entities::*;
use crate::entities::parser::{UserEmail, UserIcon, UserName, UserOpenaiKey, UserPassword};
use crate::entities::AuthTypePB;
use crate::errors::ErrorCode;
use crate::services::HistoricalUser;
#[derive(Default, ProtoBuf)]
pub struct UserTokenPB {
@ -205,3 +206,46 @@ pub struct RemoveWorkspaceUserPB {
#[pb(index = 2)]
pub workspace_id: String,
}
#[derive(ProtoBuf, Default, Clone)]
pub struct RepeatedHistoricalUserPB {
#[pb(index = 1)]
pub items: Vec<HistoricalUserPB>,
}
#[derive(ProtoBuf, Default, Clone)]
pub struct HistoricalUserPB {
#[pb(index = 1)]
pub user_id: i64,
#[pb(index = 2)]
pub user_name: String,
#[pb(index = 3)]
pub last_time: i64,
#[pb(index = 4)]
pub auth_type: AuthTypePB,
}
impl From<Vec<HistoricalUser>> for RepeatedHistoricalUserPB {
fn from(historical_users: Vec<HistoricalUser>) -> Self {
Self {
items: historical_users
.into_iter()
.map(HistoricalUserPB::from)
.collect(),
}
}
}
impl From<HistoricalUser> for HistoricalUserPB {
fn from(historical_user: HistoricalUser) -> Self {
Self {
user_id: historical_user.user_id,
user_name: historical_user.user_name,
last_time: historical_user.sign_in_timestamp,
auth_type: historical_user.auth_type.into(),
}
}
}

View File

@ -260,3 +260,23 @@ pub async fn update_network_state_handler(
.did_update_network(reachable);
Ok(())
}
#[tracing::instrument(level = "debug", skip_all, err)]
pub async fn get_historical_users_handler(
session: AFPluginState<Weak<UserSession>>,
) -> DataResult<RepeatedHistoricalUserPB, FlowyError> {
let session = upgrade_session(session)?;
let users = RepeatedHistoricalUserPB::from(session.get_historical_users());
data_result_ok(users)
}
#[tracing::instrument(level = "debug", skip_all, err)]
pub async fn open_historical_users_handler(
user: AFPluginData<HistoricalUserPB>,
session: AFPluginState<Weak<UserSession>>,
) -> Result<(), FlowyError> {
let user = user.into_inner();
let session = upgrade_session(session)?;
session.open_historical_user(user.user_id)?;
Ok(())
}

View File

@ -47,6 +47,8 @@ pub fn init(user_session: Weak<UserSession>) -> AFPlugin {
remove_user_from_workspace_handler,
)
.event(UserEvent::UpdateNetworkState, update_network_state_handler)
.event(UserEvent::GetHistoricalUsers, get_historical_users_handler)
.event(UserEvent::OpenHistoricalUser, open_historical_users_handler)
}
pub struct SignUpContext {
@ -85,6 +87,7 @@ pub trait UserCloudServiceProvider: Send + Sync + 'static {
fn update_supabase_config(&self, supabase_config: &SupabaseConfiguration);
fn set_auth_type(&self, auth_type: AuthType);
fn get_user_service(&self) -> Result<Arc<dyn UserService>, FlowyError>;
fn service_name(&self) -> String;
}
impl<T> UserCloudServiceProvider for Arc<T>
@ -102,6 +105,10 @@ where
fn get_user_service(&self) -> Result<Arc<dyn UserService>, FlowyError> {
(**self).get_user_service()
}
fn service_name(&self) -> String {
(**self).service_name()
}
}
/// Acts as a placeholder [UserStatusCallback] for the user session, but does not perform any function
@ -208,4 +215,10 @@ pub enum UserEvent {
#[event(input = "NetworkStatePB")]
UpdateNetworkState = 24,
#[event(output = "RepeatedHistoricalUserPB")]
GetHistoricalUsers = 25,
#[event(input = "HistoricalUserPB")]
OpenHistoricalUser = 26,
}

View File

@ -8,7 +8,7 @@ use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
use flowy_user_deps::entities::{SignInResponse, UserWorkspace};
use flowy_user_deps::entities::{SignInResponse, SignUpResponse, UserWorkspace};
#[derive(Debug, Clone, Serialize)]
pub struct Session {
@ -102,6 +102,15 @@ impl std::convert::From<Session> for String {
}
}
impl From<&SignUpResponse> for Session {
fn from(value: &SignUpResponse) -> Self {
Session {
user_id: value.user_id,
user_workspace: value.latest_workspace.clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -1,3 +1,5 @@
use std::convert::TryFrom;
use std::string::ToString;
use std::sync::{Arc, Weak};
use appflowy_integrate::RocksCollabDB;
@ -152,27 +154,30 @@ impl UserSession {
params: BoxAny,
auth_type: AuthType,
) -> Result<UserProfile, FlowyError> {
let resp: SignInResponse = self
let response: SignInResponse = self
.cloud_services
.get_user_service()?
.sign_in(params)
.await?;
let session: Session = resp.clone().into();
let session: Session = response.clone().into();
let uid = session.user_id;
self.set_session(Some(session))?;
self.log_user(uid, self.user_dir(uid));
self.set_current_session(Some(session))?;
let user_workspace = resp.latest_workspace.clone();
self.log_user(uid, response.name.clone(), &auth_type, self.user_dir(uid));
let user_workspace = response.latest_workspace.clone();
save_user_workspaces(
self.db_pool(uid)?,
resp
response
.user_workspaces
.iter()
.map(|user_workspace| UserWorkspaceTable::from((uid, user_workspace)))
.flat_map(|user_workspace| UserWorkspaceTable::try_from((uid, user_workspace)).ok())
.collect(),
)?;
let user_profile: UserProfile = self.save_user(uid, (resp, auth_type).into()).await?.into();
let user_profile: UserProfile = self
.save_user(uid, (response, auth_type).into())
.await?
.into();
if let Err(e) = self
.user_status_callback
.read()
@ -226,19 +231,16 @@ impl UserSession {
is_new: response.is_new,
local_folder: None,
};
let new_session = Session {
user_id: response.user_id,
user_workspace: response.latest_workspace.clone(),
};
let uid = new_session.user_id;
self.set_session(Some(new_session.clone()))?;
self.log_user(uid, self.user_dir(uid));
let new_session = Session::from(&response);
self.set_current_session(Some(new_session.clone()))?;
let uid = response.user_id;
self.log_user(uid, response.name.clone(), &auth_type, self.user_dir(uid));
save_user_workspaces(
self.db_pool(uid)?,
response
.user_workspaces
.iter()
.map(|user_workspace| UserWorkspaceTable::from((uid, user_workspace)))
.flat_map(|user_workspace| UserWorkspaceTable::try_from((uid, user_workspace)).ok())
.collect(),
)?;
let user_table = self
@ -289,7 +291,7 @@ impl UserSession {
pub async fn sign_out(&self) -> Result<(), FlowyError> {
let session = self.get_session()?;
self.database.close(session.user_id)?;
self.set_session(None)?;
self.set_current_session(None)?;
let server = self.cloud_services.get_user_service()?;
tokio::spawn(async move {
@ -513,7 +515,7 @@ impl UserSession {
pool,
new_user_workspaces
.iter()
.map(|user_workspace| UserWorkspaceTable::from((uid, user_workspace)))
.flat_map(|user_workspace| UserWorkspaceTable::try_from((uid, user_workspace)).ok())
.collect(),
);
@ -561,8 +563,8 @@ impl UserSession {
})
}
fn set_session(&self, session: Option<Session>) -> Result<(), FlowyError> {
tracing::debug!("Set user session: {:?}", session);
fn set_current_session(&self, session: Option<Session>) -> Result<(), FlowyError> {
tracing::debug!("Set current user: {:?}", session);
match &session {
None => self
.store_preferences
@ -577,13 +579,15 @@ impl UserSession {
Ok(())
}
fn log_user(&self, uid: i64, storage_path: String) {
fn log_user(&self, uid: i64, user_name: String, auth_type: &AuthType, storage_path: String) {
let mut logger_users = self
.store_preferences
.get_object::<HistoricalUsers>(HISTORICAL_USER)
.unwrap_or_default();
logger_users.add_user(HistoricalUser {
user_id: uid,
user_name,
auth_type: auth_type.clone(),
sign_in_timestamp: timestamp(),
storage_path,
});
@ -593,11 +597,27 @@ impl UserSession {
}
pub fn get_historical_users(&self) -> Vec<HistoricalUser> {
self
let mut users = self
.store_preferences
.get_object::<HistoricalUsers>(HISTORICAL_USER)
.unwrap_or_default()
.users
.users;
users.sort_by(|a, b| b.sign_in_timestamp.cmp(&a.sign_in_timestamp));
users
}
pub fn open_historical_user(&self, uid: i64) -> FlowyResult<()> {
let conn = self.db_connection(uid)?;
let row = user_workspace_table::dsl::user_workspace_table
.filter(user_workspace_table::uid.eq(uid))
.first::<UserWorkspaceTable>(&*conn)?;
let user_workspace = UserWorkspace::from(row);
let session = Session {
user_id: uid,
user_workspace,
};
self.set_current_session(Some(session))?;
Ok(())
}
/// Returns the current user session.
@ -691,6 +711,12 @@ impl HistoricalUsers {
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct HistoricalUser {
pub user_id: i64,
#[serde(default = "flowy_user_deps::DEFAULT_USER_NAME")]
pub user_name: String,
#[serde(default = "DEFAULT_AUTH_TYPE")]
pub auth_type: AuthType,
pub sign_in_timestamp: i64,
pub storage_path: String,
}
const DEFAULT_AUTH_TYPE: fn() -> AuthType = || AuthType::Local;

View File

@ -1,4 +1,6 @@
use chrono::{TimeZone, Utc};
use flowy_error::FlowyError;
use std::convert::TryFrom;
use flowy_sqlite::schema::user_workspace_table;
use flowy_user_deps::entities::UserWorkspace;
@ -13,15 +15,24 @@ pub struct UserWorkspaceTable {
pub database_storage_id: String,
}
impl From<(i64, &UserWorkspace)> for UserWorkspaceTable {
fn from(value: (i64, &UserWorkspace)) -> Self {
Self {
impl TryFrom<(i64, &UserWorkspace)> for UserWorkspaceTable {
type Error = FlowyError;
fn try_from(value: (i64, &UserWorkspace)) -> Result<Self, Self::Error> {
if value.1.id.is_empty() {
return Err(FlowyError::invalid_data().context("The id is empty"));
}
if value.1.database_storage_id.is_empty() {
return Err(FlowyError::invalid_data().context("The database storage id is empty"));
}
Ok(Self {
id: value.1.id.clone(),
name: value.1.name.clone(),
uid: value.0,
created_at: value.1.created_at.timestamp(),
database_storage_id: value.1.database_storage_id.clone(),
}
})
}
}
@ -34,7 +45,7 @@ impl From<UserWorkspaceTable> for UserWorkspace {
.timestamp_opt(value.created_at, 0)
.single()
.unwrap_or_default(),
database_storage_id: "".to_string(),
database_storage_id: value.database_storage_id,
}
}
}