mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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::*;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user