Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c3f7ee1
refactor(Addon): migrate Addon component to TypeScript and enhance fu…
mrcanelas Apr 3, 2026
ca406e6
feat(Addons): add update functionality for addons with fresh manifest
mrcanelas Apr 3, 2026
ba362f3
feat(Addons): implement updateAddonWithFreshManifest function to fetc…
mrcanelas Apr 3, 2026
b501eb0
style(Addons): update button styles for configure and add update butt…
mrcanelas Apr 3, 2026
9ab06a9
feat(ServicesToaster): add success notifications for addon update and…
mrcanelas Apr 3, 2026
cba7dc6
refactor(Image): make fallbackSrc, renderFallback, and onError props …
mrcanelas Apr 3, 2026
f28066a
refactor(Addon): revert migration Addon component to TypeScript
mrcanelas Apr 3, 2026
db22314
refactor(Addons): change import statement for Addon
mrcanelas Apr 3, 2026
1dd60fd
refactor(Image): revert make fallbackSrc, renderFallback, and onError…
mrcanelas Apr 3, 2026
e1b4152
refactor(Addon): revert changes in addon component and styles
mrcanelas Apr 5, 2026
16676db
feat(Addons): implement batch addon update functionality with event h…
mrcanelas Apr 5, 2026
68e6517
feat(Addons): enhance bulk update functionality and integrate event h…
mrcanelas Apr 5, 2026
32b0111
feat(Addons): improve error handling and messaging for bulk addon upd…
mrcanelas Apr 6, 2026
0b3be63
feat(Addons): add mobile update all button for installed addons
mrcanelas Apr 6, 2026
2673237
refactor: support logic in core
kKaskak Apr 8, 2026
85198dd
refactor: simplify handing
kKaskak Apr 8, 2026
1a25745
chore: use dev package for testing
kKaskak Apr 8, 2026
4f995ec
refactor: remove defaultValue from translation keys
mrcanelas Apr 8, 2026
fa0d000
refactor: update translation keys for addon update notifications
mrcanelas Apr 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
11 changes: 6 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 36 additions & 1 deletion src/App/ServicesToaster.js
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -74,7 +109,7 @@ const ServicesToaster = () => {
core.transport.off('CoreEvent', onCoreEvent);
dragAndDrop.off('error', onDragAndDropError);
};
}, []);
}, [t]);
return null;
};

Expand Down
24 changes: 23 additions & 1 deletion src/components/AddonDetailsModal/AddonDetailsModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 14 additions & 1 deletion src/components/AddonDetailsModal/styles.less
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

.uninstall-button {
background-color: var(--overlay-color);

&:hover {
outline: var(--focus-outline-size) solid var(--overlay-color);
background-color: transparent;
Expand All @@ -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);
}
}
}
42 changes: 42 additions & 0 deletions src/routes/Addons/Addons.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Comment on lines +96 to +103
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way we return true only if any of the addons needs to be updated to newer version? Right now we will have this as true for any unprotected addon that doesn't need configuration, even if there is no newer version?

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]);
Expand Down Expand Up @@ -130,12 +147,37 @@ const Addons = ({ urlParams, queryParams }) => {
<Icon className={styles['icon']} name={'add'} />
<div className={styles['add-button-label']}>{t('ADD_ADDON')}</div>
</Button>
{
hasUpdatableInstalledAddons ?
<Button
className={styles['update-all-button-container']}
title={t('ADDONS_CHECK_FOR_UPDATES')}
onClick={onUpdateAllAddons}
>
<Icon className={styles['icon']} name={'reset'} />
<div className={styles['update-all-button-label']}>{t('ADDONS_CHECK_FOR_UPDATES')}</div>
</Button>
:
null
}
<SearchBar
className={styles['search-bar']}
title={t('ADDON_SEARCH')}
value={search}
onChange={searchInputOnChange}
/>
{
hasUpdatableInstalledAddons ?
<Button
className={styles['mobile-update-all-button']}
title={t('ADDONS_CHECK_FOR_UPDATES')}
onClick={onUpdateAllAddons}
>
<Icon className={styles['mobile-update-all-icon']} name={'reset'} />
</Button>
:
null
}
<Button className={styles['filter-button']} title={t('ALL_FILTERS')} onClick={openFiltersModal}>
<Icon className={styles['filter-icon']} name={'filters'} />
</Button>
Expand Down
31 changes: 29 additions & 2 deletions src/routes/Addons/styles.less
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -306,6 +325,14 @@
margin-right: 1rem;
}

.update-all-button-container {
display: none;
}

.mobile-update-all-button {
display: flex;
}

.filter-button {
display: flex;
}
Expand Down