Complete local-first content migration slice
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use galaxyui::{Entity, EntityId, ModelContext, ModelHandle, SingletonEntity};
|
||||
use galaxyui::{Entity, EntityId, ModelContext, SingletonEntity};
|
||||
|
||||
use super::workflow::Workflow;
|
||||
use super::CloudWorkflowModel;
|
||||
@@ -9,9 +9,6 @@ use crate::cloud_object::model::persistence::CloudModel;
|
||||
use crate::cloud_object::{GenericCloudObject, Owner};
|
||||
use crate::drive::OpenGalaxyDriveObjectSettings;
|
||||
use crate::pane_group::{PaneContent, WorkflowPane};
|
||||
use crate::server::cloud_objects::update_manager::{
|
||||
ObjectOperation, OperationSuccessType, UpdateManager, UpdateManagerEvent,
|
||||
};
|
||||
use crate::server::ids::{ClientId, SyncId};
|
||||
use crate::workflows::workflow_view::WorkflowView;
|
||||
use crate::workflows::WorkflowViewMode;
|
||||
@@ -44,12 +41,7 @@ pub enum WorkflowOpenSource {
|
||||
}
|
||||
|
||||
impl WorkflowManager {
|
||||
pub fn new(ctx: &mut ModelContext<Self>) -> Self {
|
||||
ctx.subscribe_to_model(
|
||||
&UpdateManager::handle(ctx),
|
||||
Self::handle_update_manager_event,
|
||||
);
|
||||
|
||||
pub fn new() -> Self {
|
||||
WorkflowManager {
|
||||
panes_by_hashed_id: HashMap::new(),
|
||||
}
|
||||
@@ -81,9 +73,8 @@ impl WorkflowManager {
|
||||
if let Some(workflow) = workflow {
|
||||
view.update(ctx, |view, ctx| view.load(workflow, settings, mode, ctx));
|
||||
} else {
|
||||
// If the workflow doesn't exist, try waiting for initial load and trying again
|
||||
view.update(ctx, |view, ctx| {
|
||||
view.wait_for_initial_load_then_load(
|
||||
view.load_local_or_show_not_found(
|
||||
*workflow_id,
|
||||
settings,
|
||||
mode,
|
||||
@@ -177,44 +168,17 @@ impl WorkflowManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_update_manager_event(
|
||||
&mut self,
|
||||
_: ModelHandle<UpdateManager>,
|
||||
event: &UpdateManagerEvent,
|
||||
ctx: &mut ModelContext<Self>,
|
||||
) {
|
||||
let UpdateManagerEvent::ObjectOperationComplete { result } = event else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !matches!(&result.success_type, OperationSuccessType::Success) {
|
||||
return;
|
||||
}
|
||||
if let ObjectOperation::Create { .. } = result.operation {
|
||||
let server_id = result.server_id.expect("Expect server id on success");
|
||||
let Some(server_id) = CloudModel::as_ref(ctx)
|
||||
.get_workflow_by_uid(&server_id.uid())
|
||||
.and_then(|workflow| workflow.id.into_server())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(client_id) = result.client_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(mut pane) = self.panes_by_hashed_id.remove(&client_id.to_string()) {
|
||||
pane.workflow_id = SyncId::ServerId(server_id);
|
||||
self.panes_by_hashed_id
|
||||
.insert(server_id.uid().clone(), pane);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.panes_by_hashed_id.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WorkflowManager {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
struct WorkflowPaneData {
|
||||
workflow_id: SyncId,
|
||||
window_id: WindowId,
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::sync::Arc;
|
||||
|
||||
pub use cloud_object_models::{CloudWorkflow, CloudWorkflowModel, WorkflowId};
|
||||
use galaxy_core::context_flag::ContextFlag;
|
||||
use galaxy_core::features::FeatureFlag;
|
||||
use galaxyui::{AppContext, SingletonEntity};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -24,7 +23,6 @@ use async_trait::async_trait;
|
||||
pub use categories::{CategoriesView, CategoriesViewEvent, WorkflowsViewAction};
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::cloud_object::model::view::CloudViewModel;
|
||||
use crate::cloud_object::{
|
||||
CloudModelType, CloudObjectEventEntrypoint, CloudObjectUpsertParams, CreateCloudObjectResult,
|
||||
CreateObjectRequest, GenericServerObject, ObjectType, Revision, UpdateCloudObjectResult,
|
||||
@@ -89,41 +87,17 @@ pub enum WorkflowViewMode {
|
||||
}
|
||||
|
||||
impl WorkflowViewMode {
|
||||
/// The editing mode supported for a workflow.
|
||||
///
|
||||
/// Editing is disabled if the user does not have edit permissions.
|
||||
pub fn supported_edit_mode(workflow_id: Option<SyncId>, app: &AppContext) -> Self {
|
||||
let can_edit = workflow_id
|
||||
.map(|id| {
|
||||
CloudViewModel::as_ref(app)
|
||||
.object_editability(&id.uid(), app)
|
||||
.can_edit()
|
||||
})
|
||||
.unwrap_or(true);
|
||||
|
||||
if !FeatureFlag::SharedWithMe.is_enabled() || can_edit {
|
||||
Self::Edit
|
||||
} else {
|
||||
Self::View
|
||||
}
|
||||
/// Local workflows are always editable.
|
||||
pub fn supported_edit_mode() -> Self {
|
||||
Self::Edit
|
||||
}
|
||||
|
||||
/// The viewing mode supported for this workflow.
|
||||
///
|
||||
/// Viewing is disabled if the user is allowed to edit the workflow and in a context where
|
||||
/// running workflows is supported.
|
||||
pub fn supported_view_mode(workflow_id: Option<SyncId>, app: &AppContext) -> Self {
|
||||
let can_edit = workflow_id
|
||||
.map(|id| {
|
||||
CloudViewModel::as_ref(app)
|
||||
.object_editability(&id.uid(), app)
|
||||
.can_edit()
|
||||
})
|
||||
.unwrap_or(true);
|
||||
|
||||
if FeatureFlag::SharedWithMe.is_enabled() && !can_edit {
|
||||
Self::View
|
||||
} else if ContextFlag::RunWorkflow.is_enabled() {
|
||||
pub fn supported_view_mode() -> Self {
|
||||
if ContextFlag::RunWorkflow.is_enabled() {
|
||||
Self::Edit
|
||||
} else {
|
||||
Self::View
|
||||
|
||||
@@ -43,11 +43,8 @@ use crate::auth::{AuthStateProvider, UserUid};
|
||||
use crate::cloud_object::breadcrumbs::ContainingObject;
|
||||
use crate::cloud_object::model::persistence::{CloudModel, CloudModelEvent};
|
||||
use crate::cloud_object::model::view::CloudViewModel;
|
||||
use crate::cloud_object::{
|
||||
CloudObject, CloudObjectEventEntrypoint, ObjectType, Owner, Revision, Space,
|
||||
};
|
||||
use crate::cloud_object::{CloudObject, Owner, Revision};
|
||||
use crate::drive::cloud_object_styling::warp_drive_icon_color;
|
||||
use crate::drive::drive_helpers::has_feature_gated_anonymous_user_reached_workflow_limit;
|
||||
use crate::drive::items::WarpDriveItemId;
|
||||
use crate::drive::sharing::{ContentEditability, ShareableObject, SharingAccessLevel};
|
||||
use crate::drive::workflows::ai_assist::GeneratedCommandMetadataError;
|
||||
@@ -65,15 +62,11 @@ use crate::editor::{
|
||||
PlainTextEditorViewAction as EditorAction, PropagateAndNoOpNavigationKeys,
|
||||
SingleLineEditorOptions, TextOptions, TextStyleOperation,
|
||||
};
|
||||
use crate::local_object_repository::LocalObjectRepository;
|
||||
use crate::menu::{MenuItem, MenuItemFields};
|
||||
use crate::network::NetworkStatus;
|
||||
use crate::pane_group::focus_state::PaneFocusHandle;
|
||||
use crate::pane_group::pane::view;
|
||||
use crate::pane_group::{BackingView, PaneConfiguration, PaneEvent};
|
||||
use crate::server::cloud_objects::update_manager::{
|
||||
FetchSingleObjectOption, ObjectOperation, OperationSuccessType, UpdateManager,
|
||||
UpdateManagerEvent,
|
||||
};
|
||||
use crate::server::ids::{ClientId, ServerId, SyncId};
|
||||
use crate::server::server_api::ai::AIClient;
|
||||
use crate::server::server_api::ServerApiProvider;
|
||||
@@ -521,11 +514,6 @@ impl WorkflowView {
|
||||
ctx.subscribe_to_model(&CloudModel::handle(ctx), move |workflow, _, event, ctx| {
|
||||
workflow.handle_cloud_model_event(event, ctx)
|
||||
});
|
||||
|
||||
let update_manager = UpdateManager::handle(ctx);
|
||||
ctx.subscribe_to_model(&update_manager, |me, _, event, ctx| {
|
||||
me.handle_update_manager_event(event, ctx);
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_cloud_model_event(&mut self, event: &CloudModelEvent, ctx: &mut ViewContext<Self>) {
|
||||
@@ -545,66 +533,6 @@ impl WorkflowView {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_update_manager_event(
|
||||
&mut self,
|
||||
event: &UpdateManagerEvent,
|
||||
ctx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let UpdateManagerEvent::ObjectOperationComplete { result } = event else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let (ObjectOperation::Create { .. }, OperationSuccessType::Success) =
|
||||
(&result.operation, &result.success_type)
|
||||
{
|
||||
if self.workflow_id.into_client() == result.client_id {
|
||||
let server_id = result
|
||||
.server_id
|
||||
.expect("Expect server id on success creation");
|
||||
|
||||
// The aliases were created with the old client sync id. Update them to the new server id.
|
||||
WorkflowAliases::handle(ctx).update(ctx, |aliases, ctx| {
|
||||
if let Result::Err(e) =
|
||||
aliases.update_workflow_id(self.workflow_id, server_id.into(), ctx)
|
||||
{
|
||||
log::error!("Failed to update aliases after workflow creation: {e:?}");
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(workflow) =
|
||||
CloudModel::as_ref(ctx).get_workflow_by_uid(&server_id.uid())
|
||||
{
|
||||
self.load(
|
||||
workflow.clone(),
|
||||
&OpenGalaxyDriveObjectSettings::default(),
|
||||
self.workflow_view_mode,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
ctx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
if let (ObjectOperation::Update, OperationSuccessType::Success) =
|
||||
(&result.operation, &result.success_type)
|
||||
{
|
||||
if let Some(workflow) = self.get_cloud_workflow(ctx) {
|
||||
// This makes sure we get the correct updated revision_ts. So our subsequent
|
||||
// updates don't fail
|
||||
if self.workflow_id.into_client() == result.client_id
|
||||
|| self.workflow_id.uid() == result.server_id.unwrap_or_default().uid()
|
||||
{
|
||||
self.load(
|
||||
workflow,
|
||||
&OpenGalaxyDriveObjectSettings::default(),
|
||||
self.workflow_view_mode,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn should_show_unsaved_changes_dialog(&self, app: &AppContext) -> bool {
|
||||
self.is_dirty(app)
|
||||
}
|
||||
@@ -621,7 +549,7 @@ impl WorkflowView {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait_for_initial_load_then_load(
|
||||
pub fn load_local_or_show_not_found(
|
||||
&mut self,
|
||||
workflow_id: SyncId,
|
||||
settings: &OpenGalaxyDriveObjectSettings,
|
||||
@@ -629,75 +557,15 @@ impl WorkflowView {
|
||||
window_id: WindowId,
|
||||
ctx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let initial_load_complete = UpdateManager::as_ref(ctx).initial_load_complete();
|
||||
// TODO @ianhodge CLD-2002: it could be nice to have a loading screen here while we wait for the load
|
||||
let settings = settings.clone();
|
||||
ctx.spawn(initial_load_complete, move |me, _, ctx| {
|
||||
let workflow = CloudModel::as_ref(ctx).get_workflow(&workflow_id).cloned();
|
||||
// If either the focused folder or the workflow can't be found in cloudmodel, fetch the object from the server
|
||||
let fetch_needed = workflow.is_none()
|
||||
|| settings
|
||||
.focused_folder_id
|
||||
.map(SyncId::ServerId)
|
||||
.map(|folder_id| CloudModel::as_ref(ctx).get_folder(&folder_id).is_none())
|
||||
.unwrap_or(false);
|
||||
if fetch_needed {
|
||||
if let Some(server_id) = workflow_id.into_server() {
|
||||
me.fetch_and_load_workflow(server_id, &settings, mode, window_id, ctx);
|
||||
} else {
|
||||
log::warn!("Tried to load workflow without server id {workflow_id:?}");
|
||||
}
|
||||
} else if let Some(workflow) = workflow {
|
||||
me.load(workflow, &settings, mode, ctx);
|
||||
} else {
|
||||
ToastStack::handle(ctx).update(ctx, |toast_stack, ctx| {
|
||||
toast_stack.add_ephemeral_toast_by_type(
|
||||
ToastType::CloudObjectNotFound,
|
||||
window_id,
|
||||
ctx,
|
||||
);
|
||||
});
|
||||
log::warn!("Tried to open unknown workflow {workflow_id:?}");
|
||||
}
|
||||
});
|
||||
}
|
||||
if let Some(workflow) = CloudModel::as_ref(ctx).get_workflow(&workflow_id).cloned() {
|
||||
self.load(workflow, settings, mode, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
fn fetch_and_load_workflow(
|
||||
&mut self,
|
||||
workflow_id: ServerId,
|
||||
settings: &OpenGalaxyDriveObjectSettings,
|
||||
mode: WorkflowViewMode,
|
||||
window_id: WindowId,
|
||||
ctx: &mut ViewContext<Self>,
|
||||
) {
|
||||
// If we have a parent folder we are trying to load as a part of this workflow, fetch that instead
|
||||
let id_to_fetch = settings.focused_folder_id.unwrap_or(workflow_id);
|
||||
let fetch_cloud_object_rx =
|
||||
UpdateManager::handle(ctx).update(ctx, |update_manager, ctx| {
|
||||
update_manager.fetch_single_cloud_object(
|
||||
&id_to_fetch,
|
||||
FetchSingleObjectOption::None,
|
||||
ctx,
|
||||
)
|
||||
});
|
||||
let settings = settings.clone();
|
||||
ctx.spawn(fetch_cloud_object_rx, move |me, _, ctx| {
|
||||
if let Some(workflow) = CloudModel::as_ref(ctx)
|
||||
.get_workflow(&SyncId::ServerId(workflow_id))
|
||||
.cloned()
|
||||
{
|
||||
me.load(workflow, &settings, mode, ctx);
|
||||
} else {
|
||||
ToastStack::handle(ctx).update(ctx, |toast_stack, ctx| {
|
||||
toast_stack.add_ephemeral_toast_by_type(
|
||||
ToastType::CloudObjectNotFound,
|
||||
window_id,
|
||||
ctx,
|
||||
);
|
||||
});
|
||||
log::warn!("Tried to open unknown workflow {workflow_id:?} after fetching");
|
||||
}
|
||||
ToastStack::handle(ctx).update(ctx, |toast_stack, ctx| {
|
||||
toast_stack.add_ephemeral_toast_by_type(ToastType::CloudObjectNotFound, window_id, ctx);
|
||||
});
|
||||
log::warn!("Tried to open unknown local workflow {workflow_id:?}");
|
||||
}
|
||||
|
||||
pub fn load(
|
||||
@@ -718,14 +586,10 @@ impl WorkflowView {
|
||||
|
||||
self.workflow_view_mode = match mode {
|
||||
// Force view mode if the user is not allowed to edit the workflow.
|
||||
WorkflowViewMode::Edit => {
|
||||
WorkflowViewMode::supported_edit_mode(Some(self.workflow_id), ctx)
|
||||
}
|
||||
WorkflowViewMode::Edit => WorkflowViewMode::supported_edit_mode(),
|
||||
// Force edit mode if we are in a context where we can run workflows and we try to use view
|
||||
// mode
|
||||
WorkflowViewMode::View => {
|
||||
WorkflowViewMode::supported_view_mode(Some(self.workflow_id), ctx)
|
||||
}
|
||||
WorkflowViewMode::View => WorkflowViewMode::supported_view_mode(),
|
||||
mode => mode,
|
||||
};
|
||||
|
||||
@@ -1407,8 +1271,7 @@ impl WorkflowView {
|
||||
}
|
||||
|
||||
fn try_set_view_mode(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
self.workflow_view_mode =
|
||||
WorkflowViewMode::supported_view_mode(Some(self.workflow_id), ctx);
|
||||
self.workflow_view_mode = WorkflowViewMode::supported_view_mode();
|
||||
// always reset with the cloudmodel version whether or not we successfully
|
||||
// transition to the view mode. This reset doesn't always set the correct revision_ts
|
||||
// we rely on the load called when we handle the update_manager's change event.
|
||||
@@ -1447,13 +1310,9 @@ impl WorkflowView {
|
||||
}
|
||||
|
||||
self.workflow_view_mode = match self.workflow_view_mode {
|
||||
WorkflowViewMode::View => {
|
||||
WorkflowViewMode::supported_edit_mode(Some(self.workflow_id), ctx)
|
||||
}
|
||||
WorkflowViewMode::View => WorkflowViewMode::supported_edit_mode(),
|
||||
// Attempt to toggle to view mode only if it is allowed in this context
|
||||
WorkflowViewMode::Edit => {
|
||||
WorkflowViewMode::supported_view_mode(Some(self.workflow_id), ctx)
|
||||
}
|
||||
WorkflowViewMode::Edit => WorkflowViewMode::supported_view_mode(),
|
||||
// NOTE: prevent transition from create to any other mode
|
||||
// we also shouldn't be showing the toggle button in create view
|
||||
WorkflowViewMode::Create => WorkflowViewMode::Create,
|
||||
@@ -1598,13 +1457,8 @@ impl WorkflowView {
|
||||
|
||||
match self.workflow_view_mode {
|
||||
WorkflowViewMode::Edit => {
|
||||
UpdateManager::handle(ctx).update(ctx, |update_manager, ctx| {
|
||||
update_manager.update_workflow(
|
||||
workflow.clone(),
|
||||
self.workflow_id,
|
||||
self.revision_ts.clone(),
|
||||
ctx,
|
||||
);
|
||||
LocalObjectRepository::handle(ctx).update(ctx, |repository, ctx| {
|
||||
repository.update_workflow(self.workflow_id, workflow.clone(), ctx);
|
||||
});
|
||||
if let ContainerConfiguration::Pane(pane_config) = &mut self.container_configuration
|
||||
{
|
||||
@@ -1618,23 +1472,12 @@ impl WorkflowView {
|
||||
self.try_set_view_mode(ctx);
|
||||
}
|
||||
WorkflowViewMode::Create => {
|
||||
let client_id = if let Some(id) = self.workflow_id.into_client() {
|
||||
id
|
||||
} else {
|
||||
log::error!("No client_id obtained for creating workflow");
|
||||
self.display_error_toast(String::from("Could not create workflow"), ctx);
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(space) = self.owner {
|
||||
UpdateManager::handle(ctx).update(ctx, |update_manager, ctx| {
|
||||
update_manager.create_workflow(
|
||||
workflow.clone(),
|
||||
space,
|
||||
if self.owner.is_some() {
|
||||
LocalObjectRepository::handle(ctx).update(ctx, |repository, ctx| {
|
||||
repository.create_workflow_with_id(
|
||||
self.workflow_id,
|
||||
self.initial_folder_id,
|
||||
client_id,
|
||||
CloudObjectEventEntrypoint::Unknown,
|
||||
true,
|
||||
workflow.clone(),
|
||||
ctx,
|
||||
);
|
||||
});
|
||||
@@ -1654,7 +1497,9 @@ impl WorkflowView {
|
||||
log::error!("Attempting to create workflow but now space found");
|
||||
}
|
||||
}
|
||||
_ => log::error!("Did not match conditions to either create or save the workflow"),
|
||||
WorkflowViewMode::View => {
|
||||
log::error!("Attempted to save a workflow while in view mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1827,10 +1672,6 @@ impl WorkflowView {
|
||||
ctx.emit(WorkflowViewEvent::Pane(PaneEvent::FocusSelf));
|
||||
}
|
||||
|
||||
fn is_online(&self, app: &AppContext) -> bool {
|
||||
NetworkStatus::as_ref(app).is_online()
|
||||
}
|
||||
|
||||
/// Whether or not opening links in the desktop app is supported.
|
||||
fn can_open_on_desktop(&self, app: &AppContext) -> bool {
|
||||
!ContextFlag::HideOpenOnDesktopButton.is_enabled()
|
||||
@@ -1991,11 +1832,8 @@ impl WorkflowView {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateManager::handle(ctx).update(ctx, |update_manager, ctx| {
|
||||
update_manager.duplicate_object(
|
||||
&CloudObjectTypeAndId::from_id_and_type(self.workflow_id, ObjectType::Workflow),
|
||||
ctx,
|
||||
);
|
||||
LocalObjectRepository::handle(ctx).update(ctx, |repository, ctx| {
|
||||
repository.duplicate_workflow(self.workflow_id, ctx);
|
||||
});
|
||||
ctx.notify();
|
||||
}
|
||||
@@ -2007,24 +1845,14 @@ impl WorkflowView {
|
||||
|
||||
self.close(ctx);
|
||||
|
||||
UpdateManager::handle(ctx).update(ctx, move |update_manager, ctx| {
|
||||
update_manager.trash_object(
|
||||
CloudObjectTypeAndId::from_id_and_type(self.workflow_id, ObjectType::Workflow),
|
||||
ctx,
|
||||
);
|
||||
LocalObjectRepository::handle(ctx).update(ctx, |repository, ctx| {
|
||||
repository.set_workflow_trashed(self.workflow_id, true, ctx);
|
||||
});
|
||||
}
|
||||
|
||||
fn untrash_object(&self, ctx: &mut ViewContext<Self>) {
|
||||
if has_feature_gated_anonymous_user_reached_workflow_limit(ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateManager::handle(ctx).update(ctx, move |update_manager, ctx| {
|
||||
update_manager.untrash_object(
|
||||
CloudObjectTypeAndId::from_id_and_type(self.workflow_id, ObjectType::Workflow),
|
||||
ctx,
|
||||
);
|
||||
LocalObjectRepository::handle(ctx).update(ctx, |repository, ctx| {
|
||||
repository.set_workflow_trashed(self.workflow_id, false, ctx);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3206,30 +3034,19 @@ impl BackingView for WorkflowView {
|
||||
}
|
||||
}
|
||||
|
||||
let space = CloudViewModel::as_ref(ctx).object_space(&self.workflow_id.uid(), ctx);
|
||||
menu_items.push(
|
||||
MenuItemFields::new("Duplicate")
|
||||
.with_on_select_action(WorkflowAction::Duplicate)
|
||||
.with_icon(Icon::Duplicate)
|
||||
.into_item(),
|
||||
);
|
||||
|
||||
// Add "Duplicate" to menu
|
||||
if space != Some(Space::Shared) {
|
||||
menu_items.push(
|
||||
MenuItemFields::new("Duplicate")
|
||||
.with_on_select_action(WorkflowAction::Duplicate)
|
||||
.with_icon(Icon::Duplicate)
|
||||
.into_item(),
|
||||
);
|
||||
}
|
||||
|
||||
// Add "Trash" to menu
|
||||
let access_level = self.access_level(ctx);
|
||||
if self.is_online(ctx)
|
||||
&& (!FeatureFlag::SharedWithMe.is_enabled() || access_level.can_trash())
|
||||
{
|
||||
menu_items.push(
|
||||
MenuItemFields::new("Trash")
|
||||
.with_on_select_action(WorkflowAction::Trash)
|
||||
.with_icon(Icon::Trash)
|
||||
.into_item(),
|
||||
);
|
||||
}
|
||||
menu_items.push(
|
||||
MenuItemFields::new("Trash")
|
||||
.with_on_select_action(WorkflowAction::Trash)
|
||||
.with_icon(Icon::Trash)
|
||||
.into_item(),
|
||||
);
|
||||
|
||||
menu_items
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user