diff --git a/package.json b/package.json index 4f1f16806..23fe229df 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@babel/runtime": "7.26.0", "@sentry/browser": "8.42.0", "@stremio/stremio-colors": "5.2.0", - "@stremio/stremio-core-web": "0.55.0", + "@stremio/stremio-core-web": "https://stremio.github.io/stremio-core/stremio-core-web/feat/upgrade-user-addons/stremio-stremio-core-web-0.55.0.tgz", "@stremio/stremio-icons": "5.8.0", "@stremio/stremio-video": "0.0.70", "a-color-picker": "1.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 321a4110b..bf8d3c6e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,8 +18,8 @@ importers: specifier: 5.2.0 version: 5.2.0 '@stremio/stremio-core-web': - specifier: 0.55.0 - version: 0.55.0 + specifier: https://stremio.github.io/stremio-core/stremio-core-web/feat/upgrade-user-addons/stremio-stremio-core-web-0.55.0.tgz + version: https://stremio.github.io/stremio-core/stremio-core-web/feat/upgrade-user-addons/stremio-stremio-core-web-0.55.0.tgz '@stremio/stremio-icons': specifier: 5.8.0 version: 5.8.0 @@ -1120,8 +1120,9 @@ packages: '@stremio/stremio-colors@5.2.0': resolution: {integrity: sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg==} - '@stremio/stremio-core-web@0.55.0': - resolution: {integrity: sha512-MdalnThEwnA8osQh+3/5OMzVIYZOoYmd94dN3nmCeT4rfV7IZXRFUg/uyCY+5bqigStlE3SfKEaGiSc6UnNtlQ==} + '@stremio/stremio-core-web@https://stremio.github.io/stremio-core/stremio-core-web/feat/upgrade-user-addons/stremio-stremio-core-web-0.55.0.tgz': + resolution: {tarball: https://stremio.github.io/stremio-core/stremio-core-web/feat/upgrade-user-addons/stremio-stremio-core-web-0.55.0.tgz} + version: 0.55.0 '@stremio/stremio-icons@5.8.0': resolution: {integrity: sha512-IVUvQbIWfA4YEHCTed7v/sdQJCJ+OOCf84LTWpkE2W6GLQ+15WHcMEJrVkE1X3ekYJnGg3GjT0KLO6tKSU0P4w==} @@ -5870,7 +5871,7 @@ snapshots: '@stremio/stremio-colors@5.2.0': {} - '@stremio/stremio-core-web@0.55.0': + '@stremio/stremio-core-web@https://stremio.github.io/stremio-core/stremio-core-web/feat/upgrade-user-addons/stremio-stremio-core-web-0.55.0.tgz': dependencies: '@babel/runtime': 7.24.1 diff --git a/src/App/ServicesToaster.js b/src/App/ServicesToaster.js index a12757169..6e6b22789 100644 --- a/src/App/ServicesToaster.js +++ b/src/App/ServicesToaster.js @@ -1,15 +1,40 @@ // Copyright (C) 2017-2023 Smart code 203358507 const React = require('react'); +const { useTranslation } = require('react-i18next'); const { useServices } = require('stremio/services'); const { useToast } = require('stremio/common'); const ServicesToaster = () => { + const { t } = useTranslation(); const { core, dragAndDrop } = useServices(); const toast = useToast(); React.useEffect(() => { const onCoreEvent = ({ event, args }) => { switch (event) { + case 'UserAddonsUpgraded': { + const upgradedCount = args?.upgraded?.length ?? 0; + toast.show({ + type: 'success', + title: upgradedCount > 0 + ? t('ADDONS_CHECK_FOR_UPDATES_COUNT', { + count: upgradedCount, + }) + : t('ADDONS_CHECK_FOR_UPDATES_UP_TO_DATE'), + timeout: 5000, + dataset: { type: 'CoreEvent' } + }); + break; + } + case 'AddonUpgraded': { + toast.show({ + type: 'success', + title: t('ADDON_UPDATED'), + timeout: 4000, + dataset: { type: 'CoreEvent' } + }); + break; + } case 'Error': { if (args.source.event === 'UserPulledFromAPI' && args.source.args.uid === null) { break; @@ -23,6 +48,16 @@ const ServicesToaster = () => { break; } + if (args.error.type === 'Other' && args.error.code === 3 && args.source.event === 'AddonUpgraded') { + toast.show({ + type: 'success', + title: t('ADDON_UP_TO_DATE'), + timeout: 4000, + dataset: { type: 'CoreEvent' } + }); + break; + } + toast.show({ type: 'error', title: args.source.event, @@ -74,7 +109,7 @@ const ServicesToaster = () => { core.transport.off('CoreEvent', onCoreEvent); dragAndDrop.off('error', onDragAndDropError); }; - }, []); + }, [t]); return null; }; diff --git a/src/components/AddonDetailsModal/AddonDetailsModal.js b/src/components/AddonDetailsModal/AddonDetailsModal.js index a89a68b77..dafd88525 100644 --- a/src/components/AddonDetailsModal/AddonDetailsModal.js +++ b/src/components/AddonDetailsModal/AddonDetailsModal.js @@ -85,6 +85,28 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => { } : null; + const updateButton = addonDetails.localAddon !== null && + addonDetails.remoteAddon !== null && + addonDetails.remoteAddon.content.type === 'Ready' && + !addonDetails.remoteAddon.content.content.flags.protected && + !addonDetails.remoteAddon.content.content.manifest.behaviorHints.configurationRequired ? + { + className: styles['update-button'], + label: t('ADDON_REFRESH'), + props: { + onClick: () => { + core.transport.dispatch({ + action: 'Ctx', + args: { + action: 'UpgradeAddon', + args: addonDetails.remoteAddon.content.content + } + }); + } + } + } + : + null; const toggleButton = addonDetails.localAddon !== null ? { className: styles['uninstall-button'], @@ -137,7 +159,7 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => { } : null; - return configureButton && toggleButton ? [cancelButton, configureButton, toggleButton] : configureButton ? [cancelButton, configureButton] : toggleButton ? [cancelButton, toggleButton] : [cancelButton]; + return [cancelButton, configureButton, updateButton, toggleButton].filter(Boolean); }, [addonDetails, onCloseRequest]); const modalBackground = React.useMemo(() => { return addonDetails.remoteAddon?.content.type === 'Ready' ? addonDetails.remoteAddon.content.content.manifest.background : null; diff --git a/src/components/AddonDetailsModal/styles.less b/src/components/AddonDetailsModal/styles.less index 7182d0528..6ee200194 100644 --- a/src/components/AddonDetailsModal/styles.less +++ b/src/components/AddonDetailsModal/styles.less @@ -29,7 +29,7 @@ .uninstall-button { background-color: var(--overlay-color); - + &:hover { outline: var(--focus-outline-size) solid var(--overlay-color); background-color: transparent; @@ -39,4 +39,17 @@ outline-color: var(--primary-foreground-color); } } + + .update-button { + background-color: var(--secondary-accent-color); + + &:hover { + outline: var(--focus-outline-size) solid var(--secondary-accent-color); + background-color: transparent; + } + + &:focus { + outline-color: var(--primary-foreground-color); + } + } } \ No newline at end of file diff --git a/src/routes/Addons/Addons.js b/src/routes/Addons/Addons.js index 86a45374c..b5fc0ccf4 100644 --- a/src/routes/Addons/Addons.js +++ b/src/routes/Addons/Addons.js @@ -93,6 +93,23 @@ const Addons = ({ urlParams, queryParams }) => { const onAddonConfigure = React.useCallback((event) => { platform.openExternal(event.dataset.addon.transportUrl.replace('manifest.json', 'configure')); }, []); + const hasUpdatableInstalledAddons = React.useMemo(() => { + if (installedAddons.selected === null) { + return false; + } + return installedAddons.catalog.some((addon) => + !addon.manifest?.behaviorHints?.configurationRequired && !addon.flags?.protected + ); + }, [installedAddons.selected, installedAddons.catalog]); + const onUpdateAllAddons = React.useCallback(() => { + if (!hasUpdatableInstalledAddons) { + return; + } + core.transport.dispatch({ + action: 'Ctx', + args: { action: 'UpgradeUserAddons' } + }); + }, [hasUpdatableInstalledAddons]); const onAddonOpen = React.useCallback((event) => { setAddonDetailsTransportUrl(event.dataset.addon.transportUrl); }, [setAddonDetailsTransportUrl]); @@ -130,12 +147,37 @@ const Addons = ({ urlParams, queryParams }) => {
{t('ADD_ADDON')}
+ { + hasUpdatableInstalledAddons ? + + : + null + } + { + hasUpdatableInstalledAddons ? + + : + null + } diff --git a/src/routes/Addons/styles.less b/src/routes/Addons/styles.less index 565c0b262..5d634ee89 100644 --- a/src/routes/Addons/styles.less +++ b/src/routes/Addons/styles.less @@ -48,7 +48,7 @@ overflow: visible; z-index: 2; - .add-button-container { + .add-button-container, .update-all-button-container { flex: none; display: flex; flex-direction: row; @@ -78,7 +78,7 @@ color: var(--primary-foreground-color); } - .add-button-label { + .add-button-label, .update-all-button-label { flex-grow: 0; flex-shrink: 1; flex-basis: auto; @@ -128,6 +128,25 @@ color: var(--primary-foreground-color); } } + + .mobile-update-all-button { + flex: none; + display: none; + align-items: center; + justify-content: center; + width: 3rem; + height: 3rem; + margin-right: 1rem; + border-radius: var(--border-radius); + background-color: var(--secondary-accent-color); + + .mobile-update-all-icon { + flex: none; + width: 1.4rem; + height: 1.4rem; + color: var(--primary-foreground-color); + } + } } .message-container { @@ -306,6 +325,14 @@ margin-right: 1rem; } + .update-all-button-container { + display: none; + } + + .mobile-update-all-button { + display: flex; + } + .filter-button { display: flex; }