Merge remote-tracking branch 'upstream/v2' into store-rework-2

This commit is contained in:
Tony
2024-10-10 14:19:37 +08:00
25 changed files with 366 additions and 315 deletions
+2 -2
View File
@@ -10,11 +10,11 @@
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "2.0.1",
"@tauri-apps/api": "2.0.2",
"@tauri-apps/plugin-deep-link": "2.0.0"
},
"devDependencies": {
"@tauri-apps/cli": "2.0.0",
"@tauri-apps/cli": "2.0.2",
"typescript": "^5.2.2",
"vite": "^5.4.7"
}
@@ -10,6 +10,7 @@ import android.content.Intent
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.webkit.MimeTypeMap
import androidx.activity.result.ActivityResult
import app.tauri.Logger
import app.tauri.annotation.ActivityCallback
@@ -43,6 +44,7 @@ class MessageOptions {
@InvokeArg
class SaveFileDialogOptions {
var fileName: String? = null
lateinit var filters: Array<Filter>
}
@TauriPlugin
@@ -57,20 +59,7 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
val intent = if (parsedTypes.isNotEmpty()) {
val intent = Intent(Intent.ACTION_PICK)
intent.putExtra(Intent.EXTRA_MIME_TYPES, parsedTypes)
var uniqueMimeType = true
var mimeKind: String? = null
for (mime in parsedTypes) {
val kind = mime.split("/")[0]
if (mimeKind == null) {
mimeKind = kind
} else if (mimeKind != kind) {
uniqueMimeType = false
}
}
intent.type = if (uniqueMimeType) Intent.normalizeMimeType("$mimeKind/*") else "*/*"
setIntentMimeTypes(intent, parsedTypes)
intent
} else {
val intent = Intent(Intent.ACTION_GET_CONTENT)
@@ -130,12 +119,46 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
private fun parseFiltersOption(filters: Array<Filter>): Array<String> {
val mimeTypes = mutableListOf<String>()
for (filter in filters) {
for (mime in filter.extensions) {
mimeTypes.add(if (mime == "text/csv") "text/comma-separated-values" else mime)
for (ext in filter.extensions) {
if (ext.contains('/')) {
mimeTypes.add(if (ext == "text/csv") "text/comma-separated-values" else ext)
} else {
MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext)?.let {
mimeTypes.add(it)
}
}
}
}
return mimeTypes.toTypedArray()
}
private fun setIntentMimeTypes(intent: Intent, mimeTypes: Array<String>) {
if (mimeTypes.isNotEmpty()) {
var uniqueMimeKind = true
var mimeKind: String? = null
for (mime in mimeTypes) {
val kind = mime.split("/")[0]
if (mimeKind == null) {
mimeKind = kind
} else if (mimeKind != kind) {
uniqueMimeKind = false
}
}
if (uniqueMimeKind) {
if (mimeTypes.size > 1) {
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
intent.type = Intent.normalizeMimeType("$mimeKind/*")
} else {
intent.type = mimeTypes[0]
}
} else {
intent.type = "*/*"
}
} else {
intent.type = "*/*"
}
}
@Command
fun showMessageDialog(invoke: Invoke) {
@@ -187,10 +210,12 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
fun saveFileDialog(invoke: Invoke) {
try {
val args = invoke.parseArgs(SaveFileDialogOptions::class.java)
val parsedTypes = parseFiltersOption(args.filters)
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
setIntentMimeTypes(intent, parsedTypes)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.setType("text/plain")
intent.putExtra(Intent.EXTRA_TITLE, args.fileName ?: "")
startActivityForResult(invoke, intent, "saveFileDialogResult")
} catch (ex: Exception) {
+27 -24
View File
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc, time::Duration};
use std::{future::Future, pin::Pin, str::FromStr, sync::Arc, time::Duration};
use http::{header, HeaderName, Method, StatusCode};
use http::{header, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
use reqwest::{redirect::Policy, NoProxy};
use serde::{Deserialize, Serialize};
use tauri::{
@@ -176,7 +176,7 @@ pub async fn fetch<R: Runtime>(
let ClientConfig {
method,
url,
headers,
headers: headers_raw,
data,
connect_timeout,
max_redirections,
@@ -185,7 +185,17 @@ pub async fn fetch<R: Runtime>(
let scheme = url.scheme();
let method = Method::from_bytes(method.as_bytes())?;
let headers: HashMap<String, String> = HashMap::from_iter(headers);
let mut headers = HeaderMap::new();
for (h, v) in headers_raw {
let name = HeaderName::from_str(&h)?;
#[cfg(not(feature = "unsafe-headers"))]
if is_unsafe_header(&name) {
continue;
}
headers.append(name, HeaderValue::from_str(&v)?);
}
match scheme {
"http" | "https" => {
@@ -228,38 +238,29 @@ pub async fn fetch<R: Runtime>(
let mut request = builder.build()?.request(method.clone(), url);
for (name, value) in &headers {
let name = HeaderName::from_bytes(name.as_bytes())?;
#[cfg(not(feature = "unsafe-headers"))]
if is_unsafe_header(&name) {
continue;
}
request = request.header(name, value);
}
// POST and PUT requests should always have a 0 length content-length,
// if there is no body. https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
if data.is_none() && matches!(method, Method::POST | Method::PUT) {
request = request.header(header::CONTENT_LENGTH, 0);
headers.append(header::CONTENT_LENGTH, HeaderValue::from_str("0")?);
}
if headers.contains_key(header::RANGE.as_str()) {
if headers.contains_key(header::RANGE) {
// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch step 18
// If httpRequests header list contains `Range`, then append (`Accept-Encoding`, `identity`)
request = request.header(header::ACCEPT_ENCODING, "identity");
headers.append(header::ACCEPT_ENCODING, HeaderValue::from_str("identity")?);
}
if !headers.contains_key(header::USER_AGENT.as_str()) {
request = request.header(header::USER_AGENT, HTTP_USER_AGENT);
if !headers.contains_key(header::USER_AGENT) {
headers.append(header::USER_AGENT, HeaderValue::from_str(HTTP_USER_AGENT)?);
}
if cfg!(feature = "unsafe-headers")
&& !headers.contains_key(header::ORIGIN.as_str())
{
// ensure we have an Origin header set
if cfg!(not(feature = "unsafe-headers")) || !headers.contains_key(header::ORIGIN) {
if let Ok(url) = webview.url() {
request =
request.header(header::ORIGIN, url.origin().ascii_serialization());
headers.append(
header::ORIGIN,
HeaderValue::from_str(&url.origin().ascii_serialization())?,
);
}
}
@@ -267,6 +268,8 @@ pub async fn fetch<R: Runtime>(
request = request.body(data);
}
request = request.headers(headers);
let fut = async move { request.send().await.map_err(Into::into) };
let mut resources_table = webview.resources_table();
let rid = resources_table.add_request(Box::pin(fut));
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_POSITIONER__=function(t){"use strict";async function e(t,e={},o){return window.__TAURI_INTERNALS__.invoke(t,e,o)}var o;return"function"==typeof SuppressedError&&SuppressedError,t.Position=void 0,(o=t.Position||(t.Position={}))[o.TopLeft=0]="TopLeft",o[o.TopRight=1]="TopRight",o[o.BottomLeft=2]="BottomLeft",o[o.BottomRight=3]="BottomRight",o[o.TopCenter=4]="TopCenter",o[o.BottomCenter=5]="BottomCenter",o[o.LeftCenter=6]="LeftCenter",o[o.RightCenter=7]="RightCenter",o[o.Center=8]="Center",o[o.TrayLeft=9]="TrayLeft",o[o.TrayBottomLeft=10]="TrayBottomLeft",o[o.TrayRight=11]="TrayRight",o[o.TrayBottomRight=12]="TrayBottomRight",o[o.TrayCenter=13]="TrayCenter",o[o.TrayBottomCenter=14]="TrayBottomCenter",t.handleIconState=async function(t){const o={};o[`${t.rect.size.type}`]={width:t.rect.size.width,height:t.rect.size.height};const i={};i[`${t.rect.position.type}`]={x:t.rect.position.x,y:t.rect.position.y},await e("plugin:positioner|set_tray_icon_state",{position:i,size:o})},t.moveWindow=async function(t){await e("plugin:positioner|move_window",{position:t})},t}({});Object.defineProperty(window.__TAURI__,"positioner",{value:__TAURI_PLUGIN_POSITIONER__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_POSITIONER__=function(t){"use strict";async function o(t,o={},e){return window.__TAURI_INTERNALS__.invoke(t,o,e)}var e;return"function"==typeof SuppressedError&&SuppressedError,t.Position=void 0,(e=t.Position||(t.Position={}))[e.TopLeft=0]="TopLeft",e[e.TopRight=1]="TopRight",e[e.BottomLeft=2]="BottomLeft",e[e.BottomRight=3]="BottomRight",e[e.TopCenter=4]="TopCenter",e[e.BottomCenter=5]="BottomCenter",e[e.LeftCenter=6]="LeftCenter",e[e.RightCenter=7]="RightCenter",e[e.Center=8]="Center",e[e.TrayLeft=9]="TrayLeft",e[e.TrayBottomLeft=10]="TrayBottomLeft",e[e.TrayRight=11]="TrayRight",e[e.TrayBottomRight=12]="TrayBottomRight",e[e.TrayCenter=13]="TrayCenter",e[e.TrayBottomCenter=14]="TrayBottomCenter",t.handleIconState=async function(t){await o("plugin:positioner|set_tray_icon_state",{position:t.rect.position,size:t.rect.size})},t.moveWindow=async function(t){await o("plugin:positioner|move_window",{position:t})},t}({});Object.defineProperty(window.__TAURI__,"positioner",{value:__TAURI_PLUGIN_POSITIONER__})}
+1 -1
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
const COMMANDS: &[&str] = &["move_window"];
const COMMANDS: &[&str] = &["move_window", "set_tray_icon_state"];
fn main() {
tauri_plugin::Builder::new(COMMANDS)
+2 -14
View File
@@ -40,20 +40,8 @@ export async function moveWindow(to: Position): Promise<void> {
}
export async function handleIconState(event: TrayIconEvent): Promise<void> {
const size = {} as Record<string, unknown>
size[`${event.rect.size.type}`] = {
width: event.rect.size.width,
height: event.rect.size.height
}
const position = {} as Record<string, unknown>
position[`${event.rect.position.type}`] = {
x: event.rect.position.x,
y: event.rect.position.y
}
await invoke('plugin:positioner|set_tray_icon_state', {
position,
size
position: event.rect.position,
size: event.rect.size
})
}
@@ -4,5 +4,10 @@
[[permission]]
identifier = "allow-set-tray-icon-state"
description = "Enables the set_tray_icon_state to handle events and set the TrayIcon state."
description = "Enables the set_tray_icon_state command without any pre-configured scope."
commands.allow = ["set_tray_icon_state"]
[[permission]]
identifier = "deny-set-tray-icon-state"
description = "Denies the set_tray_icon_state command without any pre-configured scope."
commands.deny = ["set_tray_icon_state"]
@@ -1,6 +1,6 @@
## Default Permission
Allows the move_window command
Allows the moveWindow and handleIconState APIs
- `allow-move-window`
- `set-tray-icon-state`
@@ -48,7 +48,20 @@ Denies the move_window command without any pre-configured scope.
</td>
<td>
Enables the set_tray_icon_state to handle events and set the TrayIcon state.
Enables the set_tray_icon_state command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`positioner:deny-set-tray-icon-state`
</td>
<td>
Denies the set_tray_icon_state command without any pre-configured scope.
</td>
</tr>
+1 -1
View File
@@ -1,4 +1,4 @@
"$schema" = "schemas/schema.json"
[default]
description = "Allows the move_window command"
description = "Allows the moveWindow and handleIconState APIs"
permissions = ["allow-move-window", "set-tray-icon-state"]
@@ -305,12 +305,17 @@
"const": "deny-move-window"
},
{
"description": "Enables the set_tray_icon_state to handle events and set the TrayIcon state.",
"description": "Enables the set_tray_icon_state command without any pre-configured scope.",
"type": "string",
"const": "allow-set-tray-icon-state"
},
{
"description": "Allows the move_window command",
"description": "Denies the set_tray_icon_state command without any pre-configured scope.",
"type": "string",
"const": "deny-set-tray-icon-state"
},
{
"description": "Allows the moveWindow and handleIconState APIs",
"type": "string",
"const": "default"
}
+1 -1
View File
@@ -303,7 +303,7 @@ pub fn kill<R: Runtime>(
}
#[tauri::command]
pub fn open<R: Runtime>(
pub async fn open<R: Runtime>(
_window: Window<R>,
shell: State<'_, Shell<R>>,
path: String,
@@ -9,6 +9,6 @@
"author": "",
"license": "MIT",
"devDependencies": {
"@tauri-apps/cli": "2.0.0"
"@tauri-apps/cli": "2.0.2"
}
}
@@ -8,7 +8,7 @@
"tauri": "tauri"
},
"devDependencies": {
"@tauri-apps/cli": "2.0.0",
"@tauri-apps/cli": "2.0.2",
"vite": "^5.0.12",
"typescript": "^5.4.7"
}
@@ -9,7 +9,7 @@
"preview": "vite preview"
},
"devDependencies": {
"@tauri-apps/cli": "2.0.0",
"@tauri-apps/cli": "2.0.2",
"typescript": "^5.3.3",
"vite": "^5.4.7"
},