Merge remote-tracking branch 'origin/master' into dev

This commit is contained in:
jeasonnow
2023-07-21 10:30:18 +08:00
12 changed files with 849 additions and 1248 deletions

1122
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,8 +18,7 @@ tauri-build = { version = "1.4.0", features = [] }
serde_json = "1.0.96"
serde = { version = "1.0.163", features = ["derive"] }
tauri = { version = "1.4.1", features = ["api-all", "system-tray"] }
download_rs = { version = "0.2.0", features = ["sync_download"] }
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" }
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
[dev-dependencies]
cargo-bloat = "0.11.1"

BIN
src-tauri/icons/wechat.icns Normal file

Binary file not shown.

View File

@@ -1,8 +1,9 @@
use std::fs;
use crate::util::{check_file_or_append, get_download_message, show_toast};
use download_rs::sync_download::Download;
use crate::util::{check_file_or_append, get_download_message, MessageType, show_toast};
use tauri::{api, command, AppHandle, Manager, Window};
use tauri::api::http::{ClientBuilder, HttpRequestBuilder, ResponseType};
use std::fs::File;
use std::io::Write;
#[derive(serde::Deserialize)]
pub struct DownloadFileParams {
@@ -19,16 +20,29 @@ pub struct BinaryDownloadParams {
#[command]
pub async fn download_file(app: AppHandle, params: DownloadFileParams) -> Result<(), String> {
let window: Window = app.get_window("pake").unwrap();
show_toast(&window, &get_download_message(MessageType::Start));
let output_path = api::path::download_dir().unwrap().join(params.filename);
let file_path = check_file_or_append(output_path.to_str().unwrap());
let download = Download::new(&params.url, Some(&file_path), None);
match download.download() {
Ok(_) => {
show_toast(&window, &get_download_message());
let client = ClientBuilder::new().build().unwrap();
let response = client.send(
HttpRequestBuilder::new("GET", &params.url)
.unwrap()
.response_type(ResponseType::Binary)
).await;
match response {
Ok(res) => {
let bytes = res.bytes().await.unwrap().data;
let mut file = File::create(file_path).unwrap();
file.write_all(&bytes).unwrap();
show_toast(&window, &get_download_message(MessageType::Success));
Ok(())
}
Err(e) => {
show_toast(&window, &e.to_string());
show_toast(&window, &get_download_message(MessageType::Failure));
Err(e.to_string())
}
}
@@ -40,16 +54,17 @@ pub async fn download_file_by_binary(
params: BinaryDownloadParams,
) -> Result<(), String> {
let window: Window = app.get_window("pake").unwrap();
show_toast(&window, &get_download_message(MessageType::Start));
let output_path = api::path::download_dir().unwrap().join(params.filename);
let file_path = check_file_or_append(output_path.to_str().unwrap());
let download_file_result = fs::write(file_path, &params.binary);
match download_file_result {
Ok(_) => {
show_toast(&window, &get_download_message());
show_toast(&window, &get_download_message(MessageType::Success));
Ok(())
}
Err(e) => {
show_toast(&window, &e.to_string());
show_toast(&window, &get_download_message(MessageType::Failure));
Err(e.to_string())
}
}

View File

@@ -38,7 +38,6 @@ pub fn get_menu() -> Menu {
pub fn menu_event_handle(event: WindowMenuEvent) {
if event.menu_item_id() == "close" {
event.window().minimize().expect("can't minimize window");
// event.window().eval("toggleVideoPlayback(true);").unwrap();
}
if event.menu_item_id() == "goto_url" {

View File

@@ -96,18 +96,18 @@ document.addEventListener('DOMContentLoaded', () => {
const urlSubmit = document.getElementById('pakeUrlSubmit');
const urlClose = document.getElementById('pakeUrlClose');
urlSubmit.onclick = function () {
urlSubmit.onclick = function() {
const url = urlInput.value;
if (url) {
window.location.href = url;
}
};
urlClose.onclick = function () {
urlClose.onclick = function() {
urlModal.style.display = 'none';
};
urlInput.addEventListener('keydown', function (event) {
urlInput.addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
const url = urlInput.value;
if (url) {
@@ -116,13 +116,13 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
document.addEventListener('keydown', function (event) {
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape' && urlModal.style.display === 'block') {
urlModal.style.display = 'none';
}
});
window.showUrlModal = function () {
window.showUrlModal = function() {
urlModal.style.display = 'block';
urlInput.focus();
};
@@ -134,11 +134,11 @@ document.addEventListener('DOMContentLoaded', () => {
m.style.cssText =
'max-width:60%;min-width: 80px;padding:0 12px;height: 32px;color: rgb(255, 255, 255);line-height: 32px;text-align: center;border-radius: 8px;position: fixed; bottom:24px;right: 28px;z-index: 999999;background: rgba(0, 0, 0,.8);font-size: 13px;';
document.body.appendChild(m);
setTimeout(function () {
setTimeout(function() {
const d = 0.5;
m.style.transition = 'transform ' + d + 's ease-in, opacity ' + d + 's ease-in';
m.style.opacity = '0';
setTimeout(function () {
setTimeout(function() {
document.body.removeChild(m);
}, d * 1000);
}, 3000);
@@ -146,35 +146,4 @@ document.addEventListener('DOMContentLoaded', () => {
window.pakeToast = pakeToast;
// chatgpt supports unlimited times of GPT4-Mobile
if (window.location.hostname === 'chat.openai.com') {
const originFetch = fetch;
window.fetch = (url, options) => {
return originFetch(url, options).then(async response => {
if (url.indexOf('/backend-api/models') === -1) {
return response;
}
const responseClone = response.clone();
let res = await responseClone.json();
res.models = res.models.map(m => {
m.tags = m.tags.filter(t => {
return t !== 'mobile';
});
if (m.slug === 'gpt-4-mobile') {
res.categories.push({
browsing_model: null,
category: 'gpt_4',
code_interpreter_model: null,
default_model: 'gpt-4-mobile',
human_category_name: 'GPT-4-Mobile',
plugins_model: null,
subscription_level: 'plus',
});
}
return m;
});
return new Response(JSON.stringify(res), response);
});
};
}
});

View File

@@ -1,17 +1,13 @@
const shortcuts = {
ArrowUp: () => scrollTo(0, 0),
ArrowDown: () => scrollTo(0, document.body.scrollHeight),
// Don't use command + ArrowLeft or command + ArrowRight
// When editing text in page, it causes unintended page navigation.
// ArrowLeft: () => window.history.back(),
// ArrowRight: () => window.history.forward(),
'ArrowUp': () => scrollTo(0, 0),
'ArrowDown': () => scrollTo(0, document.body.scrollHeight),
'[': () => window.history.back(),
']': () => window.history.forward(),
r: () => window.location.reload(),
'r': () => window.location.reload(),
'-': () => zoomOut(),
'=': () => zoomIn(),
'+': () => zoomIn(),
0: () => setZoom('100%'),
'0': () => setZoom('100%'),
};
function setZoom(zoom) {
@@ -40,46 +36,6 @@ function handleShortcut(event) {
}
}
//这里参考 ChatGPT 的代码
const uid = () => window.crypto.getRandomValues(new Uint32Array(1))[0];
function transformCallback(callback = () => {}, once = false) {
const identifier = uid();
const prop = `_${identifier}`;
Object.defineProperty(window, prop, {
value: (result) => {
if (once) {
Reflect.deleteProperty(window, prop);
}
return callback(result);
},
writable: false,
configurable: true,
});
return identifier;
}
async function invoke(cmd, args) {
return new Promise((resolve, reject) => {
if (!window.__TAURI_POST_MESSAGE__)
reject('__TAURI_POST_MESSAGE__ does not exist~');
const callback = transformCallback((e) => {
resolve(e);
Reflect.deleteProperty(window, `_${error}`);
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true);
window.__TAURI_POST_MESSAGE__({
cmd,
callback,
error,
...args,
});
});
}
// Judgment of file download.
function isDownloadLink(url) {
const fileExtensions = [
@@ -105,6 +61,7 @@ function externalTargetLink() {
document.addEventListener('DOMContentLoaded', () => {
const tauri = window.__TAURI__;
const appWindow = tauri.window.appWindow;
const invoke = tauri.tauri.invoke;
const topDom = document.createElement('div');
topDom.id = 'pack-top-dom';
@@ -137,44 +94,39 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
const specialDownloadProtocal = ['blob', 'data'];
const isExternalLink = (url, host) => window.location.host !== host;
// process special download protocal['data:','blob:']
const isSpecialDownload = (url) => ['blob', 'data'].some(protocal => url.startsWith(protocal));
const isDownloadRequired = (url, anchorElement, e) =>
anchorElement.download || e.metaKey || e.ctrlKey || isDownloadLink(url);
const handleExternalLink = (e, url) => {
e.preventDefault();
tauri.shell.open(url);
};
const handleDownloadLink = (e, url, filename) => {
e.preventDefault();
invoke('download_file', { params: { url, filename } });
};
const detectAnchorElementClick = (e) => {
const anchorElement = e.target.closest('a');
if (anchorElement && anchorElement.href) {
const target = anchorElement.target;
anchorElement.target = '_self';
const hrefUrl = new URL(anchorElement.href);
const absoluteUrl = hrefUrl.href;
let filename = anchorElement.download || getFilenameFromUrl(absoluteUrl);
// Handling external link redirection.
if (
window.location.host !== hrefUrl.host &&
(target === '_blank' || target === '_new' || externalTargetLink())
) {
e.preventDefault && e.preventDefault();
tauri.shell.open(absoluteUrl);
if (isExternalLink(absoluteUrl, hrefUrl.host) && (['_blank', '_new'].includes(anchorElement.target) || externalTargetLink())) {
handleExternalLink(e, absoluteUrl);
return;
}
let filename = anchorElement.download || getFilenameFromUrl(absoluteUrl);
// Process download links for Rust to handle.
// If the download attribute is set, the download attribute is used as the file name.
if (
(anchorElement.download ||
e.metaKey ||
e.ctrlKey ||
isDownloadLink(absoluteUrl)) &&
!externalDownLoadLink() && specialDownloadProtocal.every(protocal => !absoluteUrl.startsWith(protocal))
) {
e.preventDefault();
invoke('download_file', {
params: {
url: absoluteUrl,
filename,
},
});
if (isDownloadRequired(absoluteUrl, anchorElement, e) && !externalDownLoadLink() && !isSpecialDownload(absoluteUrl)) {
handleDownloadLink(e, absoluteUrl, filename);
}
}
};
@@ -208,6 +160,12 @@ document.addEventListener('DOMContentLoaded', () => {
} catch (e) {
console.log(e);
}
// Fix Chinese input method "Enter" on Safari
document.addEventListener('keydown', (e) => {
if (e.keyCode === 229) e.stopPropagation();
}, true);
});
function setDefaultZoom() {
@@ -219,26 +177,7 @@ function setDefaultZoom() {
function getFilenameFromUrl(url) {
const urlPath = new URL(url).pathname;
const filename = urlPath.substring(urlPath.lastIndexOf('/') + 1);
return filename;
}
function removeUrlParameters(url) {
const parsedUrl = new URL(url);
parsedUrl.search = '';
return parsedUrl.toString();
}
// Toggle video playback when the window is hidden.
function toggleVideoPlayback(pause) {
const videos = document.getElementsByTagName('video');
for (const video of videos) {
if (pause) {
video.pause();
} else {
video.play();
}
}
return urlPath.substring(urlPath.lastIndexOf('/') + 1);
}
// Collect blob urls to blob by overriding window.URL.createObjectURL
@@ -297,6 +236,7 @@ function downloadFromBlobUrl(blobUrl, filename) {
});
}
// detect blob download by createElement("a")
function detectDownloadByCreateAnchor() {
const createEle = document.createElement;
@@ -319,3 +259,10 @@ function detectDownloadByCreateAnchor() {
return anchorEle;
};
}
// Determine the language of the current system.
function getSystemLanguage() {
const lang = navigator.language.substr(0, 2);
return lang === 'ch' ? 'ch' : 'en';
}

View File

@@ -171,6 +171,7 @@ window.addEventListener('DOMContentLoaded', _event => {
#react-root [data-testid="AppTabBar_Explore_Link"],
#react-root a[href*="/lists"][role="link"][aria-label],
#react-root a[href*="/i/communitynotes"][role="link"][aria-label],
#react-root a[role="link"][aria-label="Communities"],
#react-root a[href*="/i/verified-orgs-signup"][role="link"][aria-label] {
display: none !important;
}

View File

@@ -56,7 +56,6 @@ pub fn run_app() {
#[cfg(target_os = "macos")]
{
event.window().minimize().unwrap();
// event.window().eval("toggleVideoPlayback(true);").unwrap();
}
#[cfg(not(target_os = "macos"))]

View File

@@ -40,19 +40,43 @@ pub fn show_toast(window: &Window, message: &str) {
window.eval(&script).unwrap();
}
pub fn get_download_message() -> String {
let default_message = "Download successful, saved to download directory~";
let chinese_message = "下载成功,已保存到下载目录~";
pub enum MessageType {
Start,
Success,
Failure,
}
pub fn get_download_message(message_type: MessageType) -> String {
let default_start_message = "Start downloading~";
let chinese_start_message = "开始下载中~";
let default_success_message = "Download successful, saved to download directory~";
let chinese_success_message = "下载成功,已保存到下载目录~";
let default_failure_message = "Download failed, please check your network connection~";
let chinese_failure_message = "下载失败,请检查你的网络连接~";
env::var("LANG")
.map(|lang| {
if lang.starts_with("zh") {
chinese_message
match message_type {
MessageType::Start => chinese_start_message,
MessageType::Success => chinese_success_message,
MessageType::Failure => chinese_failure_message,
}
} else {
default_message
match message_type {
MessageType::Start => default_start_message,
MessageType::Success => default_success_message,
MessageType::Failure => default_failure_message,
}
}
})
.unwrap_or(default_message)
.unwrap_or_else(|_| match message_type {
MessageType::Start => default_start_message,
MessageType::Success => default_success_message,
MessageType::Failure => default_failure_message,
})
.to_string()
}