Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/1.1.2 #25

Merged
merged 7 commits into from
Dec 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "petompp-web-front"
version = "1.1.0"
version = "1.1.2"
edition = "2021"

[profile.release]
Expand All @@ -24,7 +24,6 @@ regex = "1.10"
reqwasm = "0.5"
serde = "1.0"
serde_json = "1.0"
serde_urlencoded = "0.7"
serde_yaml = "0.9"
strum = { version = "0.25", features = ["derive"] }
timeago = "0.4"
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,14 @@ pub fn markdown_display(props: &MarkdownDisplayProps) -> Html {
The display element is styled with *prose* class from [Typography](https://tailwindcss.com/docs/typography-plugin) plugin for Tailwind, so it looks very nice for majority of MD features.
Also some of the `<a>` elements contained have to be adjusted so local hrefs do not reload a page in `make_links_clickable` function.

### Markdown editor
### Resources editor

Although hidden for non-admin users, there's an editor built into the app for the pages content.
For the time being it is only accessible by navigating directly to it at https://petompp.net/editor/home-content/en.
Although hidden for non-admin users, there's an editor built into the app for the pages content and blog posts.
For the time being it is only accessible by navigating directly to it at https://petompp.net/editor.

The editor features caching local changes saving and update of webpage resources and blog posts, including their metadata like tags, summary and splash image.
In Blog meta editor user can select and upload images on Azure blob stroage with a nice UI.

The editor features local changes saving and immidiate update of webpage resources.
Frontend doesn't restrict unregistered users from trying to update the resources, but API shouldn't allow that.

It also features a kind of decent UI that allows to quickly switch between resources and languages,
Expand Down
9 changes: 9 additions & 0 deletions locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ SaveDraft: Save Draft
Resource: Resource
NewResource: New Resource
SelectResource: Select Resource
CreateResource: Create Resource
CreateResourceQuestion: Do you want to create a resource?
DeleteResource: Delete Resource
DeleteResourceQuestion: Do you want to delete a resource?
Resources: Resources
AvailableResources: Available Resources
Blog: Blog
Expand All @@ -50,8 +54,13 @@ Title: Title
Summary: Summary
Tags: Tags
Image: Image
DeleteDir: Delete directory
DeleteDirQuestion: Do you want to delete directory?
EnterDirname: Enter directory name..
DeleteFile: Delete file
DeleteFileQuestion: Do you want to delete file?
EnterTag: Enter tag..
Create: Create
Created: Created
Creating: Creating
Updated: Updated
Expand Down
9 changes: 9 additions & 0 deletions locales/pl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ SaveDraft: Zapisz kopię roboczą
Resource: Zasób
NewResource: Nowy zasób
SelectResource: Wybierz zasób
CreateResource: Utwórz zasób
CreateResourceQuestion: Czy chcesz utworzyć zasób?
DeleteResource: Usuń zasób
DeleteResourceQuestion: Czy chcesz usunąć zasób?
Resources: Zasoby
AvailableResources: Dostępne zasoby
Blog: Blog
Expand All @@ -50,8 +54,13 @@ Title: Tytuł
Summary: Podsumowanie
Tags: Tagi
Image: Obraz
DeleteDir: Usuń katalog
DeleteDirQuestion: Czy chcesz usunąć katalog?
EnterDirname: Wpisz nazwę katalogu..
DeleteFile: Usuń plik
DeleteFileQuestion: Czy chcesz usunąć plik?
EnterTag: Wpisz tag..
Create: Utwórz
Created: Utworzono
Creating: Tworzenie
Updated: Zaktualizowano
Expand Down
6 changes: 3 additions & 3 deletions src/components/atoms/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub fn markdown_display(props: &MarkdownDisplayProps) -> Html {
use_effect_with_deps(
|(interactive, id, navigator)| {
if interactive.is_some() {
make_links_clickable(navigator.clone(), id.as_str());
make_links_clickable(&navigator, id.as_str());
}
},
(interactive, id.clone(), navigator.clone()),
Expand Down Expand Up @@ -144,11 +144,11 @@ pub fn edit_button(props: &EditButtonProps) -> Html {
}

html! {
<button class={"btn absolute top-5 right-5 btn-accent btn-xs btn-outline"} onclick={edit_onclick}>{locales_store.get(TK::Edit)}</button>
<button class={"btn absolute top-2 right-2 lg:top-4 lg:right-6 btn-accent btn-xs btn-outline z-10"} onclick={edit_onclick}>{locales_store.get(TK::Edit)}</button>
}
}

fn make_links_clickable(navigator: Navigator, id: &str) {
fn make_links_clickable(navigator: &Navigator, id: &str) {
let element = get_display_element(id);
let links = element.query_selector_all("a").unwrap();
for i in 0..links.length() {
Expand Down
2 changes: 1 addition & 1 deletion src/components/atoms/modal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ fn get_buttons(buttons: &Buttons) -> Html {
html! {
<>
<button class="btn btn-neutral" onclick={cancel_onclick}>{&cancel_button.text}</button>
<button class="btn btn-warning" onclick={risky_onclick}>{&risky_button.text}</button>
<button class="btn btn-error" onclick={risky_onclick}>{&risky_button.text}</button>
</>
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/atoms/resource_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ fn resource_list(props: &ResourceListProps) -> Html {
}
};
let vec_into_elements = |vec: Vec<(&ResId, bool)>| {
if vec.is_empty() {
return vec![vec![]];
}

vec.clone()
.chunks(3)
.map(|x| {
Expand Down
112 changes: 68 additions & 44 deletions src/components/organisms/blog/blog_image_select.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
use std::path::Path;

use crate::{
api::client::{ApiClient, BlobClient},
components::{atoms::loading::Loading, state::State},
async_event,
components::{
atoms::{
loading::Loading,
modal::{show_modal_callback, Buttons, ModalButton, ModalData, ModalStore},
},
state::State,
},
data::{
locales::{store::LocalesStore, tk::TK},
session::SessionStore,
window::WindowStore,
},
utils::style::get_svg_bg_mask_style,
utils::{ext::Mergable, style::get_svg_bg_mask_style},
};
use std::path::Path;
use wasm_bindgen::JsCast;
use web_sys::{HtmlElement, HtmlInputElement};
use yew::{platform::spawn_local, prelude::*};
Expand Down Expand Up @@ -79,6 +85,7 @@ pub fn image_browser_dialog(props: &ImageBrowserDialogProps) -> Html {
let (window_store, _) = use_store::<WindowStore>();
let (session_store, session_dispatch) = use_store::<SessionStore>();
let (locales_store, _) = use_store::<LocalesStore>();
let (_, modal_dispatch) = use_store::<ModalStore>();
let curr = use_state(|| "/".to_string());
let selected = use_state(|| None);
let props = props.clone();
Expand Down Expand Up @@ -124,7 +131,7 @@ pub fn image_browser_dialog(props: &ImageBrowserDialogProps) -> Html {
browser.focus().unwrap();
}
},
curr.clone(),
(curr.clone(), selected.clone()),
)
}
let onselectedchanged = {
Expand Down Expand Up @@ -209,9 +216,7 @@ pub fn image_browser_dialog(props: &ImageBrowserDialogProps) -> Html {
Callback::from(move |_| {
let val = selected.as_ref().and_then(|s| match s {
BrowseItem::Dir(_) => None,
BrowseItem::File(name) => {
Some(format!("{}{}", &curr.as_str()[1..], name))
}
BrowseItem::File(name) => Some(format!("{}{}", &curr.as_str()[1..], name)),
});
props.ondatachanged.emit(val)
})
Expand All @@ -221,10 +226,15 @@ pub fn image_browser_dialog(props: &ImageBrowserDialogProps) -> Html {
Some(BrowseItem::File(_)) => select_class.push("btn-success"),
_ => select_class.push("btn-disabled"),
};
let add_img_onclick = {
let enable_force_open = {
let onforceopenchanged = props.onforceopenchanged.clone();
Callback::from(move |_| onforceopenchanged.emit(true))
};
let disable_force_open = {
let onforceopenchanged = props.onforceopenchanged.clone();
Callback::from(move |_| onforceopenchanged.emit(false))
};
let add_img_onclick = enable_force_open.clone();
let mut dir_input_class = classes!("flex", "w-12", "grow", "outline-none", "bg-transparent");
if !*dir_input_active {
dir_input_class.push("hidden");
Expand Down Expand Up @@ -321,43 +331,57 @@ pub fn image_browser_dialog(props: &ImageBrowserDialogProps) -> Html {
),
None => ("hidden", None),
};

let delete_onclick = {
let selected = selected.clone();
let session_store = session_store.clone();
let curr = curr.clone();
let state = state.clone();
Callback::from(move |_| match (*selected).clone() {
Some(BrowseItem::Dir(path)) | Some(BrowseItem::File(path)) => {
let selected = selected.clone();
let token = session_store.token.clone().unwrap_or_default();
let curr = curr.clone();
let state = state.clone();
let go_up = match &*state {
State::Ok(Some(paths)) => paths.len() == 1,
_ => false,
};
spawn_local(async move {
match ApiClient::delete_img(
&token,
(curr[1..].to_string() + path.as_str()).as_str(),
)
.await
{
Ok(_) => {
if go_up {
curr.set("/".to_string());
}
state.set(State::Ok(None));
}
Err(e) => state.set(State::Err(e)),
}
selected.set(None);
});
let onforceopenchanged = &props.onforceopenchanged;
let go_up = match &*state {
State::Ok(Some(paths)) => paths.len() == 1,
_ => false,
};
let delete_onclick = async_event!(|onforceopenchanged,
selected,
session_store,
curr,
state,
go_up| {
let path = match &*selected {
Some(BrowseItem::Dir(path) | BrowseItem::File(path)) => path,
_ => return,
};
let token = session_store.token.clone().unwrap_or_default();
match ApiClient::delete_img(&token, (curr[1..].to_string() + path.as_str()).as_str()).await
{
Ok(_) => {
if go_up {
curr.set("/".to_string());
}
state.set(State::Ok(None));
}
None => {}
})
Err(e) => state.set(State::Err(e)),
}
selected.set(None);
onforceopenchanged.emit(false);
});
let (title, message) = match &*selected {
Some(BrowseItem::Dir(_)) => (
locales_store.get(TK::DeleteDir),
locales_store.get(TK::DeleteDirQuestion),
),
Some(BrowseItem::File(_)) => (
locales_store.get(TK::DeleteFile),
locales_store.get(TK::DeleteFileQuestion),
),
None => (String::new(), String::new()),
};
let delete_onclick = enable_force_open.clone().merge(show_modal_callback(
ModalData {
title,
message,
buttons: Buttons::RiskyCancel(
ModalButton::new(locales_store.get(TK::Delete), Some(delete_onclick)),
ModalButton::new(locales_store.get(TK::Cancel), Some(disable_force_open)),
),
},
modal_dispatch,
));
let buttons = match &*state {
State::Ok(_) => html! {
<>
Expand Down
68 changes: 37 additions & 31 deletions src/components/organisms/editor/atoms/delete_button.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,59 @@
use crate::{
api::client::ApiClient,
async_event,
components::atoms::modal::{show_modal_callback, Buttons, ModalButton, ModalData, ModalStore},
data::{
locales::{store::LocalesStore, tk::TK},
resources::id::ResId,
session::SessionStore,
},
pages::editor::{EditorProps, EditorState},
router::route::Route,
};
use petompp_web_models::models::country::Country;
use yew::{platform::spawn_local, prelude::*};
use yew::prelude::*;
use yew_router::prelude::*;
use yewdux::prelude::*;

#[derive(Debug, Clone, PartialEq, Properties)]
pub struct DeleteButtonProps {
pub resid: ResId,
pub lang: Country,
}

#[function_component(DeleteButton)]
pub fn delete_button(props: &DeleteButtonProps) -> Html {
pub fn delete_button(props: &EditorProps) -> Html {
let (session_store, session_dispatch) = use_store::<SessionStore>();
let (locales_store, _) = use_store::<LocalesStore>();
let (_, modal_dispatch) = use_store::<ModalStore>();
let navigator = use_navigator().unwrap();
let resid = props.resid.clone();
let lang = props.lang;
let err = use_state(|| None);
let onclick = {
let err = err.clone();
Callback::from(move |_| {
let navigator = navigator.clone();
let resid = resid.clone();
let lang = lang;
let err = err.clone();
let token = session_store.token.clone().unwrap_or_default();
spawn_local(async move {
match match resid {
ResId::Blob(id) => ApiClient::delete_post(&id, lang.key(), &token).await,
ResId::ResKey(id) => match lang {
Country::UnitedKingdom => ApiClient::delete_resource(&token, &id).await,
_ => ApiClient::delete_resource_lang(&token, &id, &lang).await,
},
} {
Ok(_) => navigator.push(&Route::Editor),
Err(e) => err.set(Some(e)),
}
})
})
let (Some(resid), Some(lang)) = (&props.resid, &props.lang) else {
return html! {};
};
if let EditorState::Loading = &props.state {
return html! {};
}
let onstatechange = &props.onstatechanged;
let token = session_store.token.clone().unwrap_or_default();
let onclick = async_event!(|onstatechange, navigator, resid, lang, err, token| {
onstatechange.emit(EditorState::Loading);
match match resid {
ResId::Blob(id) => ApiClient::delete_post(&id, lang.key(), &token).await,
ResId::ResKey(id) => match lang {
Country::UnitedKingdom => ApiClient::delete_resource(&token, &id).await,
_ => ApiClient::delete_resource_lang(&token, &id, &lang).await,
},
} {
Ok(_) => navigator.push(&Route::Editor),
Err(e) => err.set(Some(e)),
}
});
let onclick = show_modal_callback(
ModalData {
title: locales_store.get(TK::DeleteResource),
message: locales_store.get(TK::DeleteResourceQuestion),
buttons: Buttons::RiskyCancel(
ModalButton::new(locales_store.get(TK::Delete), Some(onclick)),
ModalButton::new(locales_store.get(TK::Cancel), None),
),
},
modal_dispatch.clone(),
);
if let Some(e) = &*err {
if let Err(redirect) = e.handle_failed_auth(session_dispatch) {
return redirect;
Expand Down
Loading
Loading