Fix logic flow and add utils
This commit is contained in:
parent
f10d07a87f
commit
8f5a49b308
|
@ -26,7 +26,7 @@ version = "0.2.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
@ -217,6 +217,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1"
|
||||
dependencies = [
|
||||
"console",
|
||||
"fuzzy-matcher",
|
||||
"tempfile",
|
||||
"zeroize",
|
||||
]
|
||||
|
@ -256,6 +257,27 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.8"
|
||||
|
@ -394,6 +416,15 @@ dependencies = [
|
|||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuzzy-matcher"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
|
||||
dependencies = [
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
|
@ -445,6 +476,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.8"
|
||||
|
@ -586,12 +626,34 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d508111813f9af3afd2f92758f77e4ed2cc9371b642112c6a48d22eb73105c5"
|
||||
dependencies = [
|
||||
"hermit-abi 0.2.6",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
|
@ -628,6 +690,12 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.0.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.9"
|
||||
|
@ -668,7 +736,7 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys",
|
||||
"windows-sys 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -714,7 +782,7 @@ version = "1.13.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
@ -737,7 +805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b4a3100141f1733ea40b53381b0ae3117330735ef22309a190ac57b9576ea716"
|
||||
dependencies = [
|
||||
"pathdiff",
|
||||
"windows-sys",
|
||||
"windows-sys 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -811,7 +879,7 @@ dependencies = [
|
|||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
"windows-sys 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -952,6 +1020,20 @@ dependencies = [
|
|||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.35.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "985947f9b6423159c4726323f373be0a21bdb514c5af06a849cb3d2dce2d01e8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
|
@ -965,7 +1047,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys",
|
||||
"windows-sys 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1059,6 +1141,7 @@ dependencies = [
|
|||
"eyre",
|
||||
"futures",
|
||||
"indicatif",
|
||||
"is-terminal",
|
||||
"lazy_static",
|
||||
"open",
|
||||
"reqwest",
|
||||
|
@ -1172,6 +1255,15 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
|
@ -1480,43 +1572,100 @@ version = "0.36.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_msvc 0.36.1",
|
||||
"windows_i686_gnu 0.36.1",
|
||||
"windows_i686_msvc 0.36.1",
|
||||
"windows_x86_64_gnu 0.36.1",
|
||||
"windows_x86_64_msvc 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc 0.42.0",
|
||||
"windows_i686_gnu 0.42.0",
|
||||
"windows_i686_msvc 0.42.0",
|
||||
"windows_x86_64_gnu 0.42.0",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
|
|
|
@ -15,10 +15,11 @@ clap = { version = "4.0.17", features = ["derive"] }
|
|||
reqwest = { version = "0.11", features = ["json"] }
|
||||
ansi_term = "0.12.1"
|
||||
chrono = { version = "0.4.22", features = ["serde"] }
|
||||
dialoguer = "0.10.2"
|
||||
dialoguer = { version = "0.10.2", features = ["fuzzy-select"] }
|
||||
indicatif = "0.17.1"
|
||||
console = "0.15.2"
|
||||
tinyfiledialogs = "3.9.1"
|
||||
open = "3.0.3"
|
||||
lazy_static = "1.4.0"
|
||||
directories = "4.0.1"
|
||||
directories = "4.0.1"
|
||||
is-terminal = "0.3.0"
|
|
@ -1,17 +1,14 @@
|
|||
use crate::util;
|
||||
use crate::util::is_interactive;
|
||||
|
||||
use ansi_term::{self, Colour};
|
||||
use chrono::prelude::*;
|
||||
use chrono::Duration;
|
||||
use clap::Parser;
|
||||
use dialoguer::{theme::ColorfulTheme, Input, Select};
|
||||
use dialoguer::{theme::ColorfulTheme, FuzzySelect, Input};
|
||||
use directories::{BaseDirs, UserDirs};
|
||||
use eyre::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
io::Read,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use std::{io::Read, path::PathBuf};
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
@ -49,28 +46,31 @@ pub async fn handler(pathflag: Option<PathBuf>) -> Result<()> {
|
|||
// If a pathflag is Some() it means it's passed via a flag, use that path and skip the prompting
|
||||
// Otherwise, prompt the user to select a default path (depending on type of installation), or tinyfiledialog for a path, or manual input
|
||||
|
||||
// Maybe call prompt_history_file in the Some and None. Call get_path_input in prompt_history_file
|
||||
// Either gets the pathflag or prompts the user for a path
|
||||
let path = match pathflag {
|
||||
Some(pathflag) => pathflag,
|
||||
None => get_path_input(),
|
||||
None => prompt_history_file().ok_or(eyre::eyre!(
|
||||
"No path provided, please provide a path to your ShareX history file"
|
||||
))?,
|
||||
};
|
||||
|
||||
let file = prompt_history_file(&path);
|
||||
|
||||
if file == None {
|
||||
if !PathBuf::from(&path).exists() {
|
||||
eprintln!("A valid path was not specified. Please try again.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let history_urls = get_history_urls(file.unwrap());
|
||||
let history_urls = get_history_urls(path);
|
||||
|
||||
delete_urls(history_urls).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prompt_history_file(path: &Path) -> Option<PathBuf> {
|
||||
fn prompt_history_file() -> Option<PathBuf> {
|
||||
let default_path = get_default_history_path();
|
||||
|
||||
println!();
|
||||
let menu_response: usize = Select::with_theme(&ColorfulTheme::default())
|
||||
let menu_response: usize = FuzzySelect::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Pick an option (use arrow keys to select, enter to confirm)")
|
||||
.items(&[
|
||||
"Use default path",
|
||||
|
@ -84,11 +84,11 @@ fn prompt_history_file(path: &Path) -> Option<PathBuf> {
|
|||
println!();
|
||||
|
||||
match menu_response {
|
||||
0 => Some(path.to_path_buf()),
|
||||
0 => Some(default_path.to_path_buf()),
|
||||
1 => Some(
|
||||
tinyfiledialogs::open_file_dialog(
|
||||
"Choose where sharex history is stored",
|
||||
path.to_str().unwrap(),
|
||||
default_path.to_str().unwrap(),
|
||||
Some((&["History.json", "*.json"], "History.json")),
|
||||
)
|
||||
.map_or_else(
|
||||
|
@ -130,28 +130,20 @@ fn prompt_history_file(path: &Path) -> Option<PathBuf> {
|
|||
}
|
||||
|
||||
fn get_history_urls(path: PathBuf) -> Result<Vec<String>> {
|
||||
let spinner = util::setup_spinner("Reading and parsing JSON...");
|
||||
if is_interactive() {
|
||||
let spinner = util::setup_spinner("Reading and parsing JSON...");
|
||||
|
||||
let history_json = read_history_json(path)?;
|
||||
let history_items = parse_history_json(&history_json)?;
|
||||
let deletion_urls = filter_deletion_urls(&history_items, None);
|
||||
let history_json = read_history_json(path)?;
|
||||
let history_items = parse_history_json(&history_json)?;
|
||||
let deletion_urls = filter_deletion_urls(&history_items, None);
|
||||
|
||||
spinner.finish_with_message(format!("Done! {} items found", deletion_urls.len()));
|
||||
Ok(deletion_urls)
|
||||
}
|
||||
|
||||
fn get_path_input() -> PathBuf {
|
||||
let args = util::Args::parse();
|
||||
let path = match args.command {
|
||||
Some(util::Command::PurgeOnline { path }) => path,
|
||||
None => None,
|
||||
};
|
||||
|
||||
let default_history_path = get_default_history_path();
|
||||
|
||||
match path {
|
||||
Some(path) => path,
|
||||
None => default_history_path,
|
||||
spinner.finish_with_message(format!("Done! {} items found", deletion_urls.len()));
|
||||
return Ok(deletion_urls);
|
||||
} else {
|
||||
let history_json = read_history_json(path)?;
|
||||
let history_items = parse_history_json(&history_json)?;
|
||||
let deletion_urls = filter_deletion_urls(&history_items, None);
|
||||
return Ok(deletion_urls);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,6 +225,24 @@ async fn delete_urls(deletion_urls: Result<Vec<String>>) -> Result<()> {
|
|||
|
||||
let results = futures::future::join_all(futures).await;
|
||||
|
||||
// // I don't understand Rust enough so the stuff below looks kinda cursed
|
||||
// let headers = resp.headers().clone();
|
||||
// let remaining = headers
|
||||
// .get("x-post-rate-limit-remaining")
|
||||
// .unwrap()
|
||||
// .to_str()?
|
||||
// .to_owned();
|
||||
// let limit = headers
|
||||
// .get("x-post-rate-limit-limit")
|
||||
// .unwrap()
|
||||
// .to_str()?
|
||||
// .to_owned();
|
||||
// let reset = headers
|
||||
// .get("x-post-rate-limit-Reset")
|
||||
// .unwrap()
|
||||
// .to_str()?
|
||||
// .to_owned();
|
||||
|
||||
for result in results {
|
||||
progress_bar.inc(1);
|
||||
// Check the headers here for rate limits?
|
||||
|
@ -253,57 +263,3 @@ async fn delete_urls(deletion_urls: Result<Vec<String>>) -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// async fn send_deletion(url: &str) -> Result<(String, String, String)> {
|
||||
// // TODO: Only have 1 client (for multiple requests) and use a proxy to prevent rate limiting
|
||||
// // TODO: Send different type of body by matching on the host
|
||||
// let client = reqwest::Client::new();
|
||||
// let params = [("confirm", true)];
|
||||
// let resp = client.post(url).form(¶ms).send().await?;
|
||||
|
||||
// println!("{:#?}", resp);
|
||||
|
||||
// match resp.status() {
|
||||
// reqwest::StatusCode::OK => {
|
||||
// println!("OK");
|
||||
// }
|
||||
// reqwest::StatusCode::TOO_MANY_REQUESTS => {
|
||||
// println!("TOO MANY REQUESTS");
|
||||
// }
|
||||
// reqwest::StatusCode::BAD_GATEWAY => {
|
||||
// println!("BAD GATEWAY");
|
||||
// }
|
||||
// _ => {
|
||||
// println!("Not OK");
|
||||
// }
|
||||
// }
|
||||
|
||||
// // I don't understand Rust enough so the stuff below looks kinda cursed
|
||||
// let headers = resp.headers().clone();
|
||||
// let remaining = headers
|
||||
// .get("x-post-rate-limit-remaining")
|
||||
// .unwrap()
|
||||
// .to_str()?
|
||||
// .to_owned();
|
||||
// let limit = headers
|
||||
// .get("x-post-rate-limit-limit")
|
||||
// .unwrap()
|
||||
// .to_str()?
|
||||
// .to_owned();
|
||||
// let reset = headers
|
||||
// .get("x-post-rate-limit-Reset")
|
||||
// .unwrap()
|
||||
// .to_str()?
|
||||
// .to_owned();
|
||||
|
||||
// println!(
|
||||
// "Remaining: {} Limit: {} Reset: {}",
|
||||
// Colour::Green.paint(&remaining),
|
||||
// Colour::Yellow.paint(&limit),
|
||||
// Colour::Red.paint(&reset)
|
||||
// );
|
||||
|
||||
// print!("{:?}", headers);
|
||||
|
||||
// Ok((remaining, limit, reset))
|
||||
// }
|
||||
|
|
15
src/util.rs
15
src/util.rs
|
@ -48,3 +48,18 @@ pub fn setup_progressbar(items: usize) -> ProgressBar {
|
|||
pub fn open_webpage(url: &str) {
|
||||
open::that(url).unwrap_or_else(|_| panic!("Unable to open webpage {}", &url));
|
||||
}
|
||||
|
||||
// Check if ran with Clap arguments or not
|
||||
pub fn is_interactive() -> bool {
|
||||
let args = Args::parse();
|
||||
match args.command {
|
||||
None => {
|
||||
// No command provided, running in interactive mode
|
||||
return true;
|
||||
}
|
||||
_ => {
|
||||
// Command provided, running in non-interactive mode
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue