Fix drop create modal, add deploy info
This commit is contained in:
parent
fce3bb727f
commit
f0c275120a
@ -180,8 +180,7 @@ cargo run --bin jirs_server
|
||||
```bash
|
||||
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
cd jirs_client
|
||||
yarn
|
||||
./scripts/prod.sh
|
||||
./web/scripts/prod.sh
|
||||
```
|
||||
|
||||
```bash
|
||||
|
@ -193,7 +193,11 @@ impl CreateIssue {
|
||||
crate::issue_statuses::LoadIssueStatuses {
|
||||
project_id: msg.project_id,
|
||||
}
|
||||
.execute(conn)?
|
||||
.execute(conn)
|
||||
.map_err(|e| {
|
||||
common::log::error!("Failed to find issue status. {:?}", e);
|
||||
e
|
||||
})?
|
||||
.first()
|
||||
.ok_or(crate::DatabaseError::Issue(
|
||||
crate::IssueError::NoIssueStatuses,
|
||||
@ -223,12 +227,22 @@ impl CreateIssue {
|
||||
reporter_id: msg.reporter_id,
|
||||
epic_id: msg.epic_id,
|
||||
}
|
||||
.execute(conn)?;
|
||||
crate::issue_assignees::AsignMultiple {
|
||||
issue_id: issue.id,
|
||||
user_ids: assign_users,
|
||||
.execute(conn)
|
||||
.map_err(|e| {
|
||||
common::log::error!("Failed to insert issue. {:?}", e);
|
||||
e
|
||||
})?;
|
||||
if !assign_users.is_empty() {
|
||||
crate::issue_assignees::AsignMultiple {
|
||||
issue_id: issue.id,
|
||||
user_ids: assign_users,
|
||||
}
|
||||
.execute(conn)
|
||||
.map_err(|e| {
|
||||
common::log::error!("Failed to apply multiple assignee to issue. {:?}", e);
|
||||
e
|
||||
})?;
|
||||
}
|
||||
.execute(conn)?;
|
||||
issues.find(issue.id).get_result(conn).map_err(|e| {
|
||||
common::log::error!("{:?}", e);
|
||||
crate::DatabaseError::GenericFailure(
|
||||
|
127
docs/Deploy.md
Normal file
127
docs/Deploy.md
Normal file
@ -0,0 +1,127 @@
|
||||
# Deploy with Nginx
|
||||
|
||||
You can deploy easily JIRS to any PC including Raspberry PI. To do this you will need compile it from source code.
|
||||
|
||||
We will use following setup, but you can modify it.
|
||||
|
||||
* `issues.example.com` - domain
|
||||
* `postgres://postgres@192.168.1.144:5432/jirs` - database on other machine and within inner network
|
||||
|
||||
* `/var/jirs` - main directory
|
||||
* `/var/jirs/clone` - cloned source code
|
||||
* `/var/jirs/web` - All static assets including wasm library
|
||||
* `/var/jirs/config` - config files
|
||||
* `/var/jirs/uploads` - uploaded files
|
||||
|
||||
### JIRS Config files
|
||||
|
||||
* `config/db.toml` (REQUIRED)
|
||||
|
||||
```toml
|
||||
concurrency = 2
|
||||
database_url = "postgres://postgres@192.168.1.144:5432/jirs"
|
||||
```
|
||||
|
||||
* `config/web.toml` (public_path is required)
|
||||
|
||||
```toml
|
||||
concurrency = 2
|
||||
port = "5000"
|
||||
bind = "0.0.0.0"
|
||||
ssl = false
|
||||
tmp_dir = "/tmp"
|
||||
public_path = "issues.example.com"
|
||||
```
|
||||
|
||||
* `config/fs.toml` (must match nginx config)
|
||||
|
||||
```toml
|
||||
store_path = "./uploads"
|
||||
client_path = "/uploads"
|
||||
tmp_path = "/tmp"
|
||||
concurrency = 2
|
||||
```
|
||||
|
||||
* `config/mail.toml` (REQUIRED)
|
||||
|
||||
```toml
|
||||
concurrency = 2
|
||||
user = "apikey"
|
||||
pass = "SG.ARJL0wAxQk-LLJca9FJ5Lg.ahs7dyashd8a7shd7ahsd978h"
|
||||
host = "smtp.sendgrid.net"
|
||||
from = "admin@issues.example.com"
|
||||
```
|
||||
|
||||
### NGINX config file
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name issues.example.com;
|
||||
|
||||
root /var/jirs/web;
|
||||
try_files $uri $uri/index.html index.html;
|
||||
|
||||
location ~ /uploads/ {
|
||||
root /var/jirs;
|
||||
}
|
||||
|
||||
location ~ .js {
|
||||
add_header 'Content-Type' 'application/javascript';
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
}
|
||||
location ~ .css {
|
||||
add_header 'Content-Type' 'text/css';
|
||||
}
|
||||
location ~ .wasm {
|
||||
add_header 'Content-Type' 'application/wasm';
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
}
|
||||
|
||||
location /ws/ {
|
||||
proxy_pass http://localhost:5000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
location /avatar {
|
||||
proxy_pass http://localhost:5000;
|
||||
}
|
||||
location ~ / {
|
||||
add_header 'Content-Type' 'text/html';
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
root /var/jirs/web;
|
||||
try_files $uri $uri/index.html /index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Compile application
|
||||
|
||||
```bash
|
||||
cargo install --force wasm-pack
|
||||
cargo install --force rsass
|
||||
```
|
||||
|
||||
Ensure `$HOME/.cargo/bin` is in `$PATH`
|
||||
|
||||
* Compile server
|
||||
|
||||
```bash
|
||||
cargo build --bin jirs_server --release --no-default-features --features local-storage
|
||||
cp ./target/release/jirs_server /usr/bin/jirs_server
|
||||
```
|
||||
|
||||
* Compile web client
|
||||
|
||||
```bash
|
||||
./web/scripts/prod.sh
|
||||
|
||||
cp -r /tmp/wasm/* /var/jirs/web
|
||||
```
|
||||
|
||||
If it fails (there is no wasm-opt for Raspberry PI) you must disable wasm-opt or compile it on any other PC and just
|
||||
copy everything to `/var/jirs/web`
|
||||
|
||||
|
103
web/src/lib.rs
103
web/src/lib.rs
@ -156,60 +156,59 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
||||
if model.ws.is_none() {
|
||||
open_socket(model, orders);
|
||||
}
|
||||
let mut msg = msg;
|
||||
|
||||
let msg = match msg {
|
||||
Msg::WebSocketChange(change) => {
|
||||
match change {
|
||||
WebSocketChanged::WebSocketOpened => {
|
||||
flush_queue(model, orders);
|
||||
send_ws_msg(WsMsg::Ping, model.ws.as_ref(), orders);
|
||||
authorize_or_redirect(model, orders);
|
||||
orders.skip();
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::SendPing => {
|
||||
send_ws_msg(WsMsg::Ping, model.ws.as_ref(), orders);
|
||||
orders.skip();
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::WebSocketMessage(incoming) => {
|
||||
orders.perform_cmd(read_incoming(incoming));
|
||||
orders.skip();
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::WsMsg(ws_msg) => {
|
||||
ws::update(ws_msg, model, orders);
|
||||
orders.skip();
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::WebSocketMessageLoaded(v) => {
|
||||
match bincode::deserialize(v.as_slice()) {
|
||||
Ok(WsMsg::Ping | WsMsg::Pong) => {
|
||||
orders.skip().perform_cmd(cmds::timeout(300, || {
|
||||
Msg::WebSocketChange(WebSocketChanged::SendPing)
|
||||
}));
|
||||
}
|
||||
Ok(m) => {
|
||||
log::info!("INCOMING {:?}", m);
|
||||
orders
|
||||
.skip()
|
||||
.send_msg(Msg::WebSocketChange(WebSocketChanged::WsMsg(m)));
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::WebSocketClosed => {
|
||||
open_socket(model, orders);
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::Bounced(ws_msg) => {
|
||||
model.ws_queue.push(ws_msg);
|
||||
open_socket(model, orders);
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
Msg::WebSocketChange(change) => match change {
|
||||
WebSocketChanged::WebSocketOpened => {
|
||||
flush_queue(model, orders);
|
||||
send_ws_msg(WsMsg::Ping, model.ws.as_ref(), orders);
|
||||
authorize_or_redirect(model, orders);
|
||||
orders.skip();
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::SendPing => {
|
||||
send_ws_msg(WsMsg::Ping, model.ws.as_ref(), orders);
|
||||
orders.skip();
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::WebSocketMessage(incoming) => {
|
||||
orders.perform_cmd(read_incoming(incoming));
|
||||
orders.skip();
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::WsMsg(mut ws_msg) => {
|
||||
ws::update(&mut ws_msg, model, orders);
|
||||
orders.skip();
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(ws_msg))
|
||||
}
|
||||
WebSocketChanged::WebSocketMessageLoaded(v) => {
|
||||
match bincode::deserialize(v.as_slice()) {
|
||||
Ok(WsMsg::Ping | WsMsg::Pong) => {
|
||||
orders.skip().perform_cmd(cmds::timeout(300, || {
|
||||
Msg::WebSocketChange(WebSocketChanged::SendPing)
|
||||
}));
|
||||
}
|
||||
Ok(m) => {
|
||||
log::info!("INCOMING {:?}", m);
|
||||
orders
|
||||
.skip()
|
||||
.send_msg(Msg::WebSocketChange(WebSocketChanged::WsMsg(m)));
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::WebSocketClosed => {
|
||||
open_socket(model, orders);
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::Bounced(ws_msg) => {
|
||||
model.ws_queue.push(ws_msg);
|
||||
open_socket(model, orders);
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => msg,
|
||||
};
|
||||
|
||||
|
@ -1,14 +1,41 @@
|
||||
pub fn host_url() -> &'static str {
|
||||
if cfg!(debug_assertions) {
|
||||
"http://localhost:5000"
|
||||
} else {
|
||||
"https://localhost:5000"
|
||||
static mut HOST: String = String::new();
|
||||
static mut WS: String = String::new();
|
||||
|
||||
fn ensure_host() {
|
||||
unsafe {
|
||||
if HOST.is_empty() {
|
||||
HOST = format!(
|
||||
"{}//{}",
|
||||
seed::window().location().protocol().unwrap(),
|
||||
seed::window().location().host().unwrap()
|
||||
);
|
||||
let host: String = seed::window().location().host().unwrap();
|
||||
let is_local = host.ends_with("lvh.me")
|
||||
|| host.contains("localhost")
|
||||
|| host.starts_with("127.")
|
||||
|| host.contains("0.0.0.0");
|
||||
WS = format!(
|
||||
"{}//{}/ws/",
|
||||
match seed::window().location().protocol().unwrap().as_str() {
|
||||
"http:" => "ws:",
|
||||
_ => "wss:",
|
||||
},
|
||||
if is_local {
|
||||
"localhost:5000"
|
||||
} else {
|
||||
host.as_str()
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn host_url() -> &'static str {
|
||||
ensure_host();
|
||||
unsafe { HOST.as_str() }
|
||||
}
|
||||
|
||||
pub fn ws_url() -> &'static str {
|
||||
if cfg!(debug_assertions) {
|
||||
"ws://localhost:5000/ws/"
|
||||
} else {
|
||||
"wss://localhost:5000/ws/"
|
||||
}
|
||||
}
|
||||
ensure_host();
|
||||
unsafe { WS.as_str() }
|
||||
}
|
||||
|
@ -113,7 +113,10 @@ fn push_modal(modal_type: &ModalType, model: &mut Model, orders: &mut impl Order
|
||||
}
|
||||
|
||||
fn drop_modal(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
let modal = model.modal_stack_mut().pop().unwrap();
|
||||
let modal = match model.modal_stack_mut().pop() {
|
||||
Some(modal) => modal,
|
||||
_ => return,
|
||||
};
|
||||
let modals = model.modals_mut();
|
||||
match modal {
|
||||
ModalType::AddIssue(_) => {
|
||||
|
@ -45,9 +45,10 @@ pub fn send_ws_msg(msg: WsMsg, ws: Option<&WebSocket>, orders: &mut impl Orders<
|
||||
return;
|
||||
}
|
||||
};
|
||||
let binary = bincode::serialize(&msg).unwrap();
|
||||
ws.send_bytes(binary.as_slice())
|
||||
.expect("Failed to send ws msg");
|
||||
let binary = bincode::serialize(&msg).unwrap_or_default();
|
||||
if let Err(e) = ws.send_bytes(binary.as_slice()) {
|
||||
log::error!("Failed to send ws msg. {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_socket(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
@ -92,12 +93,12 @@ pub async fn read_incoming(msg: WebSocketMessage) -> Msg {
|
||||
Msg::WebSocketChange(WebSocketChanged::WebSocketMessageLoaded(bytes))
|
||||
}
|
||||
|
||||
pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
match msg {
|
||||
// auth
|
||||
WsMsg::Session(WsMsgSession::AuthorizeLoaded(Ok((user, setting)))) => {
|
||||
model.user = Some(user);
|
||||
model.user_settings = Some(setting);
|
||||
model.user = Some(user.clone());
|
||||
model.user_settings = Some(setting.clone());
|
||||
|
||||
if is_non_logged_area() {
|
||||
go_to_board(orders);
|
||||
@ -134,7 +135,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
// project
|
||||
WsMsg::Project(WsMsgProject::ProjectsLoaded(v)) => {
|
||||
model.projects = v;
|
||||
model.projects = std::mem::take(v);
|
||||
init_current_project(model, orders);
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Project,
|
||||
@ -145,7 +146,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
// user projects
|
||||
WsMsg::UserProjectsLoaded(v) => {
|
||||
model.current_user_project = v.iter().find(|up| up.is_current).cloned();
|
||||
model.user_projects = v;
|
||||
model.user_projects = std::mem::take(v);
|
||||
init_current_project(model, orders);
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::UserProject,
|
||||
@ -160,7 +161,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
up.is_current = up.id == user_project.id;
|
||||
model.user_projects.push(up);
|
||||
}
|
||||
model.current_user_project = Some(user_project);
|
||||
model.current_user_project = Some(user_project.clone());
|
||||
init_current_project(model, orders);
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::UserProject,
|
||||
@ -170,23 +171,23 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
// user settings
|
||||
WsMsg::User(WsMsgUser::AvatarUrlChanged(id, url)) => {
|
||||
if let Some(user) = model.user.as_mut().filter(|u| u.id == id) {
|
||||
if let Some(user) = model.user.as_mut().filter(|u| u.id == *id) {
|
||||
user.avatar_url = Some(url.clone());
|
||||
}
|
||||
if let Some(user) = model.users_by_id.get_mut(&id) {
|
||||
user.avatar_url = Some(url.clone());
|
||||
}
|
||||
if let Some(user) = model.users.iter_mut().find(|u| u.id == id) {
|
||||
user.avatar_url = Some(url);
|
||||
if let Some(user) = model.users.iter_mut().find(|u| u.id == *id) {
|
||||
user.avatar_url = Some(url.clone());
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::User,
|
||||
OperationKind::SingleModified,
|
||||
Some(id),
|
||||
Some(*id),
|
||||
));
|
||||
}
|
||||
WsMsg::User(WsMsgUser::UserSettingUpdated(setting)) => {
|
||||
model.user_settings = Some(setting);
|
||||
model.user_settings = Some(setting.clone());
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::UserSetting,
|
||||
OperationKind::SingleModified,
|
||||
@ -196,7 +197,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
|
||||
// issue statuses
|
||||
WsMsg::IssueStatus(WsMsgIssueStatus::IssueStatusesLoaded(v)) => {
|
||||
model.issue_statuses = v;
|
||||
model.issue_statuses = std::mem::take(v);
|
||||
model
|
||||
.issue_statuses
|
||||
.sort_by(|a, b| a.position.cmp(&b.position));
|
||||
@ -208,7 +209,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
WsMsg::IssueStatus(WsMsgIssueStatus::IssueStatusCreated(is)) => {
|
||||
let id = is.id;
|
||||
model.issue_statuses.push(is);
|
||||
model.issue_statuses.push(is.clone());
|
||||
model
|
||||
.issue_statuses
|
||||
.sort_by(|a, b| a.position.cmp(&b.position));
|
||||
@ -218,10 +219,10 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
Some(id),
|
||||
));
|
||||
}
|
||||
WsMsg::IssueStatus(WsMsgIssueStatus::IssueStatusUpdated(mut changed)) => {
|
||||
WsMsg::IssueStatus(WsMsgIssueStatus::IssueStatusUpdated(changed)) => {
|
||||
let id = changed.id;
|
||||
if let Some(idx) = model.issue_statuses.iter().position(|c| c.id == changed.id) {
|
||||
std::mem::swap(&mut model.issue_statuses[idx], &mut changed);
|
||||
std::mem::swap(&mut model.issue_statuses[idx], changed);
|
||||
}
|
||||
model
|
||||
.issue_statuses
|
||||
@ -236,7 +237,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
let mut old = vec![];
|
||||
std::mem::swap(&mut model.issue_statuses, &mut old);
|
||||
for is in old {
|
||||
if is.id != dropped_id {
|
||||
if is.id != *dropped_id {
|
||||
model.issue_statuses.push(is);
|
||||
}
|
||||
}
|
||||
@ -246,11 +247,11 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::IssueStatus,
|
||||
OperationKind::SingleRemoved,
|
||||
Some(dropped_id),
|
||||
Some(*dropped_id),
|
||||
));
|
||||
}
|
||||
// issues
|
||||
WsMsg::Project(WsMsgProject::ProjectIssuesLoaded(mut v)) => {
|
||||
WsMsg::Project(WsMsgProject::ProjectIssuesLoaded(v)) => {
|
||||
v.sort_by(|a, b| (a.list_position as i64).cmp(&(b.list_position as i64)));
|
||||
{
|
||||
let _ = std::mem::replace(model.issues_mut(), v.clone());
|
||||
@ -266,12 +267,24 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
None,
|
||||
));
|
||||
}
|
||||
WsMsg::Issue(WsMsgIssue::IssueCreated(issue)) => {
|
||||
let id = issue.id;
|
||||
model.issues_by_id.insert(id, issue.clone());
|
||||
if let Some(idx) = model.issues().iter().position(|i| i.id == issue.id) {
|
||||
let _ = std::mem::replace(&mut model.issues_mut()[idx], issue.clone());
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Issue,
|
||||
OperationKind::SingleCreated,
|
||||
Some(id),
|
||||
));
|
||||
}
|
||||
WsMsg::Issue(WsMsgIssue::IssueUpdated(issue)) => {
|
||||
let id = issue.id;
|
||||
model.issues_by_id.remove(&id);
|
||||
model.issues_by_id.insert(id, issue.clone());
|
||||
if let Some(idx) = model.issues().iter().position(|i| i.id == issue.id) {
|
||||
let _ = std::mem::replace(&mut model.issues_mut()[idx], issue);
|
||||
let _ = std::mem::replace(&mut model.issues_mut()[idx], issue.clone());
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Issue,
|
||||
@ -280,10 +293,9 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
));
|
||||
}
|
||||
WsMsg::Issue(WsMsgIssue::IssueDeleted(id, _count)) => {
|
||||
let mut old = vec![];
|
||||
std::mem::swap(model.issues_mut(), &mut old);
|
||||
let old = std::mem::take(model.issues_mut());
|
||||
for is in old {
|
||||
if is.id == id {
|
||||
if is.id == *id {
|
||||
continue;
|
||||
}
|
||||
model.issues_mut().push(is);
|
||||
@ -291,7 +303,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Issue,
|
||||
OperationKind::SingleRemoved,
|
||||
Some(id),
|
||||
Some(*id),
|
||||
));
|
||||
}
|
||||
// users
|
||||
@ -308,7 +320,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
));
|
||||
}
|
||||
// comments
|
||||
WsMsg::Comment(WsMsgComment::IssueCommentsLoaded(mut comments)) => {
|
||||
WsMsg::Comment(WsMsgComment::IssueCommentsLoaded(comments)) => {
|
||||
let issue_id = match &model.modals().edit_issue {
|
||||
Some(modal) => modal.id,
|
||||
_ => return,
|
||||
@ -318,7 +330,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
comments.sort_by(|a, b| a.updated_at.cmp(&b.updated_at));
|
||||
model.comments = comments.clone();
|
||||
for comment in comments {
|
||||
for comment in std::mem::take(comments) {
|
||||
model.comments_by_id.insert(comment.id, comment);
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
@ -331,7 +343,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
let comment_id = comment.id;
|
||||
if let Some(idx) = model.comments.iter().position(|c| c.id == comment.id) {
|
||||
let _ = std::mem::replace(&mut model.comments[idx], comment.clone());
|
||||
model.comments_by_id.insert(comment.id, comment);
|
||||
model.comments_by_id.insert(comment.id, comment.clone());
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Comment,
|
||||
@ -340,20 +352,20 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
));
|
||||
}
|
||||
WsMsg::Comment(WsMsgComment::CommentDeleted(comment_id, _count)) => {
|
||||
if let Some(idx) = model.comments.iter().position(|c| c.id == comment_id) {
|
||||
if let Some(idx) = model.comments.iter().position(|c| c.id == *comment_id) {
|
||||
model.comments.remove(idx);
|
||||
}
|
||||
model.comments_by_id.remove(&comment_id);
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Comment,
|
||||
OperationKind::SingleRemoved,
|
||||
Some(comment_id),
|
||||
Some(*comment_id),
|
||||
));
|
||||
}
|
||||
// messages
|
||||
WsMsg::Message(WsMsgMessage::MessageUpdated(mut received)) => {
|
||||
WsMsg::Message(WsMsgMessage::MessageUpdated(received)) => {
|
||||
if let Some(idx) = model.messages.iter().position(|m| m.id == received.id) {
|
||||
std::mem::swap(&mut model.messages[idx], &mut received);
|
||||
std::mem::swap(&mut model.messages[idx], received);
|
||||
}
|
||||
model.messages.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
@ -363,7 +375,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
));
|
||||
}
|
||||
WsMsg::Message(WsMsgMessage::MessagesLoaded(v)) => {
|
||||
model.messages = v;
|
||||
model.messages = std::mem::take(v);
|
||||
model.messages.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Message,
|
||||
@ -372,14 +384,14 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
));
|
||||
}
|
||||
WsMsg::Message(WsMsgMessage::MessageMarkedSeen(id, _count)) => {
|
||||
if let Some(idx) = model.messages.iter().position(|m| m.id == id) {
|
||||
if let Some(idx) = model.messages.iter().position(|m| m.id == *id) {
|
||||
model.messages.remove(idx);
|
||||
}
|
||||
model.messages.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Message,
|
||||
OperationKind::SingleRemoved,
|
||||
Some(id),
|
||||
Some(*id),
|
||||
));
|
||||
}
|
||||
|
||||
@ -387,7 +399,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
WsMsg::Epic(WsMsgEpic::EpicsLoaded(epics)) => {
|
||||
model.epics = epics.clone();
|
||||
for epic in epics {
|
||||
model.epics_by_id.insert(epic.id, epic);
|
||||
model.epics_by_id.insert(epic.id, epic.clone());
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Epic,
|
||||
@ -399,7 +411,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
let id = epic.id;
|
||||
model.epics.push(epic.clone());
|
||||
model.epics.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
model.epics_by_id.insert(epic.id, epic);
|
||||
model.epics_by_id.insert(epic.id, epic.clone());
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Epic,
|
||||
OperationKind::SingleCreated,
|
||||
@ -411,7 +423,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
if let Some(idx) = model.epics.iter().position(|e| e.id == epic.id) {
|
||||
let _ = std::mem::replace(&mut model.epics[idx], epic.clone());
|
||||
}
|
||||
model.epics_by_id.insert(epic.id, epic);
|
||||
model.epics_by_id.insert(epic.id, epic.clone());
|
||||
model.epics.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Epic,
|
||||
@ -420,7 +432,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
));
|
||||
}
|
||||
WsMsg::Epic(WsMsgEpic::EpicDeleted(id, _count)) => {
|
||||
if let Some(idx) = model.epics.iter().position(|e| e.id == id) {
|
||||
if let Some(idx) = model.epics.iter().position(|e| e.id == *id) {
|
||||
model.epics.remove(idx);
|
||||
}
|
||||
model.epics_by_id.remove(&id);
|
||||
@ -428,7 +440,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Epic,
|
||||
OperationKind::SingleRemoved,
|
||||
Some(id),
|
||||
Some(*id),
|
||||
));
|
||||
}
|
||||
WsMsg::Session(WsMsgSession::AuthenticateSuccess) => {
|
||||
@ -436,7 +448,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
page.login_success = true;
|
||||
}
|
||||
WsMsg::Session(WsMsgSession::BindTokenOk(access_token)) => {
|
||||
match write_auth_token(Some(access_token)) {
|
||||
match write_auth_token(Some(*access_token)) {
|
||||
Ok(msg) => {
|
||||
orders.skip().send_msg(msg);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user