mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-04-19 05:32:09 +00:00
Merge pull request #766 from Stremio/feat/addons-one-click-actions
Addons: make actions single click
This commit is contained in:
commit
b5d073bef9
5 changed files with 81 additions and 27 deletions
9
package-lock.json
generated
9
package-lock.json
generated
|
|
@ -12,7 +12,7 @@
|
|||
"@babel/runtime": "7.26.0",
|
||||
"@sentry/browser": "8.42.0",
|
||||
"@stremio/stremio-colors": "5.2.0",
|
||||
"@stremio/stremio-core-web": "0.48.3",
|
||||
"@stremio/stremio-core-web": "0.48.4",
|
||||
"@stremio/stremio-icons": "5.4.1",
|
||||
"@stremio/stremio-video": "0.0.48",
|
||||
"a-color-picker": "1.2.1",
|
||||
|
|
@ -3371,9 +3371,10 @@
|
|||
"integrity": "sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg=="
|
||||
},
|
||||
"node_modules/@stremio/stremio-core-web": {
|
||||
"version": "0.48.3",
|
||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.48.3.tgz",
|
||||
"integrity": "sha512-JL8pOLOEVACYG+33Dtp/mrB2/vuc7RoYZdxX1BQa5MPR8EzsODjpvL5uETmdxo/swgtMZyx2A6/e1B53eKA4oQ==",
|
||||
"version": "0.48.4",
|
||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.48.4.tgz",
|
||||
"integrity": "sha512-848OLm0dtP75aAlYhUB0KoOqwosJIj+ubB8/abuaAzH/N3dtxs40vu2AezmMpGjwR4V60rlOUkUZeWFvrUOjrw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.24.1"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
"@babel/runtime": "7.26.0",
|
||||
"@sentry/browser": "8.42.0",
|
||||
"@stremio/stremio-colors": "5.2.0",
|
||||
"@stremio/stremio-core-web": "0.48.3",
|
||||
"@stremio/stremio-core-web": "0.48.4",
|
||||
"@stremio/stremio-icons": "5.4.1",
|
||||
"@stremio/stremio-video": "0.0.48",
|
||||
"a-color-picker": "1.2.1",
|
||||
|
|
|
|||
|
|
@ -8,19 +8,43 @@ const { default: Icon } = require('@stremio/stremio-icons/react');
|
|||
const { Button, Image } = require('stremio/common');
|
||||
const styles = require('./styles');
|
||||
|
||||
const Addon = ({ className, id, name, version, logo, description, types, behaviorHints, installed, onToggle, onConfigure, onShare, dataset }) => {
|
||||
const Addon = ({ className, id, name, version, logo, description, types, behaviorHints, installed, onInstall, onUninstall, onConfigure, onOpen, onShare, dataset }) => {
|
||||
const { t } = useTranslation();
|
||||
const toggleButtonOnClick = React.useCallback((event) => {
|
||||
if (typeof onToggle === 'function') {
|
||||
onToggle({
|
||||
type: 'toggle',
|
||||
const onInstallClick = React.useCallback((event) => {
|
||||
event.stopPropagation();
|
||||
if (typeof onInstall === 'function') {
|
||||
onInstall({
|
||||
type: 'install',
|
||||
nativeEvent: event.nativeEvent,
|
||||
reactEvent: event,
|
||||
dataset: dataset
|
||||
});
|
||||
}
|
||||
}, [onToggle, dataset]);
|
||||
}, [onInstall, dataset]);
|
||||
const onUninstallClick = React.useCallback((event) => {
|
||||
event.stopPropagation();
|
||||
if (typeof onUninstall === 'function') {
|
||||
onUninstall({
|
||||
type: 'uninstall',
|
||||
nativeEvent: event.nativeEvent,
|
||||
reactEvent: event,
|
||||
dataset: dataset
|
||||
});
|
||||
}
|
||||
}, [onUninstall, dataset]);
|
||||
const onOpenClick = React.useCallback((event) => {
|
||||
event.stopPropagation();
|
||||
if (typeof onOpen === 'function') {
|
||||
onOpen({
|
||||
type: 'open',
|
||||
nativeEvent: event.nativeEvent,
|
||||
reactEvent: event,
|
||||
dataset: dataset
|
||||
});
|
||||
}
|
||||
}, [onOpen, dataset]);
|
||||
const configureButtonOnClick = React.useCallback((event) => {
|
||||
event.stopPropagation();
|
||||
if (typeof onConfigure === 'function') {
|
||||
onConfigure({
|
||||
type: 'configure',
|
||||
|
|
@ -31,6 +55,7 @@ const Addon = ({ className, id, name, version, logo, description, types, behavio
|
|||
}
|
||||
}, [onConfigure, dataset]);
|
||||
const shareButtonOnClick = React.useCallback((event) => {
|
||||
event.stopPropagation();
|
||||
if (typeof onShare === 'function') {
|
||||
onShare({
|
||||
type: 'share',
|
||||
|
|
@ -41,20 +66,15 @@ const Addon = ({ className, id, name, version, logo, description, types, behavio
|
|||
}
|
||||
}, [onShare, dataset]);
|
||||
const onKeyDown = React.useCallback((event) => {
|
||||
if (event.key === 'Enter' && typeof onToggle === 'function') {
|
||||
onToggle({
|
||||
type: 'toggle',
|
||||
nativeEvent: event.nativeEvent,
|
||||
reactEvent: event,
|
||||
dataset: dataset
|
||||
});
|
||||
if (event.key === 'Enter') {
|
||||
onOpenClick(event);
|
||||
}
|
||||
}, [onToggle, dataset]);
|
||||
}, [onOpenClick]);
|
||||
const renderLogoFallback = React.useCallback(() => (
|
||||
<Icon className={styles['icon']} name={'addons'} />
|
||||
), []);
|
||||
return (
|
||||
<Button className={classnames(className, styles['addon-container'])} onKeyDown={onKeyDown}>
|
||||
<Button className={classnames(className, styles['addon-container'])} onKeyDown={onKeyDown} onClick={onOpenClick}>
|
||||
<div className={styles['logo-container']}>
|
||||
<Image
|
||||
className={styles['logo']}
|
||||
|
|
@ -107,7 +127,7 @@ const Addon = ({ className, id, name, version, logo, description, types, behavio
|
|||
className={installed ? styles['uninstall-button-container'] : styles['install-button-container']}
|
||||
title={installed ? t('ADDON_UNINSTALL') : behaviorHints.configurationRequired ? t('ADDON_CONFIGURE') : t('ADDON_INSTALL')}
|
||||
tabIndex={-1}
|
||||
onClick={!installed && behaviorHints.configurationRequired ? configureButtonOnClick : toggleButtonOnClick}
|
||||
onClick={installed ? onUninstallClick : behaviorHints.configurationRequired ? configureButtonOnClick : onInstallClick}
|
||||
>
|
||||
<div className={styles['label']}>{installed ? t('ADDON_UNINSTALL') : behaviorHints.configurationRequired ? t('ADDON_CONFIGURE') : t('ADDON_INSTALL')}</div>
|
||||
</Button>
|
||||
|
|
@ -137,7 +157,10 @@ Addon.propTypes = {
|
|||
}),
|
||||
installed: PropTypes.bool,
|
||||
onToggle: PropTypes.func,
|
||||
onInstall: PropTypes.func,
|
||||
onUninstall: PropTypes.func,
|
||||
onConfigure: PropTypes.func,
|
||||
onOpen: PropTypes.func,
|
||||
onShare: PropTypes.func,
|
||||
dataset: PropTypes.object
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,9 +8,15 @@
|
|||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
padding: 1.5rem;
|
||||
border: 0.15rem solid transparent;
|
||||
border-radius: var(--border-radius);
|
||||
background-color: var(--overlay-color);
|
||||
cursor: inherit;
|
||||
transition: border-color 0.1s ease-out;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--overlay-color);
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
flex: none;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const classnames = require('classnames');
|
|||
const { useTranslation } = require('react-i18next');
|
||||
const { default: Icon } = require('@stremio/stremio-icons/react');
|
||||
const { AddonDetailsModal, Button, Image, Multiselect, MainNavBars, TextInput, SearchBar, SharePrompt, ModalDialog, usePlatform, useBinaryState, withCoreSuspender } = require('stremio/common');
|
||||
const { useServices } = require('stremio/services');
|
||||
const Addon = require('./Addon');
|
||||
const useInstalledAddons = require('./useInstalledAddons');
|
||||
const useRemoteAddons = require('./useRemoteAddons');
|
||||
|
|
@ -17,6 +18,7 @@ const { AddonPlaceholder } = require('./AddonPlaceholder');
|
|||
const Addons = ({ urlParams, queryParams }) => {
|
||||
const { t } = useTranslation();
|
||||
const platform = usePlatform();
|
||||
const { core } = useServices();
|
||||
const installedAddons = useInstalledAddons(urlParams);
|
||||
const remoteAddons = useRemoteAddons(urlParams);
|
||||
const [addonDetailsTransportUrl, setAddonDetailsTransportUrl] = useAddonDetailsTransportUrl(urlParams, queryParams);
|
||||
|
|
@ -57,12 +59,30 @@ const Addons = ({ urlParams, queryParams }) => {
|
|||
const onAddonShare = React.useCallback((event) => {
|
||||
setSharedAddon(event.dataset.addon);
|
||||
}, []);
|
||||
const onAddonToggle = React.useCallback((event) => {
|
||||
setAddonDetailsTransportUrl(event.dataset.addon.transportUrl);
|
||||
}, [setAddonDetailsTransportUrl]);
|
||||
const onAddonInstall = React.useCallback((event) => {
|
||||
core.transport.dispatch({
|
||||
action: 'Ctx',
|
||||
args: {
|
||||
action: 'InstallAddon',
|
||||
args: event.dataset.addon,
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
const onAddonUninstall = React.useCallback((event) => {
|
||||
core.transport.dispatch({
|
||||
action: 'Ctx',
|
||||
args: {
|
||||
action: 'UninstallAddon',
|
||||
args: event.dataset.addon,
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
const onAddonConfigure = React.useCallback((event) => {
|
||||
platform.openExternal(event.dataset.addon.transportUrl.replace('manifest.json', 'configure'));
|
||||
}, []);
|
||||
const onAddonOpen = React.useCallback((event) => {
|
||||
setAddonDetailsTransportUrl(event.dataset.addon.transportUrl);
|
||||
}, [setAddonDetailsTransportUrl]);
|
||||
const closeAddonDetails = React.useCallback(() => {
|
||||
setAddonDetailsTransportUrl(null);
|
||||
}, [setAddonDetailsTransportUrl]);
|
||||
|
|
@ -135,8 +155,10 @@ const Addons = ({ urlParams, queryParams }) => {
|
|||
types={addon.manifest.types}
|
||||
behaviorHints={addon.manifest.behaviorHints}
|
||||
installed={addon.installed}
|
||||
onToggle={onAddonToggle}
|
||||
onInstall={onAddonInstall}
|
||||
onUninstall={onAddonUninstall}
|
||||
onConfigure={onAddonConfigure}
|
||||
onOpen={onAddonOpen}
|
||||
onShare={onAddonShare}
|
||||
dataset={{ addon }}
|
||||
/>
|
||||
|
|
@ -173,8 +195,10 @@ const Addons = ({ urlParams, queryParams }) => {
|
|||
types={addon.manifest.types}
|
||||
behaviorHints={addon.manifest.behaviorHints}
|
||||
installed={addon.installed}
|
||||
onToggle={onAddonToggle}
|
||||
onInstall={onAddonInstall}
|
||||
onUninstall={onAddonUninstall}
|
||||
onConfigure={onAddonConfigure}
|
||||
onOpen={onAddonOpen}
|
||||
onShare={onAddonShare}
|
||||
dataset={{ addon }}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue