Add dragging
This commit is contained in:
parent
a019d56a59
commit
dc3be999be
@ -86,7 +86,9 @@ pub enum Msg {
|
||||
// dragging
|
||||
IssueDragStarted(IssueId),
|
||||
IssueDragStopped(IssueId),
|
||||
ExchangePosition(IssueId),
|
||||
IssueDropZone(IssueStatus),
|
||||
UnlockDragOver,
|
||||
|
||||
// inputs
|
||||
InputChanged(FieldId, String),
|
||||
|
@ -115,7 +115,6 @@ pub fn update(msg: &Msg, model: &mut crate::model::Model, orders: &mut impl Orde
|
||||
|
||||
_ => (),
|
||||
}
|
||||
log!(modal);
|
||||
}
|
||||
|
||||
pub fn view(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
||||
|
@ -118,6 +118,7 @@ pub struct ProjectPage {
|
||||
pub only_my_filter: bool,
|
||||
pub recently_updated_filter: bool,
|
||||
pub dragged_issue_id: Option<IssueId>,
|
||||
pub drag_locked: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -167,6 +168,7 @@ impl Default for Model {
|
||||
only_my_filter: false,
|
||||
recently_updated_filter: false,
|
||||
dragged_issue_id: None,
|
||||
drag_locked: false,
|
||||
},
|
||||
modals: vec![],
|
||||
project: None,
|
||||
|
@ -10,9 +10,9 @@ use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||
use crate::shared::styled_input::StyledInput;
|
||||
use crate::shared::styled_select::StyledSelectChange;
|
||||
use crate::shared::{drag_ev, inner_layout, ToNode};
|
||||
use crate::{FieldId, Msg};
|
||||
use crate::{FieldId, Msg, APP};
|
||||
|
||||
pub fn update(msg: Msg, model: &mut crate::model::Model, _orders: &mut impl Orders<Msg>) {
|
||||
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
||||
match msg {
|
||||
Msg::ChangePage(Page::Project)
|
||||
| Msg::ChangePage(Page::AddIssue)
|
||||
@ -41,6 +41,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, _orders: &mut impl Orde
|
||||
model.issues.push(is);
|
||||
}
|
||||
}
|
||||
orders.skip().send_msg(Msg::ModalDropped);
|
||||
}
|
||||
Msg::ToggleAboutTooltip => {
|
||||
model.project_page.about_tooltip_visible = !model.project_page.about_tooltip_visible;
|
||||
@ -97,13 +98,73 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, _orders: &mut impl Orde
|
||||
Msg::IssueDragStopped(_) => {
|
||||
model.project_page.dragged_issue_id = None;
|
||||
}
|
||||
Msg::ExchangePosition(issue_bellow_id) => {
|
||||
if model.project_page.drag_locked {
|
||||
return;
|
||||
}
|
||||
log!(issue_bellow_id);
|
||||
log!(model.project_page.dragged_issue_id);
|
||||
let dragged_id = match model.project_page.dragged_issue_id {
|
||||
Some(id) => id,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let mut below = None;
|
||||
let mut dragged = None;
|
||||
let mut issues = vec![];
|
||||
std::mem::swap(&mut issues, &mut model.issues);
|
||||
|
||||
for issue in issues.into_iter() {
|
||||
match issue.id {
|
||||
id if id == issue_bellow_id => below = Some(issue),
|
||||
id if id == dragged_id => dragged = Some(issue),
|
||||
_ => model.issues.push(issue),
|
||||
};
|
||||
}
|
||||
|
||||
let mut below = match below {
|
||||
Some(below) => below,
|
||||
_ => return,
|
||||
};
|
||||
let mut dragged = match dragged {
|
||||
Some(issue) => issue,
|
||||
_ => {
|
||||
model.issues.push(below);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if dragged.status == below.status {
|
||||
std::mem::swap(&mut dragged.list_position, &mut below.list_position);
|
||||
below.status = dragged.status.clone();
|
||||
} else {
|
||||
below.list_position = model
|
||||
.issues
|
||||
.iter()
|
||||
.map(|i| i.list_position)
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
+ 1;
|
||||
std::mem::swap(&mut dragged.list_position, &mut below.list_position);
|
||||
below.status = dragged.status.clone();
|
||||
}
|
||||
model.issues.push(below);
|
||||
model.issues.push(dragged);
|
||||
model
|
||||
.issues
|
||||
.sort_by(|a, b| a.list_position.cmp(&b.list_position));
|
||||
model.project_page.drag_locked = true;
|
||||
// log!(model.issues);
|
||||
}
|
||||
Msg::UnlockDragOver => {
|
||||
model.project_page.drag_locked = false;
|
||||
}
|
||||
Msg::IssueDropZone(status) => match model.project_page.dragged_issue_id.as_ref().cloned() {
|
||||
Some(issue_id) => {
|
||||
let mut position = 0f64;
|
||||
let mut position = 0;
|
||||
let mut found: Option<&mut Issue> = None;
|
||||
for issue in model.issues.iter_mut() {
|
||||
if issue.status == status {
|
||||
position += 1f64;
|
||||
position += 1;
|
||||
}
|
||||
if issue.id == issue_id {
|
||||
found = Some(issue);
|
||||
@ -116,7 +177,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, _orders: &mut impl Orde
|
||||
};
|
||||
|
||||
issue.status = status.clone();
|
||||
issue.list_position = position + 1f64;
|
||||
issue.list_position = position + 1;
|
||||
|
||||
let payload = UpdateIssuePayload {
|
||||
title: Some(issue.title.clone()),
|
||||
@ -138,7 +199,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, _orders: &mut impl Orde
|
||||
_ => error!("Drag stopped before drop :("),
|
||||
},
|
||||
Msg::DeleteIssue(issue_id) => {
|
||||
send_ws_msg(jirs_data::WsMsg::IssueDeleted(issue_id));
|
||||
send_ws_msg(jirs_data::WsMsg::IssueDeleteRequest(issue_id));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@ -356,10 +417,25 @@ fn project_issue(model: &Model, issue: &Issue) -> Node<Msg> {
|
||||
let issue_id = issue.id;
|
||||
let drag_started = drag_ev(Ev::DragStart, move |_| Msg::IssueDragStarted(issue_id));
|
||||
let drag_stopped = drag_ev(Ev::DragEnd, move |_| Msg::IssueDragStopped(issue_id));
|
||||
let drag_over_handler = drag_ev(Ev::DragOver, move |ev| {
|
||||
ev.prevent_default();
|
||||
ev.stop_propagation();
|
||||
seed::set_timeout(
|
||||
Box::new(|| {
|
||||
let app = match unsafe { APP.as_mut().unwrap() }.write() {
|
||||
Ok(app) => app,
|
||||
_ => return,
|
||||
};
|
||||
app.update(Msg::UnlockDragOver);
|
||||
}),
|
||||
3000,
|
||||
);
|
||||
Msg::ExchangePosition(issue_id)
|
||||
});
|
||||
|
||||
let mut class_list = vec!["issue"];
|
||||
let class_list = vec!["issue"];
|
||||
if Some(issue_id) == model.project_page.dragged_issue_id {
|
||||
class_list.push("hidden");
|
||||
// class_list.push("hidden");
|
||||
}
|
||||
|
||||
let href = format!("/issues/{id}", id = issue_id);
|
||||
@ -370,6 +446,7 @@ fn project_issue(model: &Model, issue: &Issue) -> Node<Msg> {
|
||||
div![
|
||||
attrs![At::Class => class_list.join(" "), At::Draggable => true],
|
||||
drag_stopped,
|
||||
drag_over_handler,
|
||||
p![attrs![At::Class => "title"], issue.title,],
|
||||
div![
|
||||
attrs![At::Class => "bottom"],
|
||||
|
@ -25,7 +25,9 @@ pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>
|
||||
model.user = Some(user.clone());
|
||||
}
|
||||
Msg::WsMsg(WsMsg::ProjectIssuesLoaded(v)) => {
|
||||
model.issues = v.clone();
|
||||
let mut v = v.clone();
|
||||
v.sort_by(|a, b| (a.list_position as i64).cmp(&(b.list_position as i64)));
|
||||
model.issues = v;
|
||||
}
|
||||
Msg::WsMsg(WsMsg::ProjectUsersLoaded(v)) => {
|
||||
model.users = v.clone();
|
||||
|
@ -223,7 +223,7 @@ pub struct FullIssue {
|
||||
pub issue_type: IssueType,
|
||||
pub status: IssueStatus,
|
||||
pub priority: IssuePriority,
|
||||
pub list_position: f64,
|
||||
pub list_position: i32,
|
||||
pub description: Option<String>,
|
||||
pub description_text: Option<String>,
|
||||
pub estimate: Option<i32>,
|
||||
@ -282,7 +282,7 @@ pub struct Issue {
|
||||
pub issue_type: IssueType,
|
||||
pub status: IssueStatus,
|
||||
pub priority: IssuePriority,
|
||||
pub list_position: f64,
|
||||
pub list_position: i32,
|
||||
pub description: Option<String>,
|
||||
pub description_text: Option<String>,
|
||||
pub estimate: Option<i32>,
|
||||
@ -340,7 +340,7 @@ pub struct UpdateIssuePayload {
|
||||
pub issue_type: Option<IssueType>,
|
||||
pub status: Option<IssueStatus>,
|
||||
pub priority: Option<IssuePriority>,
|
||||
pub list_position: Option<f64>,
|
||||
pub list_position: Option<i32>,
|
||||
pub description: Option<Option<String>>,
|
||||
pub description_text: Option<Option<String>>,
|
||||
pub estimate: Option<Option<i32>>,
|
||||
|
@ -0,0 +1 @@
|
||||
alter table issues alter column list_position set data type number;
|
@ -0,0 +1 @@
|
||||
alter table issues alter column list_position set data type integer;
|
@ -76,7 +76,7 @@ pub struct UpdateIssue {
|
||||
pub issue_type: Option<IssueType>,
|
||||
pub status: Option<IssueStatus>,
|
||||
pub priority: Option<IssuePriority>,
|
||||
pub list_position: Option<f64>,
|
||||
pub list_position: Option<i32>,
|
||||
pub description: Option<Option<String>>,
|
||||
pub description_text: Option<Option<String>>,
|
||||
pub estimate: Option<Option<i32>>,
|
||||
@ -232,8 +232,8 @@ impl Handler<CreateIssue> for DbExecutor {
|
||||
|
||||
let list_position = issues
|
||||
.filter(status.eq(IssueStatus::Backlog))
|
||||
.select(sql("max(list_position) + 1.0"))
|
||||
.get_result::<f64>(conn)
|
||||
.select(sql("max(list_position) + 1"))
|
||||
.get_result::<i32>(conn)
|
||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||
|
||||
let form = crate::models::CreateIssueForm {
|
||||
|
@ -50,7 +50,7 @@ pub struct Issue {
|
||||
pub issue_type: IssueType,
|
||||
pub status: IssueStatus,
|
||||
pub priority: IssuePriority,
|
||||
pub list_position: f64,
|
||||
pub list_position: i32,
|
||||
pub description: Option<String>,
|
||||
pub description_text: Option<String>,
|
||||
pub estimate: Option<i32>,
|
||||
@ -120,7 +120,7 @@ pub struct CreateIssueForm {
|
||||
pub issue_type: IssueType,
|
||||
pub status: IssueStatus,
|
||||
pub priority: IssuePriority,
|
||||
pub list_position: f64,
|
||||
pub list_position: i32,
|
||||
pub description: Option<String>,
|
||||
pub description_text: Option<String>,
|
||||
pub estimate: Option<i32>,
|
||||
|
@ -128,10 +128,10 @@ table! {
|
||||
priority -> IssuePriorityType,
|
||||
/// The `list_position` column of the `issues` table.
|
||||
///
|
||||
/// Its SQL type is `Float8`.
|
||||
/// Its SQL type is `Int4`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
list_position -> Float8,
|
||||
list_position -> Int4,
|
||||
/// The `description` column of the `issues` table.
|
||||
///
|
||||
/// Its SQL type is `Nullable<Text>`.
|
||||
@ -351,4 +351,11 @@ joinable!(issues -> users (reporter_id));
|
||||
joinable!(tokens -> users (user_id));
|
||||
joinable!(users -> projects (project_id));
|
||||
|
||||
allow_tables_to_appear_in_same_query!(comments, issue_assignees, issues, projects, tokens, users,);
|
||||
allow_tables_to_appear_in_same_query!(
|
||||
comments,
|
||||
issue_assignees,
|
||||
issues,
|
||||
projects,
|
||||
tokens,
|
||||
users,
|
||||
);
|
||||
|
@ -34,10 +34,7 @@ impl WsMessageSender for ws::WebsocketContext<WebSocketActor> {
|
||||
|
||||
impl WebSocketActor {
|
||||
fn handle_ws_msg(&mut self, msg: WsMsg) -> WsResult {
|
||||
// use futures::executor::LocalPool;
|
||||
use futures::executor::block_on;
|
||||
// let mut pool = LocalPool::new();
|
||||
// pool.run_until();
|
||||
|
||||
if msg != WsMsg::Ping && msg != WsMsg::Pong {
|
||||
info!("(2)incoming message: {:?}", msg);
|
||||
@ -54,7 +51,7 @@ impl WebSocketActor {
|
||||
WsMsg::ProjectIssuesRequest => block_on(self.load_issues())?,
|
||||
WsMsg::ProjectUsersRequest => block_on(self.load_project_users())?,
|
||||
_ => {
|
||||
error!("Failed to resolve message");
|
||||
error!("No handle for {:?} specified", msg);
|
||||
None
|
||||
}
|
||||
};
|
||||
@ -124,36 +121,22 @@ impl WebSocketActor {
|
||||
payload: jirs_data::UpdateIssuePayload,
|
||||
) -> WsResult {
|
||||
self.current_user()?;
|
||||
let jirs_data::UpdateIssuePayload {
|
||||
title,
|
||||
issue_type,
|
||||
status,
|
||||
priority,
|
||||
list_position,
|
||||
description,
|
||||
description_text,
|
||||
estimate,
|
||||
time_spent,
|
||||
time_remaining,
|
||||
project_id,
|
||||
user_ids,
|
||||
} = payload;
|
||||
let m = match self
|
||||
.db
|
||||
.send(UpdateIssue {
|
||||
issue_id,
|
||||
title,
|
||||
issue_type,
|
||||
status,
|
||||
priority,
|
||||
list_position,
|
||||
description,
|
||||
description_text,
|
||||
estimate,
|
||||
time_spent,
|
||||
time_remaining,
|
||||
project_id,
|
||||
user_ids,
|
||||
title: payload.title,
|
||||
issue_type: payload.issue_type,
|
||||
status: payload.status,
|
||||
priority: payload.priority,
|
||||
list_position: payload.list_position,
|
||||
description: payload.description,
|
||||
description_text: payload.description_text,
|
||||
estimate: payload.estimate,
|
||||
time_spent: payload.time_spent,
|
||||
time_remaining: payload.time_remaining,
|
||||
project_id: payload.project_id,
|
||||
user_ids: payload.user_ids,
|
||||
})
|
||||
.await
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user