mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-20 05:19:23 +01:00
Compare commits
5 Commits
v0.7.1
...
add-macos-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7f8f7ef6e | ||
|
|
4709f8c660 | ||
|
|
4696aa1759 | ||
|
|
924fc09516 | ||
|
|
5a700662dd |
@@ -5,7 +5,7 @@ title: "Release Notes"
|
|||||||
|
|
||||||
# Release Notes
|
# Release Notes
|
||||||
|
|
||||||
Information about release notes of Coco Server is provided here.
|
Information about release notes of Coco App is provided here.
|
||||||
|
|
||||||
## Latest (In development)
|
## Latest (In development)
|
||||||
|
|
||||||
@@ -13,6 +13,20 @@ Information about release notes of Coco Server is provided here.
|
|||||||
|
|
||||||
### 🚀 Features
|
### 🚀 Features
|
||||||
|
|
||||||
|
- feat: enhance ui for skipped version #834
|
||||||
|
|
||||||
|
### 🐛 Bug fix
|
||||||
|
|
||||||
|
- fix: fix issue with update check failure #833
|
||||||
|
|
||||||
|
### ✈️ Improvements
|
||||||
|
|
||||||
|
## 0.7.1 (2025-07-27)
|
||||||
|
|
||||||
|
### ❌ Breaking changes
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
### 🐛 Bug fix
|
### 🐛 Bug fix
|
||||||
|
|
||||||
- fix: correct enter key behavior #828
|
- fix: correct enter key behavior #828
|
||||||
@@ -20,6 +34,7 @@ Information about release notes of Coco Server is provided here.
|
|||||||
### ✈️ Improvements
|
### ✈️ Improvements
|
||||||
|
|
||||||
- chore: web component add notification component #825
|
- chore: web component add notification component #825
|
||||||
|
- refactor: collection behavior defaults to `MoveToActiveSpace`, and only use `CanJoinAllSpaces` when window is pinned #829
|
||||||
|
|
||||||
## 0.7.0 (2025-07-25)
|
## 0.7.0 (2025-07-25)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use tauri_plugin_updater::RemoteRelease;
|
|||||||
///
|
///
|
||||||
/// If the version string is in the `x.y.z` format and does not include a build
|
/// If the version string is in the `x.y.z` format and does not include a build
|
||||||
/// number, we assume a build number of 0.
|
/// number, we assume a build number of 0.
|
||||||
fn extract_version_number(version: &Version) -> u32 {
|
fn extract_build_number(version: &Version) -> u32 {
|
||||||
let pre = &version.pre;
|
let pre = &version.pre;
|
||||||
|
|
||||||
if pre.is_empty() {
|
if pre.is_empty() {
|
||||||
@@ -52,8 +52,8 @@ fn extract_version_number(version: &Version) -> u32 {
|
|||||||
pub(crate) fn custom_version_comparator(local: Version, remote_release: RemoteRelease) -> bool {
|
pub(crate) fn custom_version_comparator(local: Version, remote_release: RemoteRelease) -> bool {
|
||||||
let remote = remote_release.version;
|
let remote = remote_release.version;
|
||||||
|
|
||||||
let local_build_number = extract_version_number(&local);
|
let local_build_number = extract_build_number(&local);
|
||||||
let remote_build_number = extract_version_number(&remote);
|
let remote_build_number = extract_build_number(&remote);
|
||||||
|
|
||||||
let should_update = remote_build_number > local_build_number;
|
let should_update = remote_build_number > local_build_number;
|
||||||
log::debug!(
|
log::debug!(
|
||||||
@@ -65,3 +65,23 @@ pub(crate) fn custom_version_comparator(local: Version, remote_release: RemoteRe
|
|||||||
|
|
||||||
should_update
|
should_update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_build_number() {
|
||||||
|
// 0.6.0 => 0
|
||||||
|
let version = Version::parse("0.6.0").unwrap();
|
||||||
|
assert_eq!(extract_build_number(&version), 0);
|
||||||
|
|
||||||
|
// 0.6.0-2371 => 2371
|
||||||
|
let version = Version::parse("0.6.0-2371").unwrap();
|
||||||
|
assert_eq!(extract_build_number(&version), 2371);
|
||||||
|
|
||||||
|
// 0.6.0-SNAPSHOT-2371 => 2371
|
||||||
|
let version = Version::parse("0.6.0-SNAPSHOT-2371").unwrap();
|
||||||
|
assert_eq!(extract_build_number(&version), 2371);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"identifier": "rs.coco.app",
|
||||||
"bundle": {
|
"bundle": {
|
||||||
"macOS": {
|
"macOS": {
|
||||||
"entitlements": "./Entitlements.plist",
|
"entitlements": "./Entitlements.plist",
|
||||||
|
|||||||
8
src-tauri/tauri.macos.conf.json
Normal file
8
src-tauri/tauri.macos.conf.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"identifier": "rs.coco.app",
|
||||||
|
"bundle": {
|
||||||
|
"macOS": {
|
||||||
|
"entitlements": "./Entitlements.plist"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { ArrowDown01, CornerDownLeft } from "lucide-react";
|
import { ArrowDown01, CornerDownLeft } from "lucide-react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
@@ -44,7 +44,7 @@ export default function Footer({ setIsPinnedWeb }: FooterProps) {
|
|||||||
onPinChange: setIsPinnedWeb,
|
onPinChange: setIsPinnedWeb,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { setVisible, updateInfo } = useUpdateStore();
|
const { setVisible, updateInfo, skipVersions } = useUpdateStore();
|
||||||
|
|
||||||
const { fixedWindow, modifierKey } = useShortcutsStore();
|
const { fixedWindow, modifierKey } = useShortcutsStore();
|
||||||
|
|
||||||
@@ -52,6 +52,10 @@ export default function Footer({ setIsPinnedWeb }: FooterProps) {
|
|||||||
return platformAdapter.emitEvent("open_settings", "");
|
return platformAdapter.emitEvent("open_settings", "");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const hasUpdate = useMemo(() => {
|
||||||
|
return updateInfo && !skipVersions.includes(updateInfo.version);
|
||||||
|
}, [updateInfo, skipVersions]);
|
||||||
|
|
||||||
const renderLeft = () => {
|
const renderLeft = () => {
|
||||||
if (sourceData?.source?.name) {
|
if (sourceData?.source?.name) {
|
||||||
return (
|
return (
|
||||||
@@ -97,7 +101,7 @@ export default function Footer({ setIsPinnedWeb }: FooterProps) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="relative text-xs text-gray-500 dark:text-gray-400">
|
<div className="relative text-xs text-gray-500 dark:text-gray-400">
|
||||||
{updateInfo?.available ? (
|
{hasUpdate ? (
|
||||||
<div className="cursor-pointer" onClick={() => setVisible(true)}>
|
<div className="cursor-pointer" onClick={() => setVisible(true)}>
|
||||||
<span>{t("search.footer.updateAvailable")}</span>
|
<span>{t("search.footer.updateAvailable")}</span>
|
||||||
<span className="absolute top-0 -right-2 size-1.5 bg-[#FF3434] rounded-full"></span>
|
<span className="absolute top-0 -right-2 size-1.5 bg-[#FF3434] rounded-full"></span>
|
||||||
@@ -127,7 +131,7 @@ export default function Footer({ setIsPinnedWeb }: FooterProps) {
|
|||||||
onClick={togglePin}
|
onClick={togglePin}
|
||||||
className={clsx({
|
className={clsx({
|
||||||
"text-blue-500": isPinned,
|
"text-blue-500": isPinned,
|
||||||
"pl-2": updateInfo?.available,
|
"pl-2": hasUpdate,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<VisibleKey shortcut={fixedWindow} onKeyPress={togglePin}>
|
<VisibleKey shortcut={fixedWindow} onKeyPress={togglePin}>
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
|||||||
const {
|
const {
|
||||||
visible,
|
visible,
|
||||||
setVisible,
|
setVisible,
|
||||||
skipVersion,
|
skipVersions,
|
||||||
setSkipVersion,
|
setSkipVersions,
|
||||||
isOptional,
|
isOptional,
|
||||||
updateInfo,
|
updateInfo,
|
||||||
setUpdateInfo,
|
setUpdateInfo,
|
||||||
@@ -50,11 +50,7 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!snapshotUpdate) return;
|
checkUpdateStatus();
|
||||||
|
|
||||||
checkUpdate().catch((error) => {
|
|
||||||
addError("Update failed:" + error, "error");
|
|
||||||
});
|
|
||||||
}, [snapshotUpdate]);
|
}, [snapshotUpdate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -79,13 +75,13 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
|||||||
const update = await checkUpdate();
|
const update = await checkUpdate();
|
||||||
|
|
||||||
if (update) {
|
if (update) {
|
||||||
|
const { skipVersions } = useUpdateStore.getState();
|
||||||
|
|
||||||
|
setVisible(!skipVersions.includes(update.version));
|
||||||
|
|
||||||
setUpdateInfo(update);
|
setUpdateInfo(update);
|
||||||
|
|
||||||
if (skipVersion === update.version) return;
|
|
||||||
|
|
||||||
setVisible(true);
|
|
||||||
}
|
}
|
||||||
}, [skipVersion]);
|
}, [skipVersions]);
|
||||||
|
|
||||||
const cursorClassName = useMemo(() => {
|
const cursorClassName = useMemo(() => {
|
||||||
return state.loading ? "cursor-not-allowed" : "cursor-pointer";
|
return state.loading ? "cursor-not-allowed" : "cursor-pointer";
|
||||||
@@ -133,7 +129,9 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
|||||||
const handleSkip = () => {
|
const handleSkip = () => {
|
||||||
if (state.loading) return;
|
if (state.loading) return;
|
||||||
|
|
||||||
setSkipVersion(updateInfo?.version);
|
const { skipVersions, updateInfo } = useUpdateStore.getState();
|
||||||
|
|
||||||
|
setSkipVersions([...skipVersions, updateInfo.version]);
|
||||||
|
|
||||||
isCheckPage ? hide_check() : setVisible(false);
|
isCheckPage ? hide_check() : setVisible(false);
|
||||||
};
|
};
|
||||||
@@ -182,7 +180,7 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
|||||||
<img src={isDark ? darkIcon : lightIcon} className="h-6" />
|
<img src={isDark ? darkIcon : lightIcon} className="h-6" />
|
||||||
|
|
||||||
<div className="text-[#333] text-sm leading-5 py-2 dark:text-[#D8D8D8] text-center">
|
<div className="text-[#333] text-sm leading-5 py-2 dark:text-[#D8D8D8] text-center">
|
||||||
{updateInfo?.available ? (
|
{updateInfo ? (
|
||||||
isOptional ? (
|
isOptional ? (
|
||||||
t("update.optional_description")
|
t("update.optional_description")
|
||||||
) : (
|
) : (
|
||||||
@@ -196,7 +194,7 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{updateInfo?.available ? (
|
{updateInfo ? (
|
||||||
<div
|
<div
|
||||||
className="text-xs text-[#0072FF] cursor-pointer"
|
className="text-xs text-[#0072FF] cursor-pointer"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@@ -223,21 +221,21 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
|||||||
cursorClassName,
|
cursorClassName,
|
||||||
state.loading && "opacity-50"
|
state.loading && "opacity-50"
|
||||||
)}
|
)}
|
||||||
onClick={updateInfo?.available ? handleDownload : handleSkip}
|
onClick={updateInfo ? handleDownload : handleSkip}
|
||||||
>
|
>
|
||||||
{state.loading ? (
|
{state.loading ? (
|
||||||
<div className="flex justify-center items-center gap-2">
|
<div className="flex justify-center items-center gap-2">
|
||||||
<LoaderCircle className="animate-spin size-5" />
|
<LoaderCircle className="animate-spin size-5" />
|
||||||
{percent}%
|
{percent}%
|
||||||
</div>
|
</div>
|
||||||
) : updateInfo?.available ? (
|
) : updateInfo ? (
|
||||||
t("update.button.install")
|
t("update.button.install")
|
||||||
) : (
|
) : (
|
||||||
t("update.button.ok")
|
t("update.button.ok")
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{updateInfo?.available && isOptional && (
|
{!isCheckPage && updateInfo && isOptional && (
|
||||||
<div
|
<div
|
||||||
className={clsx("text-xs text-[#999]", cursorClassName)}
|
className={clsx("text-xs text-[#999]", cursorClassName)}
|
||||||
onClick={handleSkip}
|
onClick={handleSkip}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { persist } from "zustand/middleware";
|
|||||||
export type IUpdateStore = {
|
export type IUpdateStore = {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
setVisible: (visible: boolean) => void;
|
setVisible: (visible: boolean) => void;
|
||||||
skipVersion?: string;
|
skipVersions: string[];
|
||||||
setSkipVersion: (skipVersion?: string) => void;
|
setSkipVersions: (skipVersions: string[]) => void;
|
||||||
isOptional: boolean;
|
isOptional: boolean;
|
||||||
setIsOptional: (isOptional: boolean) => void;
|
setIsOptional: (isOptional: boolean) => void;
|
||||||
updateInfo?: any;
|
updateInfo?: any;
|
||||||
@@ -19,8 +19,9 @@ export const useUpdateStore = create<IUpdateStore>()(
|
|||||||
setVisible: (visible: boolean) => {
|
setVisible: (visible: boolean) => {
|
||||||
return set({ visible });
|
return set({ visible });
|
||||||
},
|
},
|
||||||
setSkipVersion: (skipVersion?: string) => {
|
skipVersions: [],
|
||||||
return set({ skipVersion });
|
setSkipVersions: (skipVersions: string[]) => {
|
||||||
|
return set({ skipVersions });
|
||||||
},
|
},
|
||||||
isOptional: true,
|
isOptional: true,
|
||||||
setIsOptional: (isOptional: boolean) => {
|
setIsOptional: (isOptional: boolean) => {
|
||||||
@@ -33,7 +34,7 @@ export const useUpdateStore = create<IUpdateStore>()(
|
|||||||
{
|
{
|
||||||
name: "update-store",
|
name: "update-store",
|
||||||
partialize: (state) => ({
|
partialize: (state) => ({
|
||||||
skipVersion: state.skipVersion,
|
skipVersions: state.skipVersions,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user