first pass of merging in warp (doesn't build)
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
use super::{CloudObject, Space};
|
||||
use crate::{
|
||||
drive::{folders::CloudFolder, items::WarpDriveItemId, CloudObjectTypeAndId},
|
||||
ui_components::breadcrumb::Breadcrumb,
|
||||
};
|
||||
use galaxyui::AppContext;
|
||||
|
||||
use super::{CloudObject, Space};
|
||||
use crate::drive::folders::CloudFolder;
|
||||
use crate::drive::items::WarpDriveItemId;
|
||||
use crate::drive::CloudObjectTypeAndId;
|
||||
use crate::ui_components::breadcrumb::Breadcrumb;
|
||||
|
||||
// Encapsulates an object that can contain other objects, and keeps
|
||||
// information necessary to show breadcrumbs.
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use galaxyui::elements::{Container, Element, MouseStateHandle, Text};
|
||||
use galaxyui::fonts::{Properties, Style, Weight};
|
||||
use galaxyui::platform::Cursor;
|
||||
use galaxyui::ui_components::button::ButtonVariant;
|
||||
use galaxyui::{elements::Element, AppContext, View};
|
||||
use galaxyui::{Entity, SingletonEntity, TypedActionView, ViewContext};
|
||||
use galaxyui::ui_components::components::UiComponent;
|
||||
use galaxyui::{AppContext, Entity, SingletonEntity, TypedActionView, View, ViewContext};
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::ui_components::buttons::close_button;
|
||||
use crate::ui_components::dialog::{dialog_styles, Dialog};
|
||||
use galaxyui::elements::{Container, MouseStateHandle, Text};
|
||||
use galaxyui::ui_components::components::UiComponent;
|
||||
|
||||
const EDIT_ANYWAY_CTA_LABEL: &str = "Edit anyway";
|
||||
const CANCEL_CTA_LABEL: &str = "Cancel";
|
||||
|
||||
+113
-754
File diff suppressed because it is too large
Load Diff
@@ -1,189 +1,86 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
pub use cloud_object_client::{
|
||||
ObjectAction, ObjectActionHistory, ObjectActionSubtype, ObjectActionType,
|
||||
};
|
||||
use galaxyui::{Entity, ModelContext, SingletonEntity};
|
||||
|
||||
use crate::{
|
||||
persistence::model::PersistedObjectAction,
|
||||
server::ids::{parse_sqlite_id_to_uid, HashedSqliteId, ObjectUid},
|
||||
};
|
||||
use crate::server::ids::{HashedSqliteId, ObjectUid};
|
||||
|
||||
pub enum ObjectActionsEvent {}
|
||||
|
||||
/// The type of action that occurred on an object, such as an execution, selection, so on
|
||||
/// and so forth.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ObjectActionType {
|
||||
Execute,
|
||||
}
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub fn object_action_from_persisted(
|
||||
other: crate::persistence::model::PersistedObjectAction,
|
||||
) -> Result<ObjectAction, ()> {
|
||||
// Each persisted object action is either a single action or a bundled action.
|
||||
// If there's any inconsistencies from the SQL row, we return an error.
|
||||
let action_subtype = if let Some(count) = other.count {
|
||||
let oldest_timestamp = other
|
||||
.oldest_timestamp
|
||||
.as_ref()
|
||||
.map(|time| time.and_utc())
|
||||
.ok_or(())?;
|
||||
let latest_timestamp = other
|
||||
.latest_timestamp
|
||||
.as_ref()
|
||||
.map(|time| time.and_utc())
|
||||
.ok_or(())?;
|
||||
|
||||
// In order to convert from a graphql type and from a SQLite read, the action type
|
||||
// implements to_string().
|
||||
//
|
||||
// Temporarily suppress clippy warnings about the `ToString` impl until we
|
||||
// move `ObjectType` away from using `std::fmt::Display` for serialization.
|
||||
#[allow(clippy::to_string_trait_impl)]
|
||||
impl ToString for ObjectActionType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
ObjectActionType::Execute => String::from("EXECUTE"),
|
||||
// When the db row is a bundled action, the processed_at_timestamp field refers
|
||||
// to the latest processed_at_timestamp in the bundle. Because bundled actions come
|
||||
// from the server, this is a value, not an option.
|
||||
let latest_processed_at_timestamp = other
|
||||
.processed_at_timestamp
|
||||
.as_ref()
|
||||
.map(|time| time.and_utc())
|
||||
.ok_or(())?;
|
||||
ObjectActionSubtype::BundledActions {
|
||||
count,
|
||||
oldest_timestamp,
|
||||
latest_timestamp,
|
||||
latest_processed_at_timestamp,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let timestamp = other
|
||||
.timestamp
|
||||
.as_ref()
|
||||
.map(|time| time.and_utc())
|
||||
.ok_or(())?;
|
||||
let pending = other.pending.ok_or(())?;
|
||||
|
||||
impl ObjectActionType {
|
||||
fn singular(&self) -> String {
|
||||
match self {
|
||||
ObjectActionType::Execute => "run".to_string(),
|
||||
// The processed_at_timestamp is still None when the action hasn't been synced.
|
||||
let processed_at_timestamp = other
|
||||
.processed_at_timestamp
|
||||
.as_ref()
|
||||
.map(|time| time.and_utc());
|
||||
ObjectActionSubtype::SingleAction {
|
||||
timestamp,
|
||||
data: other.data,
|
||||
pending,
|
||||
processed_at_timestamp,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn plural(&self) -> String {
|
||||
match self {
|
||||
ObjectActionType::Execute => "runs".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
// The object_sync_id stored in SQLite is the hashed id that's used to index into the ObjectActions
|
||||
// model.
|
||||
let hashed_object_id = other.hashed_object_id;
|
||||
let action_type = match other.action.as_str() {
|
||||
s if s == ObjectActionType::Execute.to_string() => ObjectActionType::Execute,
|
||||
_ => return Err(()),
|
||||
};
|
||||
|
||||
/// We track object actions, both those that have been sent to the server and not, through this
|
||||
/// type. A single ObjectAction represents an object_id, action pair and a subtype that contains data
|
||||
/// about the action(s). Each ObjectAction either represents one action or a summary of identical actions
|
||||
/// that occurred at different times. We summarize old actions in order to save memory footprint on the client.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ObjectAction {
|
||||
pub action_type: ObjectActionType,
|
||||
pub uid: ObjectUid,
|
||||
pub hashed_sqlite_id: HashedSqliteId,
|
||||
// This action either represents one action or a consolidation of multiple actions.
|
||||
pub action_subtype: ObjectActionSubtype,
|
||||
}
|
||||
// NOTE: This is needed since we only store the sqlite hash, but we need the uid (the second part of the hash)
|
||||
// to index into CloudModel and store the object actions in memory.
|
||||
let uid = crate::server::ids::parse_sqlite_id_to_uid(hashed_object_id.clone())?;
|
||||
|
||||
impl ObjectAction {
|
||||
pub fn is_pending(&self) -> bool {
|
||||
match self.action_subtype {
|
||||
ObjectActionSubtype::SingleAction { pending, .. } => pending,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PersistedObjectAction> for ObjectAction {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(other: PersistedObjectAction) -> Result<Self, Self::Error> {
|
||||
// Each persisted object action is either a single action or a bundled action.
|
||||
// If there's any inconsistencies from the SQL row, we return an error.
|
||||
let action_subtype = if let Some(count) = other.count {
|
||||
let oldest_timestamp = other
|
||||
.oldest_timestamp
|
||||
.as_ref()
|
||||
.map(|time| time.and_utc())
|
||||
.ok_or(())?;
|
||||
let latest_timestamp = other
|
||||
.latest_timestamp
|
||||
.as_ref()
|
||||
.map(|time| time.and_utc())
|
||||
.ok_or(())?;
|
||||
|
||||
// When the db row is a bundled action, the processed_at_timestamp field refers
|
||||
// to the latest processed_at_timestamp in the bundle. Because bundled actions come
|
||||
// from the server, this is a value, not an option.
|
||||
let latest_processed_at_timestamp = other
|
||||
.processed_at_timestamp
|
||||
.as_ref()
|
||||
.map(|time| time.and_utc())
|
||||
.ok_or(())?;
|
||||
ObjectActionSubtype::BundledActions {
|
||||
count,
|
||||
oldest_timestamp,
|
||||
latest_timestamp,
|
||||
latest_processed_at_timestamp,
|
||||
}
|
||||
} else {
|
||||
let timestamp = other
|
||||
.timestamp
|
||||
.as_ref()
|
||||
.map(|time| time.and_utc())
|
||||
.ok_or(())?;
|
||||
let pending = other.pending.ok_or(())?;
|
||||
|
||||
// The processed_at_timestamp is still None when the action hasn't been synced.
|
||||
let processed_at_timestamp = other
|
||||
.processed_at_timestamp
|
||||
.as_ref()
|
||||
.map(|time| time.and_utc());
|
||||
ObjectActionSubtype::SingleAction {
|
||||
timestamp,
|
||||
data: other.data,
|
||||
pending,
|
||||
processed_at_timestamp,
|
||||
}
|
||||
};
|
||||
|
||||
// The object_sync_id stored in SQLite is the hashed id that's used to index into the ObjectActions
|
||||
// model.
|
||||
let hashed_object_id = other.hashed_object_id;
|
||||
let action_type = match other.action.as_str() {
|
||||
s if s == ObjectActionType::Execute.to_string() => ObjectActionType::Execute,
|
||||
_ => return Err(()),
|
||||
};
|
||||
|
||||
// NOTE: This is needed since we only store the sqlite hash, but we need the uid (the second part of the hash)
|
||||
// to index into CloudModel and store the object actions in memory.
|
||||
let uid = parse_sqlite_id_to_uid(hashed_object_id.clone())?;
|
||||
|
||||
Ok(ObjectAction {
|
||||
uid: uid.to_string(),
|
||||
hashed_sqlite_id: hashed_object_id,
|
||||
action_type,
|
||||
action_subtype,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The server communicates the action history of an object via an "ObjectActionHistory" type that
|
||||
/// contains the uid, a list of actions (single or bundled), and the timestamp of the most recent action
|
||||
/// (which is redundant from the list of actions). We use this type to convert from the graphql layer into
|
||||
/// an identical type the sync_queue and update_manager can pass around.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ObjectActionHistory {
|
||||
pub uid: ObjectUid,
|
||||
pub hashed_sqlite_id: HashedSqliteId,
|
||||
pub latest_processed_at_timestamp: DateTime<Utc>,
|
||||
pub actions: Vec<ObjectAction>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ObjectActionSubtype {
|
||||
SingleAction {
|
||||
// When the action occurred.
|
||||
timestamp: DateTime<Utc>,
|
||||
|
||||
// When the action was processed by the server (used to order actions against eachother).
|
||||
// None if the action has not been synced.
|
||||
processed_at_timestamp: Option<DateTime<Utc>>,
|
||||
|
||||
// A JSON representation of anything else we might want to track about the action.
|
||||
// For example, the exit code of a workflow execution.
|
||||
data: Option<String>,
|
||||
|
||||
// Whether or not this action has been successfully synced to the server.
|
||||
pending: bool,
|
||||
},
|
||||
BundledActions {
|
||||
// The number of distinct actions that are coalesced into one entry here.
|
||||
count: i32,
|
||||
|
||||
// The timestamp of the oldest action within this bundle.
|
||||
oldest_timestamp: DateTime<Utc>,
|
||||
|
||||
// The timestamp of the most recent action within the bundle.
|
||||
latest_timestamp: DateTime<Utc>,
|
||||
|
||||
// The most recent processed_at timestamp contained in the bundle (used to order actions and determine
|
||||
// how up-to-date the client's actions are.)
|
||||
latest_processed_at_timestamp: DateTime<Utc>,
|
||||
},
|
||||
Ok(ObjectAction {
|
||||
uid: uid.to_string(),
|
||||
hashed_sqlite_id: hashed_object_id,
|
||||
action_type,
|
||||
action_subtype,
|
||||
})
|
||||
}
|
||||
|
||||
/// A singleton model representing the actions that have occurred on a per-object basis. These
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use chrono::{Duration, Utc};
|
||||
use galaxyui::App;
|
||||
|
||||
use super::{ObjectAction, ObjectActionSubtype, ObjectActionType, ObjectActions};
|
||||
|
||||
use chrono::{Duration, Utc};
|
||||
|
||||
#[test]
|
||||
fn test_object_actions_daily() {
|
||||
App::test((), |mut app| async move {
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
use std::{fmt::Debug, sync::Arc};
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::server::cloud_objects::update_manager::InitiatedBy;
|
||||
use crate::{
|
||||
appearance::Appearance,
|
||||
cloud_object::{
|
||||
CloudModelType, CloudObject, CloudObjectEventEntrypoint, CreateCloudObjectResult,
|
||||
CreateObjectRequest, GenericCloudObject, GenericServerObject, GenericStringObjectFormat,
|
||||
GenericStringObjectUniqueKey, ObjectType, Revision, ServerCloudObject,
|
||||
UpdateCloudObjectResult,
|
||||
},
|
||||
drive::{items::WarpDriveItem, CloudObjectTypeAndId},
|
||||
persistence::ModelEvent,
|
||||
server::{
|
||||
ids::{ObjectUid, ServerId, SyncId},
|
||||
server_api::object::ObjectClient,
|
||||
sync_queue::{QueueItem, SerializedModel},
|
||||
},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use cloud_objects::cloud_object::CloudObjectUpsertParams;
|
||||
// Re-exported from cloud_objects.
|
||||
pub use cloud_objects::cloud_object::{GenericStringModel, Serializer};
|
||||
pub use warp_server_client::ids::GenericStringObjectId;
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::cloud_object::{
|
||||
CloudModelType, CloudObject, CloudObjectEventEntrypoint, CreateCloudObjectResult,
|
||||
CreateObjectRequest, GenericCloudObject, GenericServerObject, GenericStringObjectFormat,
|
||||
GenericStringObjectUniqueKey, ObjectType, Revision, UpdateCloudObjectResult,
|
||||
};
|
||||
use crate::drive::items::WarpDriveItem;
|
||||
use crate::drive::CloudObjectTypeAndId;
|
||||
use crate::persistence::ModelEvent;
|
||||
use crate::server::cloud_objects::update_manager::InitiatedBy;
|
||||
use crate::server::ids::{ServerId, SyncId};
|
||||
use crate::server::server_api::object::ObjectClient;
|
||||
use crate::server::sync_queue::{QueueItem, SerializedModel};
|
||||
|
||||
/// A trait that generic string-based objects should implement.
|
||||
pub trait CloudStringObject: CloudObject + Send + Sync {
|
||||
@@ -108,9 +109,6 @@ pub trait StringModel: Clone + Debug + PartialEq + Send + Sync + 'static {
|
||||
object: &Self::CloudObjectType,
|
||||
) -> QueueItem;
|
||||
|
||||
/// Returns a new instance from a server update, or None if the update should be ignored.
|
||||
fn new_from_server_update(&self, server_cloud_object: &ServerCloudObject) -> Option<Self>;
|
||||
|
||||
/// Returns whether this model type should clear on a unique key conflict.
|
||||
fn should_clear_on_unique_key_conflict(&self) -> bool {
|
||||
false
|
||||
@@ -122,27 +120,6 @@ pub trait StringModel: Clone + Debug + PartialEq + Send + Sync + 'static {
|
||||
fn uniqueness_key(&self) -> Option<GenericStringObjectUniqueKey>;
|
||||
}
|
||||
|
||||
/// A serializer goes from a model to a string and back.
|
||||
pub trait Serializer<M>: Debug + Clone + 'static {
|
||||
fn serialize(model: &M) -> SerializedModel;
|
||||
fn deserialize_owned(serialized: &str) -> Result<M>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
/// A `GenericStringModel` is a generic implementation of model types that can serialize to/from string.
|
||||
/// given a particular serializer.
|
||||
#[derive(Clone, Debug, PartialEq, Default)]
|
||||
pub struct GenericStringModel<M, S>
|
||||
where
|
||||
M: StringModel<
|
||||
CloudObjectType = GenericCloudObject<GenericStringObjectId, GenericStringModel<M, S>>,
|
||||
>,
|
||||
S: Serializer<M>,
|
||||
{
|
||||
pub string_model: M,
|
||||
}
|
||||
|
||||
impl<M, S> CloudStringObject for GenericCloudObject<GenericStringObjectId, GenericStringModel<M, S>>
|
||||
where
|
||||
M: StringModel<
|
||||
@@ -159,7 +136,7 @@ where
|
||||
}
|
||||
|
||||
fn serialized(&self) -> SerializedModel {
|
||||
self.model.serialized()
|
||||
self.model().serialized()
|
||||
}
|
||||
|
||||
fn clone_box(&self) -> Box<dyn CloudStringObject> {
|
||||
@@ -207,8 +184,9 @@ where
|
||||
self.string_model.set_display_name(name);
|
||||
}
|
||||
|
||||
fn upsert_event(&self, object: &GenericCloudObject<GenericStringObjectId, Self>) -> ModelEvent {
|
||||
let object = object as &dyn CloudStringObject;
|
||||
fn upsert_event(params: CloudObjectUpsertParams<Self>) -> ModelEvent {
|
||||
let object = GenericCloudObject::<GenericStringObjectId, Self>::from(params);
|
||||
let object = &object as &dyn CloudStringObject;
|
||||
ModelEvent::UpsertGenericStringObject {
|
||||
object: CloudStringObject::clone_box(object),
|
||||
}
|
||||
@@ -230,11 +208,16 @@ where
|
||||
self.string_model.can_export()
|
||||
}
|
||||
|
||||
fn bulk_upsert_event(
|
||||
objects: &[GenericCloudObject<GenericStringObjectId, Self>],
|
||||
) -> ModelEvent {
|
||||
fn bulk_upsert_event(objects: Vec<CloudObjectUpsertParams<Self>>) -> ModelEvent {
|
||||
ModelEvent::UpsertGenericStringObjects(
|
||||
objects.iter().map(CloudStringObject::clone_box).collect(),
|
||||
objects
|
||||
.into_iter()
|
||||
.map(|params| {
|
||||
Box::new(GenericCloudObject::<GenericStringObjectId, Self>::from(
|
||||
params,
|
||||
)) as Box<dyn CloudStringObject>
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -250,7 +233,7 @@ where
|
||||
owner: object.permissions.owner,
|
||||
id: client_id,
|
||||
title: None,
|
||||
serialized_model: Some(object.model.serialized().into()),
|
||||
serialized_model: Some(object.model().serialized().into()),
|
||||
initial_folder_id: object.metadata.folder_id,
|
||||
entrypoint,
|
||||
initiated_by,
|
||||
@@ -276,12 +259,6 @@ where
|
||||
true
|
||||
}
|
||||
|
||||
fn new_from_server_update(&self, server_cloud_object: &ServerCloudObject) -> Option<Self> {
|
||||
self.string_model
|
||||
.new_from_server_update(server_cloud_object)
|
||||
.map(Self::new)
|
||||
}
|
||||
|
||||
fn serialized(&self) -> SerializedModel {
|
||||
S::serialize(&self.string_model)
|
||||
}
|
||||
@@ -349,42 +326,3 @@ where
|
||||
self.string_model.to_warp_drive_item(id, appearance, object)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, S> GenericStringModel<M, S>
|
||||
where
|
||||
M: StringModel<
|
||||
CloudObjectType = GenericCloudObject<GenericStringObjectId, GenericStringModel<M, S>>,
|
||||
>,
|
||||
S: Serializer<M>,
|
||||
{
|
||||
pub fn deserialize_owned(serialized: &str) -> Result<Self> {
|
||||
S::deserialize_owned(serialized).map(Self::new)
|
||||
}
|
||||
|
||||
pub fn new(model: M) -> Self {
|
||||
Self {
|
||||
string_model: model,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn json_model(&self) -> &M {
|
||||
&self.string_model
|
||||
}
|
||||
}
|
||||
|
||||
/// Object id type that is common for all generic string objects.
|
||||
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||
pub struct GenericStringObjectId(ServerId);
|
||||
crate::server_id_traits! { GenericStringObjectId, "GenericStringObject" }
|
||||
|
||||
impl From<GenericStringObjectId> for SyncId {
|
||||
fn from(id: GenericStringObjectId) -> Self {
|
||||
Self::ServerId(id.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericStringObjectId {
|
||||
pub fn uid(&self) -> ObjectUid {
|
||||
self.0.uid()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
use crate::{cloud_object::JsonObjectType, server::sync_queue::SerializedModel};
|
||||
use cloud_objects::cloud_object::GenericStringObjectFormat;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::generic_string_model::{Serializer, StringModel};
|
||||
use crate::cloud_object::JsonObjectType;
|
||||
use crate::server::sync_queue::SerializedModel;
|
||||
|
||||
/// A `JsonModel` is a string model that can be serialized to and deserialized from JSON.
|
||||
pub trait JsonModel: StringModel + Serialize + DeserializeOwned + 'static {
|
||||
@@ -10,10 +12,14 @@ pub trait JsonModel: StringModel + Serialize + DeserializeOwned + 'static {
|
||||
fn json_object_type() -> JsonObjectType;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Debug, PartialEq, Default)]
|
||||
pub struct JsonSerializer;
|
||||
|
||||
impl<M: JsonModel> Serializer<M> for JsonSerializer {
|
||||
fn model_format() -> GenericStringObjectFormat {
|
||||
M::model_format()
|
||||
}
|
||||
fn serialize(model: &M) -> SerializedModel {
|
||||
SerializedModel::new(serde_json::to_string(model).expect("model should serialize"))
|
||||
}
|
||||
|
||||
+109
-123
@@ -1,67 +1,49 @@
|
||||
use chrono::Utc;
|
||||
use galaxyui::{App, ModelHandle};
|
||||
use lazy_static::lazy_static;
|
||||
use settings::SyncToCloud;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::Utc;
|
||||
use cloud_object_client::MockObjectClient;
|
||||
use lazy_static::lazy_static;
|
||||
use mockall::Sequence;
|
||||
use rand::Rng;
|
||||
use settings::{RespectUserSyncSetting, SyncToCloud};
|
||||
use galaxyui::{App, ModelHandle};
|
||||
|
||||
use super::*;
|
||||
use crate::auth::auth_manager::AuthManager;
|
||||
use crate::auth::user::TEST_USER_UID;
|
||||
use crate::auth::AuthStateProvider;
|
||||
use crate::auth::UserUid;
|
||||
use crate::auth::{AuthStateProvider, UserUid};
|
||||
use crate::cloud_object::model::actions::ObjectActions;
|
||||
use crate::cloud_object::model::generic_string_model::GenericStringModel;
|
||||
use crate::cloud_object::model::view::CloudViewModel;
|
||||
use crate::cloud_object::model::view::EditorState;
|
||||
use crate::cloud_object::model::view::UpdateTimestamp;
|
||||
use crate::cloud_object::model::view::EDITOR_TIMEOUT_DURATION_MINUTES;
|
||||
use crate::cloud_object::CloudObjectMetadata;
|
||||
use crate::cloud_object::CloudObjectPermissions;
|
||||
use crate::cloud_object::CloudObjectStatuses;
|
||||
use crate::cloud_object::CloudObjectSyncStatus;
|
||||
use crate::cloud_object::NumInFlightRequests;
|
||||
use crate::cloud_object::ObjectIdType;
|
||||
use crate::cloud_object::Owner;
|
||||
use crate::cloud_object::ServerMetadata;
|
||||
use crate::cloud_object::ServerPermissions;
|
||||
use crate::drive::folders::CloudFolderModel;
|
||||
use crate::drive::folders::FolderId;
|
||||
use crate::cloud_object::model::view::{
|
||||
CloudViewModel, EditorState, UpdateTimestamp, EDITOR_TIMEOUT_DURATION_MINUTES,
|
||||
};
|
||||
use crate::cloud_object::{
|
||||
CloudObjectMetadata, CloudObjectPermissions, CloudObjectStatuses, CloudObjectSyncStatus,
|
||||
NumInFlightRequests, ObjectIdType, Owner, ServerMetadata, ServerPermissions,
|
||||
};
|
||||
use crate::drive::folders::{CloudFolderModel, FolderId};
|
||||
use crate::drive::DriveIndexVariant;
|
||||
use crate::features::FeatureFlag;
|
||||
use crate::notebooks::CloudNotebookModel;
|
||||
use crate::notebooks::NotebookId;
|
||||
use crate::notebooks::{CloudNotebookModel, NotebookId};
|
||||
use crate::server::cloud_objects::listener::ObjectUpdateMessage;
|
||||
use crate::server::cloud_objects::update_manager::InitialLoadResponse;
|
||||
use crate::server::ids::ServerId;
|
||||
use crate::server::ids::ServerIdAndType;
|
||||
use crate::server::ids::{ServerId, ServerIdAndType};
|
||||
use crate::server::server_api::object::ObjectClient;
|
||||
use crate::server::server_api::team::MockTeamClient;
|
||||
use crate::server::server_api::workspace::MockWorkspaceClient;
|
||||
use crate::server::server_api::ServerApiProvider;
|
||||
use crate::server::sync_queue::SyncQueue;
|
||||
use crate::server::telemetry::context_provider::AppTelemetryContextProvider;
|
||||
use crate::settings::init_and_register_user_preferences;
|
||||
use crate::settings::Preference;
|
||||
use crate::settings::{init_and_register_user_preferences, Preference};
|
||||
use crate::system::SystemStats;
|
||||
use crate::workflows::CloudWorkflowModel;
|
||||
use crate::workspaces::team::Team;
|
||||
use crate::workspaces::team_tester::TeamTesterStatus;
|
||||
use crate::workspaces::user_profiles::UserProfiles;
|
||||
use crate::workspaces::user_workspaces::UserWorkspaces;
|
||||
use crate::workspaces::workspace::Workspace;
|
||||
|
||||
use crate::workflows::CloudWorkflowModel;
|
||||
use crate::workspaces::workspace::WorkspaceUid;
|
||||
use crate::NetworkStatus;
|
||||
use crate::UpdateManager;
|
||||
use mockall::Sequence;
|
||||
use rand::Rng;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::server::server_api::object::MockObjectClient;
|
||||
#[cfg(test)]
|
||||
use crate::server::server_api::team::MockTeamClient;
|
||||
#[cfg(test)]
|
||||
use crate::server::server_api::workspace::MockWorkspaceClient;
|
||||
use crate::workspaces::workspace::{Workspace, WorkspaceUid};
|
||||
use crate::{NetworkStatus, UpdateManager};
|
||||
|
||||
fn create_cloud_model(
|
||||
app: &mut App,
|
||||
@@ -173,14 +155,16 @@ fn mock_server_workflows(
|
||||
number_of_workflows: i64,
|
||||
) -> Vec<ServerWorkflow> {
|
||||
(0..number_of_workflows)
|
||||
.map(|idx| ServerWorkflow {
|
||||
id: SyncId::ServerId((start_id + idx).into()),
|
||||
metadata: mock_server_metadata(),
|
||||
permissions: mock_server_permissions(owner),
|
||||
model: CloudWorkflowModel::new(Workflow::new(
|
||||
format!("w{}", start_id + idx),
|
||||
format!("c{}", start_id + idx),
|
||||
)),
|
||||
.map(|idx| {
|
||||
ServerWorkflow::new(
|
||||
SyncId::ServerId((start_id + idx).into()),
|
||||
CloudWorkflowModel::new(Workflow::new(
|
||||
format!("w{}", start_id + idx),
|
||||
format!("c{}", start_id + idx),
|
||||
)),
|
||||
mock_server_metadata(),
|
||||
mock_server_permissions(owner),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -194,11 +178,13 @@ fn mock_random_folders(start_id: i64, owner: Owner) -> Vec<ServerFolder> {
|
||||
|
||||
fn mock_server_folders(start_id: i64, owner: Owner, number_of_folders: i64) -> Vec<ServerFolder> {
|
||||
(0..number_of_folders)
|
||||
.map(|idx| ServerFolder {
|
||||
id: SyncId::ServerId((start_id + idx).into()),
|
||||
model: CloudFolderModel::new(&format!("f{}", start_id + idx), false),
|
||||
metadata: mock_server_metadata(),
|
||||
permissions: mock_server_permissions(owner),
|
||||
.map(|idx| {
|
||||
ServerFolder::new(
|
||||
SyncId::ServerId((start_id + idx).into()),
|
||||
CloudFolderModel::new(&format!("f{}", start_id + idx), false),
|
||||
mock_server_metadata(),
|
||||
mock_server_permissions(owner),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -206,50 +192,50 @@ fn mock_server_folders(start_id: i64, owner: Owner, number_of_folders: i64) -> V
|
||||
fn mock_server_notebooks() -> Vec<ServerNotebook> {
|
||||
let owner = Owner::mock_current_user();
|
||||
vec![
|
||||
ServerNotebook {
|
||||
id: SyncId::ServerId(1.into()),
|
||||
model: CloudNotebookModel {
|
||||
ServerNotebook::new(
|
||||
SyncId::ServerId(1.into()),
|
||||
CloudNotebookModel {
|
||||
title: "t1".to_string(),
|
||||
data: "d1".to_string(),
|
||||
ai_document_id: None,
|
||||
conversation_id: None,
|
||||
},
|
||||
metadata: mock_server_metadata(),
|
||||
permissions: mock_server_permissions(owner),
|
||||
},
|
||||
ServerNotebook {
|
||||
id: SyncId::ServerId(2.into()),
|
||||
model: CloudNotebookModel {
|
||||
mock_server_metadata(),
|
||||
mock_server_permissions(owner),
|
||||
),
|
||||
ServerNotebook::new(
|
||||
SyncId::ServerId(2.into()),
|
||||
CloudNotebookModel {
|
||||
title: "t2".to_string(),
|
||||
data: "d2".to_string(),
|
||||
ai_document_id: None,
|
||||
conversation_id: None,
|
||||
},
|
||||
metadata: mock_server_metadata(),
|
||||
permissions: mock_server_permissions(owner),
|
||||
},
|
||||
ServerNotebook {
|
||||
id: SyncId::ServerId(3.into()),
|
||||
model: CloudNotebookModel {
|
||||
mock_server_metadata(),
|
||||
mock_server_permissions(owner),
|
||||
),
|
||||
ServerNotebook::new(
|
||||
SyncId::ServerId(3.into()),
|
||||
CloudNotebookModel {
|
||||
title: "t3".to_string(),
|
||||
data: "d3".to_string(),
|
||||
ai_document_id: None,
|
||||
conversation_id: None,
|
||||
},
|
||||
metadata: mock_server_metadata(),
|
||||
permissions: mock_server_permissions(owner),
|
||||
},
|
||||
ServerNotebook {
|
||||
id: SyncId::ServerId(4.into()),
|
||||
model: CloudNotebookModel {
|
||||
mock_server_metadata(),
|
||||
mock_server_permissions(owner),
|
||||
),
|
||||
ServerNotebook::new(
|
||||
SyncId::ServerId(4.into()),
|
||||
CloudNotebookModel {
|
||||
title: "t4".to_string(),
|
||||
data: "d4".to_string(),
|
||||
ai_document_id: None,
|
||||
conversation_id: None,
|
||||
},
|
||||
metadata: mock_server_metadata(),
|
||||
permissions: mock_server_permissions(owner),
|
||||
},
|
||||
mock_server_metadata(),
|
||||
mock_server_permissions(owner),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1049,18 +1035,18 @@ fn test_collapse_all_in_location() {
|
||||
let folder_1 = folder_from_cloud_model(model, folder_1_id);
|
||||
let folder_4 = folder_from_cloud_model(model, folder_4_id);
|
||||
let folder_5 = folder_from_cloud_model(model, folder_5_id);
|
||||
assert!(!folder_1.model.is_open);
|
||||
assert!(!folder_4.model.is_open);
|
||||
assert!(!folder_5.model.is_open);
|
||||
assert!(!folder_1.model().is_open);
|
||||
assert!(!folder_4.model().is_open);
|
||||
assert!(!folder_5.model().is_open);
|
||||
// but the others are still open
|
||||
let folder_2 = folder_from_cloud_model(model, folder_2_id);
|
||||
let folder_3 = folder_from_cloud_model(model, folder_3_id);
|
||||
let folder_6 = folder_from_cloud_model(model, folder_6_id);
|
||||
let folder_7 = folder_from_cloud_model(model, folder_7_id);
|
||||
assert!(folder_2.model.is_open);
|
||||
assert!(folder_3.model.is_open);
|
||||
assert!(folder_6.model.is_open);
|
||||
assert!(folder_7.model.is_open);
|
||||
assert!(folder_2.model().is_open);
|
||||
assert!(folder_3.model().is_open);
|
||||
assert!(folder_6.model().is_open);
|
||||
assert!(folder_7.model().is_open);
|
||||
|
||||
model.collapse_all_in_location(
|
||||
CloudObjectLocation::Space(Default::default()),
|
||||
@@ -1075,13 +1061,13 @@ fn test_collapse_all_in_location() {
|
||||
let folder_5 = folder_from_cloud_model(model, folder_5_id);
|
||||
let folder_6 = folder_from_cloud_model(model, folder_6_id);
|
||||
let folder_7 = folder_from_cloud_model(model, folder_7_id);
|
||||
assert!(!folder_1.model.is_open);
|
||||
assert!(!folder_2.model.is_open);
|
||||
assert!(!folder_3.model.is_open);
|
||||
assert!(!folder_4.model.is_open);
|
||||
assert!(!folder_5.model.is_open);
|
||||
assert!(!folder_6.model.is_open);
|
||||
assert!(!folder_7.model.is_open);
|
||||
assert!(!folder_1.model().is_open);
|
||||
assert!(!folder_2.model().is_open);
|
||||
assert!(!folder_3.model().is_open);
|
||||
assert!(!folder_4.model().is_open);
|
||||
assert!(!folder_5.model().is_open);
|
||||
assert!(!folder_6.model().is_open);
|
||||
assert!(!folder_7.model().is_open);
|
||||
});
|
||||
})
|
||||
}
|
||||
@@ -1146,19 +1132,19 @@ fn test_collapse_all_in_trash() {
|
||||
// folders 1, 4 should be collapsed
|
||||
let folder_1 = folder_from_cloud_model(model, folder_1_id);
|
||||
let folder_4 = folder_from_cloud_model(model, folder_4_id);
|
||||
assert!(!folder_1.model.is_open);
|
||||
assert!(!folder_4.model.is_open);
|
||||
assert!(!folder_1.model().is_open);
|
||||
assert!(!folder_4.model().is_open);
|
||||
// but the others, including folder 5, are still open
|
||||
let folder_2 = folder_from_cloud_model(model, folder_2_id);
|
||||
let folder_3 = folder_from_cloud_model(model, folder_3_id);
|
||||
let folder_5 = folder_from_cloud_model(model, folder_5_id);
|
||||
let folder_6 = folder_from_cloud_model(model, folder_6_id);
|
||||
let folder_7 = folder_from_cloud_model(model, folder_7_id);
|
||||
assert!(folder_2.model.is_open);
|
||||
assert!(folder_3.model.is_open);
|
||||
assert!(folder_5.model.is_open);
|
||||
assert!(folder_6.model.is_open);
|
||||
assert!(folder_7.model.is_open);
|
||||
assert!(folder_2.model().is_open);
|
||||
assert!(folder_3.model().is_open);
|
||||
assert!(folder_5.model().is_open);
|
||||
assert!(folder_6.model().is_open);
|
||||
assert!(folder_7.model().is_open);
|
||||
|
||||
model.collapse_all_in_location(
|
||||
CloudObjectLocation::Space(Default::default()),
|
||||
@@ -1173,13 +1159,13 @@ fn test_collapse_all_in_trash() {
|
||||
let folder_5 = folder_from_cloud_model(model, folder_5_id);
|
||||
let folder_6 = folder_from_cloud_model(model, folder_6_id);
|
||||
let folder_7 = folder_from_cloud_model(model, folder_7_id);
|
||||
assert!(!folder_1.model.is_open);
|
||||
assert!(!folder_2.model.is_open);
|
||||
assert!(!folder_3.model.is_open);
|
||||
assert!(!folder_4.model.is_open);
|
||||
assert!(!folder_5.model.is_open);
|
||||
assert!(!folder_6.model.is_open);
|
||||
assert!(!folder_7.model.is_open);
|
||||
assert!(!folder_1.model().is_open);
|
||||
assert!(!folder_2.model().is_open);
|
||||
assert!(!folder_3.model().is_open);
|
||||
assert!(!folder_4.model().is_open);
|
||||
assert!(!folder_5.model().is_open);
|
||||
assert!(!folder_6.model().is_open);
|
||||
assert!(!folder_7.model().is_open);
|
||||
});
|
||||
})
|
||||
}
|
||||
@@ -1335,15 +1321,15 @@ fn test_update_folder_timestamp_from_child_update() {
|
||||
let new_ts = initial_ts + chrono::Duration::seconds(5);
|
||||
receive_rtc_update(
|
||||
ObjectUpdateMessage::ObjectContentChanged {
|
||||
server_object: Box::new(ServerCloudObject::Notebook(ServerNotebook {
|
||||
id: SyncId::ServerId(notebook_id),
|
||||
model: CloudNotebookModel {
|
||||
server_object: Box::new(ServerCloudObject::Notebook(ServerNotebook::new(
|
||||
SyncId::ServerId(notebook_id),
|
||||
CloudNotebookModel {
|
||||
title: "Test Notebook".to_string(),
|
||||
data: "test2".into(),
|
||||
ai_document_id: None,
|
||||
conversation_id: None,
|
||||
},
|
||||
metadata: ServerMetadata {
|
||||
ServerMetadata {
|
||||
uid: notebook_id,
|
||||
revision: new_ts.into(),
|
||||
metadata_last_updated_ts: new_ts.into(),
|
||||
@@ -1354,8 +1340,8 @@ fn test_update_folder_timestamp_from_child_update() {
|
||||
last_editor_uid: None,
|
||||
current_editor_uid: None,
|
||||
},
|
||||
permissions: mock_server_permissions(Owner::mock_current_user()),
|
||||
})),
|
||||
mock_server_permissions(Owner::mock_current_user()),
|
||||
))),
|
||||
last_editor: None,
|
||||
},
|
||||
&mut app,
|
||||
@@ -1466,15 +1452,15 @@ fn test_update_folder_timestamp_from_new_child() {
|
||||
// Create a notebook inside the folder.
|
||||
receive_rtc_update(
|
||||
ObjectUpdateMessage::ObjectContentChanged {
|
||||
server_object: Box::new(ServerCloudObject::Notebook(ServerNotebook {
|
||||
id: SyncId::ServerId(notebook_id),
|
||||
model: CloudNotebookModel {
|
||||
server_object: Box::new(ServerCloudObject::Notebook(ServerNotebook::new(
|
||||
SyncId::ServerId(notebook_id),
|
||||
CloudNotebookModel {
|
||||
title: "Test Notebook".to_string(),
|
||||
data: "test".to_string(),
|
||||
ai_document_id: None,
|
||||
conversation_id: None,
|
||||
},
|
||||
metadata: ServerMetadata {
|
||||
ServerMetadata {
|
||||
uid: notebook_id,
|
||||
revision: t2.into(),
|
||||
metadata_last_updated_ts: t2.into(),
|
||||
@@ -1485,8 +1471,8 @@ fn test_update_folder_timestamp_from_new_child() {
|
||||
last_editor_uid: None,
|
||||
current_editor_uid: None,
|
||||
},
|
||||
permissions: mock_server_permissions(Owner::mock_current_user()),
|
||||
})),
|
||||
mock_server_permissions(Owner::mock_current_user()),
|
||||
))),
|
||||
last_editor: None,
|
||||
},
|
||||
&mut app,
|
||||
@@ -1,7 +1,18 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::mpsc::SyncSender;
|
||||
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use itertools::Itertools;
|
||||
use rand::Rng;
|
||||
use galaxy_core::features::FeatureFlag;
|
||||
use warp_graphql::scalars::time::ServerTimestamp;
|
||||
use warpui::{AppContext, Entity, ModelContext, SingletonEntity};
|
||||
|
||||
use super::generic_string_model::GenericStringObjectId;
|
||||
use crate::ai::execution_profiles::CloudAIExecutionProfile;
|
||||
use crate::auth::AuthStateProvider;
|
||||
use crate::cloud_object::{
|
||||
CloudModelType, CloudObjectLocation, CloudObjectPermissions, GenericCloudObject,
|
||||
CloudModelType, CloudObject, CloudObjectLocation, CloudObjectPermissions, GenericCloudObject,
|
||||
GenericServerObject, GenericStringObjectFormat, JsonObjectType, ObjectIdType, ObjectType,
|
||||
ObjectsToUpdate, Owner, Revision, RevisionAndLastEditor, ServerCloudObject, ServerCreationInfo,
|
||||
ServerFolder, ServerMetadata, ServerNotebook, ServerPermissions, ServerWorkflow, Space,
|
||||
@@ -21,20 +32,6 @@ use crate::workflows::workflow_enum::{CloudWorkflowEnum, CloudWorkflowEnumModel,
|
||||
use crate::workflows::{CloudWorkflow, CloudWorkflowModel};
|
||||
use crate::workspaces::user_workspaces::UserWorkspaces;
|
||||
|
||||
use galaxy_graphql::scalars::time::ServerTimestamp;
|
||||
use itertools::Itertools;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::mpsc::SyncSender;
|
||||
|
||||
use galaxyui::{AppContext, Entity, ModelContext, SingletonEntity};
|
||||
|
||||
use crate::cloud_object::CloudObject;
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use galaxy_core::features::FeatureFlag;
|
||||
use rand::Rng;
|
||||
|
||||
use super::generic_string_model::GenericStringObjectId;
|
||||
|
||||
// Equivalent to 24 hours
|
||||
const MIN_MINUTES_UNTIL_NEXT_FORCE_REFRESH: i64 = 1440;
|
||||
|
||||
@@ -177,7 +174,7 @@ impl CloudModel {
|
||||
///
|
||||
/// We do NOT support
|
||||
/// - Moving folders across spaces
|
||||
/// - Transfering from team space to personal space
|
||||
/// - Transferring from team space to personal space
|
||||
/// - Moving directly into a folder across spaces
|
||||
pub fn can_move_object_to_location(
|
||||
&self,
|
||||
@@ -547,7 +544,7 @@ impl CloudModel {
|
||||
ctx: &mut ModelContext<Self>,
|
||||
) {
|
||||
if let Some(folder) = self.get_folder(&server_folder.id) {
|
||||
server_folder.model.is_open = folder.model.is_open;
|
||||
server_folder.model.is_open = folder.model().is_open;
|
||||
}
|
||||
|
||||
self.upsert_from_server_object(server_folder, ctx);
|
||||
@@ -893,13 +890,13 @@ impl CloudModel {
|
||||
let is_open = match open_state {
|
||||
FolderOpenState::Open => true,
|
||||
FolderOpenState::Closed => false,
|
||||
FolderOpenState::Reversed => !folder.model.is_open,
|
||||
FolderOpenState::Reversed => !folder.model().is_open,
|
||||
};
|
||||
|
||||
folder.set_model(CloudFolderModel {
|
||||
is_open,
|
||||
is_warp_pack: folder.model.is_warp_pack,
|
||||
name: folder.model.name.clone(),
|
||||
is_warp_pack: folder.model().is_warp_pack,
|
||||
name: folder.model().name.clone(),
|
||||
});
|
||||
|
||||
let folder_clone = folder.clone();
|
||||
@@ -1783,7 +1780,10 @@ impl CloudModel {
|
||||
|
||||
if let Some(model_event_sender) = &self.model_event_sender {
|
||||
if let Err(e) = model_event_sender.send(M::bulk_upsert_event(
|
||||
objects_without_pending_changes.as_slice(),
|
||||
objects_without_pending_changes
|
||||
.iter()
|
||||
.map(|object| object.upsert_params(object.object_type()))
|
||||
.collect(),
|
||||
)) {
|
||||
log::error!("Error saving team objects to cache: {e:?}");
|
||||
}
|
||||
@@ -1826,5 +1826,5 @@ impl Entity for CloudModel {
|
||||
impl SingletonEntity for CloudModel {}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "model_test.rs"]
|
||||
#[path = "model_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -1,27 +1,21 @@
|
||||
use std::{cell::RefCell, collections::HashMap};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use chrono::{Duration, Utc};
|
||||
use galaxy_graphql::scalars::time::ServerTimestamp;
|
||||
use galaxyui::{AppContext, Entity, ModelContext, SingletonEntity};
|
||||
|
||||
use crate::{
|
||||
auth::{AuthStateProvider, UserUid},
|
||||
cloud_object::{CloudObject, CloudObjectLocation, Space},
|
||||
drive::{
|
||||
folders::CloudFolder,
|
||||
sharing::{ContentEditability, SharingAccessLevel},
|
||||
},
|
||||
safe_info,
|
||||
server::{
|
||||
cloud_objects::update_manager::{
|
||||
ObjectOperation, OperationSuccessType, UpdateManager, UpdateManagerEvent,
|
||||
},
|
||||
ids::{ObjectUid, SyncId},
|
||||
},
|
||||
workspaces::user_profiles::UserProfiles,
|
||||
};
|
||||
use galaxyui::{AppContext, Entity, ModelContext, ModelHandle, SingletonEntity};
|
||||
|
||||
use super::persistence::{CloudModel, CloudModelEvent};
|
||||
use crate::auth::{AuthStateProvider, UserUid};
|
||||
use crate::cloud_object::{CloudObject, CloudObjectLocation, Space};
|
||||
use crate::drive::folders::CloudFolder;
|
||||
use crate::drive::sharing::{ContentEditability, SharingAccessLevel};
|
||||
use crate::safe_info;
|
||||
use crate::server::cloud_objects::update_manager::{
|
||||
ObjectOperation, OperationSuccessType, UpdateManager, UpdateManagerEvent,
|
||||
};
|
||||
use crate::server::ids::{ObjectUid, SyncId};
|
||||
use crate::workspaces::user_profiles::UserProfiles;
|
||||
|
||||
pub const EDITOR_TIMEOUT_DURATION_MINUTES: i64 = 15;
|
||||
|
||||
@@ -55,7 +49,7 @@ impl Editor {
|
||||
}
|
||||
|
||||
/// Singleton model for storing and querying the data and logic logic needed by various view, based on the information
|
||||
/// stored in [CloudModel]. As a general, rule, any new API that requires logic beyond just retriving the raw value
|
||||
/// stored in [CloudModel]. As a general, rule, any new API that requires logic beyond just retrieving the raw value
|
||||
/// in [CloudModel], should be stored here. This includes logic such as object trashed status, the object current editor,
|
||||
/// and object location.
|
||||
///
|
||||
@@ -309,7 +303,12 @@ impl CloudViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_cloud_model_event(&mut self, event: &CloudModelEvent, ctx: &mut ModelContext<Self>) {
|
||||
fn handle_cloud_model_event(
|
||||
&mut self,
|
||||
_: ModelHandle<CloudModel>,
|
||||
event: &CloudModelEvent,
|
||||
ctx: &mut ModelContext<Self>,
|
||||
) {
|
||||
match event {
|
||||
CloudModelEvent::ObjectUpdated { type_and_id, .. }
|
||||
| CloudModelEvent::ObjectTrashed { type_and_id, .. }
|
||||
@@ -370,6 +369,7 @@ impl CloudViewModel {
|
||||
|
||||
fn handle_update_manager_event(
|
||||
&mut self,
|
||||
_: ModelHandle<UpdateManager>,
|
||||
event: &UpdateManagerEvent,
|
||||
ctx: &mut ModelContext<Self>,
|
||||
) {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use galaxyui::AppContext;
|
||||
|
||||
use super::{CloudObject, GenericStringObjectFormat, JsonObjectType, ObjectType};
|
||||
use crate::server::cloud_objects::update_manager::{
|
||||
InitiatedBy, ObjectOperation, OperationSuccessType,
|
||||
};
|
||||
|
||||
use super::{CloudObject, GenericStringObjectFormat, JsonObjectType, ObjectType};
|
||||
|
||||
pub struct CloudObjectToastMessage;
|
||||
|
||||
impl CloudObjectToastMessage {
|
||||
|
||||
Reference in New Issue
Block a user