Compare commits

...

9 Commits

Author SHA1 Message Date
Gregory Schier
03a4cd30ca Fix release (#30) 2024-05-14 15:35:53 -07:00
Gregory Schier
40b477ad8b Release 2024.4.1 (#28) 2024-05-14 15:01:55 -07:00
Gregory Schier
4f5dbc0770 Add num launches to notification call 2024-05-14 14:25:10 -07:00
Gregory Schier
eb5f9a4671 Fix window clicking on Linux 2024-05-14 12:19:19 -07:00
Gregory Schier
ad1a4eadd9 Slight refactor 2024-05-14 08:44:15 -07:00
Gregory Schier
69a151bfe5 Build plugin 2024-05-14 08:28:20 -07:00
Gregory Schier
d8d2f44723 Improve Curl imports 2024-05-14 08:28:01 -07:00
Gregory Schier
d5ea03ce91 Bump version for release 2024-05-14 07:50:01 -07:00
Gregory Schier
816bc543d7 Fix upgrade cancel 2024-05-14 07:08:57 -07:00
16 changed files with 6966 additions and 115 deletions

View File

@@ -1,10 +1,8 @@
name: Generate Artifacts
on:
push:
tags: [ v* ]
permissions: write-all
branches:
- release
jobs:
build-artifacts:
permissions:
@@ -23,20 +21,17 @@ jobs:
- platform: 'windows-latest'
args: ''
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: setup node
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
# Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds.
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
- name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-22.04' # This must match the platform value defined above.
run: |
@@ -44,13 +39,10 @@ jobs:
sudo apt-get install -y libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
# webkitgtk 4.0 is for Tauri v1 - webkitgtk 4.1 is for Tauri v2.
# You can remove the one that doesn't apply to your app to speed up the workflow a bit.
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Build Desktop" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="npm run tauri build -- --target universal-apple-darwin" />
<option name="SCRIPT_TEXT" value="npm run tauri build" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="" />
<option name="SCRIPT_OPTIONS" value="" />
@@ -9,12 +9,9 @@
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="/bin/zsh" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_IN_TERMINAL" value="false" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs>
<env name="TAURI_KEY_PASSWORD" value="fishhook-upstream-wash-assured" />
<env name="TAURI_PRIVATE_KEY" value="dW50cnVzdGVkIGNvbW1lbnQ6IHJzaWduIGVuY3J5cHRlZCBzZWNyZXQga2V5ClJXUlRZMEl5OGxWaytTa3dIa2xXVUltQzRGUXIzd2lYQ2NpV0ZhQURSbWJWZ1NrK0tnY0FBQkFBQUFBQUFBQUFBQUlBQUFBQUV2M1VKdVRyVHpHSzhQdGc2ZVFtOVNsMU5tNEVSN280cFNrbXhncW9tdjNXaFJZUTJqUzQ5Q01zWTJWRVhaY1pGNHNjR1NFR3JmcWFRN09NdWdGMXpZVXhzejR4V3lDV1JpZHlnbW5LNS9vMFFtRlZjbUl4YjZSNzhlMmk3ait5SExYcG5QZUkxOFE9Cg==" />
</envs>
<envs />
<method v="2" />
</configuration>
</component>

View File

@@ -184,7 +184,7 @@ export function importCommand(parseEntries: ParseEntry[], workspaceId: string) {
urlParameters =
search?.split('&').map((p) => {
const v = splitOnce(p, '=');
return { name: v[0] ?? '', value: v[1] ?? '' };
return { name: v[0] ?? '', value: v[1] ?? '', enabled: true };
}) ?? [];
url = baseUrl ?? urlArg;
@@ -212,11 +212,13 @@ export function importCommand(parseEntries: ParseEntry[], workspaceId: string) {
return {
name: (name ?? '').trim().replace(/;$/, ''),
value: '',
enabled: true,
};
}
return {
name: (name ?? '').trim(),
value: value.trim(),
enabled: true,
};
});
@@ -243,6 +245,7 @@ export function importCommand(parseEntries: ParseEntry[], workspaceId: string) {
headers.push({
name: 'Cookie',
value: cookieHeaderValue,
enabled: true,
});
}
@@ -286,12 +289,17 @@ export function importCommand(parseEntries: ParseEntry[], workspaceId: string) {
) {
bodyType = mimeType ?? 'application/x-www-form-urlencoded';
body = {
params: dataParameters.map((parameter) => ({
form: dataParameters.map((parameter) => ({
...parameter,
name: decodeURIComponent(parameter.name || ''),
value: decodeURIComponent(parameter.value || ''),
})),
};
headers.push({
name: 'Content-Type',
value: 'application/x-www-form-urlencoded',
enabled: true,
});
} else if (dataParameters.length > 0) {
bodyType =
mimeType === 'application/json' || mimeType === 'text/xml' || mimeType === 'text/plain'
@@ -307,13 +315,20 @@ export function importCommand(parseEntries: ParseEntry[], workspaceId: string) {
body = {
form: formDataParams,
};
if (mimeType == null) {
headers.push({
name: 'Content-Type',
value: 'multipart/form-data',
enabled: true,
});
}
}
// Method
let method = getPairValue(pairsByName, '', ['X', 'request']).toUpperCase();
if (method === '' && body) {
method = 'text' in body || 'params' in body ? 'POST' : 'GET';
method = 'text' in body || 'form' in body ? 'POST' : 'GET';
}
const request: ExportResources['httpRequests'][0] = {
@@ -342,6 +357,7 @@ const pairsToDataParameters = (keyedPairs: PairsByName) => {
value: string;
contentType?: string;
filePath?: string;
enabled?: boolean;
}[] = [];
for (const flagName of DATA_FLAGS) {
@@ -361,11 +377,13 @@ const pairsToDataParameters = (keyedPairs: PairsByName) => {
name: name ?? '',
value: '',
filePath: p.slice(1),
enabled: true,
});
} else {
dataParameters.push({
name: name ?? '',
value: flagName === 'data-urlencode' ? encodeURIComponent(value ?? '') : value ?? '',
enabled: true,
});
}
}

View File

@@ -122,6 +122,13 @@ describe('importer-curl', () => {
baseRequest({
method: 'POST',
url: 'https://yaak.app',
headers: [
{
name: 'Content-Type',
value: 'multipart/form-data',
enabled: true,
},
],
bodyType: 'multipart/form-data',
body: {
form: [
@@ -145,11 +152,18 @@ describe('importer-curl', () => {
method: 'POST',
url: 'https://yaak.app',
bodyType: 'application/x-www-form-urlencoded',
headers: [
{
name: 'Content-Type',
value: 'application/x-www-form-urlencoded',
enabled: true,
},
],
body: {
params: [
{ name: 'a', value: '' },
{ name: 'b', value: '' },
{ name: 'c', value: 'ccc' },
form: [
{ name: 'a', value: '', enabled: true },
{ name: 'b', value: '', enabled: true },
{ name: 'c', value: 'ccc', enabled: true },
],
},
}),
@@ -168,7 +182,7 @@ describe('importer-curl', () => {
baseRequest({
method: 'POST',
url: 'https://yaak.app',
headers: [{ name: 'Content-Type', value: 'text/plain' }],
headers: [{ name: 'Content-Type', value: 'text/plain', enabled: true }],
bodyType: 'text/plain',
body: { text: 'a&b&c=ccc' },
}),
@@ -189,7 +203,7 @@ describe('importer-curl', () => {
baseRequest({
method: 'POST',
url: 'https://yaak.app',
headers: [{ name: 'Content-Type', value: 'application/json' }],
headers: [{ name: 'Content-Type', value: 'application/json', enabled: true }],
bodyType: 'application/json',
body: { text: '{\n "foo":"bar"\n}' },
}),
@@ -208,10 +222,10 @@ describe('importer-curl', () => {
baseRequest({
url: 'https://yaak.app',
headers: [
{ name: 'Name', value: '' },
{ name: 'Foo', value: 'bar' },
{ name: 'AAA', value: 'bbb' },
{ name: '', value: 'ccc' },
{ name: 'Name', value: '', enabled: true },
{ name: 'Foo', value: 'bar', enabled: true },
{ name: 'AAA', value: 'bbb', enabled: true },
{ name: '', value: 'ccc', enabled: true },
],
}),
],
@@ -262,7 +276,7 @@ describe('importer-curl', () => {
httpRequests: [
baseRequest({
url: 'https://yaak.app',
headers: [{ name: 'Cookie', value: 'foo=bar' }],
headers: [{ name: 'Cookie', value: 'foo=bar', enabled: true }],
}),
],
},
@@ -277,8 +291,8 @@ describe('importer-curl', () => {
baseRequest({
url: 'https://yaak.app',
urlParameters: [
{ name: 'foo', value: 'bar' },
{ name: 'baz', value: 'a%20a' },
{ name: 'foo', value: 'bar', enabled: true },
{ name: 'baz', value: 'a%20a', enabled: true },
],
}),
],

View File

@@ -43,6 +43,7 @@
"window:allow-is-fullscreen",
"window:allow-maximize",
"window:allow-minimize",
"window:allow-toggle-maximize",
"window:allow-set-decorations",
"window:allow-set-title",
"window:allow-start-dragging",

View File

@@ -1 +1 @@
{"main":{"identifier":"main","description":"Main permissions","local":true,"windows":["*"],"permissions":["os:allow-os-type","event:allow-emit","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:allow-open","dialog:allow-save","event:allow-listen","event:allow-unlisten","fs:allow-read-file","fs:allow-read-text-file",{"identifier":"fs:scope","allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]},"shell:allow-open",{"identifier":"shell:allow-execute","allow":[{"args":true,"name":"protoc","sidecar":true}]},"window:allow-close","window:allow-is-fullscreen","window:allow-maximize","window:allow-minimize","window:allow-set-decorations","window:allow-set-title","window:allow-start-dragging","window:allow-unmaximize","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text"]}}
{"main":{"identifier":"main","description":"Main permissions","local":true,"windows":["*"],"permissions":["os:allow-os-type","event:allow-emit","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:allow-open","dialog:allow-save","event:allow-listen","event:allow-unlisten","fs:allow-read-file","fs:allow-read-text-file",{"identifier":"fs:scope","allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]},"shell:allow-open",{"identifier":"shell:allow-execute","allow":[{"args":true,"name":"protoc","sidecar":true}]},"window:allow-close","window:allow-is-fullscreen","window:allow-maximize","window:allow-minimize","window:allow-toggle-maximize","window:allow-set-decorations","window:allow-set-title","window:allow-start-dragging","window:allow-unmaximize","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text"]}}

File diff suppressed because it is too large Load Diff

View File

@@ -9,10 +9,10 @@ var j = "(?:" + [
">\\&",
"<\\&",
"[&;()|<>]"
].join("|") + ")", D = new RegExp("^" + j + "$"), q = "|&;()<> \\t", M = '"((\\\\"|[^"])*?)"', Q = "'((\\\\'|[^'])*?)'", V = /^#$/, _ = "'", G = '"', U = "$", R = "", z = 4294967296;
].join("|") + ")", D = new RegExp("^" + j + "$"), q = "|&;()<> \\t", M = '"((\\\\"|[^"])*?)"', Q = "'((\\\\'|[^'])*?)'", V = /^#$/, _ = "'", G = '"', U = "$", $ = "", z = 4294967296;
for (var L = 0; L < 4; L++)
R += (z * Math.random()).toString(16);
var J = new RegExp("^" + R);
$ += (z * Math.random()).toString(16);
var J = new RegExp("^" + $);
function X(n, s) {
for (var e = s.lastIndex, t = [], c; c = s.exec(n); )
t.push(c), s.lastIndex === c.index && (s.lastIndex += 1);
@@ -20,7 +20,7 @@ function X(n, s) {
}
function F(n, s, e) {
var t = typeof n == "function" ? n(e) : n[e];
return typeof t > "u" && e != "" ? t = "" : typeof t > "u" && (t = "$"), typeof t == "object" ? s + R + JSON.stringify(t) + R : s + t;
return typeof t > "u" && e != "" ? t = "" : typeof t > "u" && (t = "$"), typeof t == "object" ? s + $ + JSON.stringify(t) + $ : s + t;
}
function K(n, s, e) {
e || (e = {});
@@ -39,30 +39,30 @@ function K(n, s, e) {
return;
if (D.test(a))
return { op: a };
var x = !1, O = !1, p = "", A = !1, i;
function b() {
var x = !1, C = !1, d = "", O = !1, i;
function T() {
i += 1;
var v, d, T = a.charAt(i);
if (T === "{") {
var v, p, R = a.charAt(i);
if (R === "{") {
if (i += 1, a.charAt(i) === "}")
throw new Error("Bad substitution: " + a.slice(i - 2, i + 1));
if (v = a.indexOf("}", i), v < 0)
throw new Error("Bad substitution: " + a.slice(i));
d = a.slice(i, v), i = v;
} else if (/[*@#?$!_-]/.test(T))
d = T, i += 1;
p = a.slice(i, v), i = v;
} else if (/[*@#?$!_-]/.test(R))
p = R, i += 1;
else {
var g = a.slice(i);
v = g.match(/[^\w\d_]/), v ? (d = g.slice(0, v.index), i += v.index - 1) : (d = g, i = a.length);
v = g.match(/[^\w\d_]/), v ? (p = g.slice(0, v.index), i += v.index - 1) : (p = g, i = a.length);
}
return F(s, "", d);
return F(s, "", p);
}
for (i = 0; i < a.length; i++) {
var u = a.charAt(i);
if (A = A || !x && (u === "*" || u === "?"), O)
p += u, O = !1;
if (O = O || !x && (u === "*" || u === "?"), C)
d += u, C = !1;
else if (x)
u === x ? x = !1 : x == _ ? p += u : u === t ? (i += 1, u = a.charAt(i), u === G || u === t || u === U ? p += u : p += t + u) : u === U ? p += b() : p += u;
u === x ? x = !1 : x == _ ? d += u : u === t ? (i += 1, u = a.charAt(i), u === G || u === t || u === U ? d += u : d += t + u) : u === U ? d += T() : d += u;
else if (u === G || u === _)
x = u;
else {
@@ -70,13 +70,13 @@ function K(n, s, e) {
return { op: a };
if (V.test(u)) {
w = !0;
var E = { comment: n.slice(r.index + i + 1) };
return p.length ? [p, E] : [E];
var b = { comment: n.slice(r.index + i + 1) };
return d.length ? [d, b] : [b];
} else
u === t ? O = !0 : u === U ? p += b() : p += u;
u === t ? C = !0 : u === U ? d += T() : d += u;
}
}
return A ? { op: "glob", pattern: p } : p;
return O ? { op: "glob", pattern: d } : d;
}).reduce(function(r, a) {
return typeof a > "u" ? r : r.concat(a);
}, []);
@@ -86,9 +86,9 @@ var Y = function(s, e, t) {
return typeof e != "function" ? c : c.reduce(function(m, f) {
if (typeof f == "object")
return m.concat(f);
var w = f.split(RegExp("(" + R + ".*?" + R + ")", "g"));
var w = f.split(RegExp("(" + $ + ".*?" + $ + ")", "g"));
return w.length === 1 ? m.concat(w[0]) : m.concat(w.filter(Boolean).map(function(r) {
return J.test(r) ? JSON.parse(r.split(R)[1]) : r;
return J.test(r) ? JSON.parse(r.split($)[1]) : r;
}));
}, []);
}, Z = Y;
@@ -158,73 +158,84 @@ function te(n, s) {
for (let o = 1; o < n.length; o++) {
let l = n[o];
if (typeof l == "string" && (l = l.trim()), typeof l == "string" && l.match(/^-{1,2}[\w-]+/)) {
const y = l[0] === "-" && l[1] !== "-";
const E = l[0] === "-" && l[1] !== "-";
let h = l.replace(/^-{1,2}/, "");
if (!ee.includes(h))
continue;
let $;
let y;
const S = n[o + 1];
y && h.length > 1 ? ($ = h.slice(1), h = h.slice(0, 1)) : typeof S == "string" && !S.startsWith("-") ? ($ = S, o++) : $ = !0, e[h] = e[h] || [], e[h].push($);
E && h.length > 1 ? (y = h.slice(1), h = h.slice(0, 1)) : typeof S == "string" && !S.startsWith("-") ? (y = S, o++) : y = !0, e[h] = e[h] || [], e[h].push(y);
} else
l && t.push(l);
}
let c, m;
const f = C(e, t[0] || "", ["url"]), [w, r] = W(f, "?");
const f = A(e, t[0] || "", ["url"]), [w, r] = W(f, "?");
c = (r == null ? void 0 : r.split("&").map((o) => {
const l = W(o, "=");
return { name: l[0] ?? "", value: l[1] ?? "" };
return { name: l[0] ?? "", value: l[1] ?? "", enabled: !0 };
})) ?? [], m = w ?? f;
const [a, x] = C(e, "", ["u", "user"]).split(/:(.*)$/), O = C(e, !1, ["digest"]), p = a ? O ? "digest" : "basic" : null, A = a ? {
const [a, x] = A(e, "", ["u", "user"]).split(/:(.*)$/), C = A(e, !1, ["digest"]), d = a ? C ? "digest" : "basic" : null, O = a ? {
username: a.trim(),
password: (x ?? "").trim()
} : {}, i = [
...e.header || [],
...e.H || []
].map((o) => {
const [l, y] = o.split(/:(.*)$/);
return y ? {
const [l, E] = o.split(/:(.*)$/);
return E ? {
name: (l ?? "").trim(),
value: y.trim()
value: E.trim(),
enabled: !0
} : {
name: (l ?? "").trim().replace(/;$/, ""),
value: ""
value: "",
enabled: !0
};
}), b = [
}), T = [
...e.cookie || [],
...e.b || []
].map((o) => {
const l = o.split("=", 1)[0], y = o.replace(`${l}=`, "");
return `${l}=${y}`;
const l = o.split("=", 1)[0], E = o.replace(`${l}=`, "");
return `${l}=${E}`;
}).join("; "), u = i.find((o) => o.name.toLowerCase() === "cookie");
b && u ? u.value += `; ${b}` : b && i.push({
T && u ? u.value += `; ${T}` : T && i.push({
name: "Cookie",
value: b
value: T,
enabled: !0
});
const E = ne(e), v = i.find((o) => o.name.toLowerCase() === "content-type"), d = v ? v.value.split(";")[0] : null, T = [
const b = ne(e), v = i.find((o) => o.name.toLowerCase() === "content-type"), p = v ? v.value.split(";")[0] : null, R = [
...e.form || [],
...e.F || []
].map((o) => {
const l = o.split("="), y = l[0] ?? "", h = l[1] ?? "", $ = {
name: y,
const l = o.split("="), E = l[0] ?? "", h = l[1] ?? "", y = {
name: E,
enabled: !0
};
return h.indexOf("@") === 0 ? $.file = h.slice(1) : $.value = h, $;
return h.indexOf("@") === 0 ? y.file = h.slice(1) : y.value = h, y;
});
let g = {}, I = null;
const B = C(e, !1, ["G", "get"]);
E.length > 0 && B ? c.push(...E) : E.length > 0 && (d == null || d === "application/x-www-form-urlencoded") ? (I = d ?? "application/x-www-form-urlencoded", g = {
params: E.map((o) => ({
const B = A(e, !1, ["G", "get"]);
b.length > 0 && B ? c.push(...b) : b.length > 0 && (p == null || p === "application/x-www-form-urlencoded") ? (I = p ?? "application/x-www-form-urlencoded", g = {
form: b.map((o) => ({
...o,
name: decodeURIComponent(o.name || ""),
value: decodeURIComponent(o.value || "")
}))
}) : E.length > 0 ? (I = d === "application/json" || d === "text/xml" || d === "text/plain" ? d : "other", g = {
text: E.map(({ name: o, value: l }) => o && l ? `${o}=${l}` : o || l).join("&")
}) : T.length && (I = d ?? "multipart/form-data", g = {
form: T
});
let P = C(e, "", ["X", "request"]).toUpperCase();
return P === "" && g && (P = "text" in g || "params" in g ? "POST" : "GET"), {
}, i.push({
name: "Content-Type",
value: "application/x-www-form-urlencoded",
enabled: !0
})) : b.length > 0 ? (I = p === "application/json" || p === "text/xml" || p === "text/plain" ? p : "other", g = {
text: b.map(({ name: o, value: l }) => o && l ? `${o}=${l}` : o || l).join("&")
}) : R.length && (I = p ?? "multipart/form-data", g = {
form: R
}, p == null && i.push({
name: "Content-Type",
value: "multipart/form-data",
enabled: !0
}));
let P = A(e, "", ["X", "request"]).toUpperCase();
return P === "" && g && (P = "text" in g || "form" in g ? "POST" : "GET"), {
id: N("http_request"),
model: "http_request",
workspaceId: s,
@@ -233,8 +244,8 @@ function te(n, s) {
url: m,
method: P,
headers: i,
authentication: A,
authenticationType: p,
authentication: O,
authenticationType: d,
body: g,
bodyType: I,
folderId: null,
@@ -253,15 +264,17 @@ const ne = (n) => {
c.startsWith("@") ? s.push({
name: m ?? "",
value: "",
filePath: c.slice(1)
filePath: c.slice(1),
enabled: !0
}) : s.push({
name: m ?? "",
value: e === "data-urlencode" ? encodeURIComponent(f ?? "") : f ?? ""
value: e === "data-urlencode" ? encodeURIComponent(f ?? "") : f ?? "",
enabled: !0
});
}
}
return s;
}, C = (n, s, e) => {
}, A = (n, s, e) => {
for (const t of e)
if (n[t] && n[t].length)
return n[t][0];

View File

@@ -11,6 +11,9 @@ use crate::models::{
generate_id, get_key_value_int, get_key_value_string, set_key_value_int, set_key_value_string,
};
const NAMESPACE: &str = "analytics";
const NUM_LAUNCHES_KEY: &str = "num_launches";
// serializable
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
@@ -93,20 +96,19 @@ pub struct LaunchEventInfo {
pub num_launches: i32,
}
pub async fn track_launch_event(app_handle: &AppHandle) -> LaunchEventInfo {
let namespace = "analytics";
pub async fn track_launch_event(app: &AppHandle) -> LaunchEventInfo {
let last_tracked_version_key = "last_tracked_version";
let mut info = LaunchEventInfo::default();
info.num_launches = get_key_value_int(app_handle, namespace, "num_launches", 0).await + 1;
info.num_launches = get_num_launches(app).await + 1;
info.previous_version =
get_key_value_string(app_handle, namespace, last_tracked_version_key, "").await;
info.current_version = app_handle.package_info().version.to_string();
get_key_value_string(app, NAMESPACE, last_tracked_version_key, "").await;
info.current_version = app.package_info().version.to_string();
if info.previous_version.is_empty() {
track_event(
app_handle,
app,
AnalyticsResource::App,
AnalyticsAction::LaunchFirst,
None,
@@ -116,10 +118,10 @@ pub async fn track_launch_event(app_handle: &AppHandle) -> LaunchEventInfo {
info.launched_after_update = info.current_version != info.previous_version;
if info.launched_after_update {
track_event(
app_handle,
app,
AnalyticsResource::App,
AnalyticsAction::LaunchUpdate,
Some(json!({ "num_launches": info.num_launches })),
Some(json!({ NUM_LAUNCHES_KEY: info.num_launches })),
)
.await;
}
@@ -127,23 +129,23 @@ pub async fn track_launch_event(app_handle: &AppHandle) -> LaunchEventInfo {
// Track a launch event in all cases
track_event(
app_handle,
app,
AnalyticsResource::App,
AnalyticsAction::Launch,
Some(json!({ "num_launches": info.num_launches })),
Some(json!({ NUM_LAUNCHES_KEY: info.num_launches })),
)
.await;
// Update key values
set_key_value_string(
app_handle,
namespace,
app,
NAMESPACE,
last_tracked_version_key,
info.current_version.as_str(),
)
.await;
set_key_value_int(app_handle, namespace, "num_launches", info.num_launches).await;
set_key_value_int(app, NAMESPACE, NUM_LAUNCHES_KEY, info.num_launches).await;
info
}
@@ -242,3 +244,7 @@ async fn get_id(app_handle: &AppHandle) -> String {
id
}
}
pub async fn get_num_launches(app: &AppHandle) -> i32 {
get_key_value_int(app, NAMESPACE, NUM_LAUNCHES_KEY, 0).await
}

View File

@@ -1721,6 +1721,9 @@ fn is_dev() -> bool {
fn create_window(handle: &AppHandle, url: Option<&str>) -> WebviewWindow {
let menu = app_menu(handle).unwrap();
// This causes the window to not be clickable (in AppImage), so disable on Linux
#[cfg(not(target_os = "linux"))]
handle.set_menu(menu).expect("Failed to set app menu");
let window_num = handle.webview_windows().len();

View File

@@ -6,6 +6,7 @@ use log::debug;
use serde::{Deserialize, Serialize};
use tauri::{AppHandle, Manager};
use crate::analytics::get_num_launches;
use crate::models::{get_key_value_raw, set_key_value_raw};
// Check for updates every hour
@@ -60,10 +61,14 @@ impl YaakNotifier {
self.last_check = SystemTime::now();
let num_launches = get_num_launches(app).await;
let info = app.package_info().clone();
let req = reqwest::Client::default()
.request(Method::GET, "https://notify.yaak.app/notifications")
.query(&[("version", info.version)]);
.query(&[
("version", info.version.to_string()),
("launches", num_launches.to_string()),
]);
let resp = req.send().await.map_err(|e| e.to_string())?;
let notification = resp
.json::<YaakNotification>()

View File

@@ -54,21 +54,20 @@ impl YaakUpdater {
self.last_update_check = SystemTime::now();
let enabled = !is_dev();
info!(
"Checking for updates mode={} enabled={}",
mode, enabled
);
info!("Checking for updates mode={} enabled={}", mode, enabled);
if !enabled {
return Ok(false);
}
match app_handle
let update_check_result = app_handle
.updater_builder()
.header("X-Update-Mode", mode.to_string())?
.build()?
.check()
.await
.await;
match update_check_result
{
Ok(Some(update)) => {
let h = app_handle.clone();
@@ -78,6 +77,8 @@ impl YaakUpdater {
"{} is available. Would you like to download and install it now?",
update.version
))
.ok_button_label("Download")
.cancel_button_label("Later")
.title("Update Available")
.show(|confirmed| {
if !confirmed {
@@ -89,6 +90,8 @@ impl YaakUpdater {
if h.dialog()
.message("Would you like to restart the app?")
.title("Update Installed")
.ok_button_label("Restart")
.cancel_button_label("Later")
.blocking_show()
{
h.restart();

View File

@@ -6,7 +6,7 @@
"frontendDist": "../dist"
},
"productName": "Yaak",
"version": "2024.4.0-beta.3",
"version": "2024.4.2",
"identifier": "app.yaak.desktop",
"app": {
"withGlobalTauri": false,

View File

@@ -40,7 +40,7 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
<div className="pointer-events-none">
<RecentRequestsDropdown />
</div>
<div className="flex-1 flex items-center h-full justify-end pointer-events-none">
<div className="flex-1 flex gap-1 items-center h-full justify-end pointer-events-none">
<ImportCurlButton />
<SettingsDropdown />
{(osInfo?.osType === 'linux' || osInfo?.osType === 'windows') && (

View File

@@ -21,12 +21,20 @@ export function twig(
const completions = twigCompletion({ options: variables });
const language = mixLanguage(base);
const completion = language.data.of({ autocomplete: completions });
const completionBase = base.language.data.of({ autocomplete: completions });
const additionalCompletion = autocomplete
? [base.language.data.of({ autocomplete: genericCompletion(autocomplete) })]
: [];
return [language, completionBase, base.support, placeholders(variables), ...additionalCompletion];
return [
language,
completion,
completionBase,
base.support,
placeholders(variables),
...additionalCompletion,
];
}
function mixLanguage(base: LanguageSupport): LRLanguage {

View File

@@ -496,7 +496,7 @@ const newPairContainer = (initialPair?: Pair): PairContainer => {
};
const getFileName = (path: string | null | undefined): string => {
console.log('PATH', path);
const parts = String(path).split(/[\\/]/);
if (typeof path !== 'string') return '';
const parts = path.split(/[\\/]/);
return parts[parts.length - 1] ?? '';
};