Skip to content

Commit

Permalink
Merge pull request #289 from Stremio/core-async-api
Browse files Browse the repository at this point in the history
Core async api
  • Loading branch information
nklhtv authored Sep 13, 2022
2 parents 19ba2a1 + 418782e commit a1c008e
Show file tree
Hide file tree
Showing 46 changed files with 842 additions and 620 deletions.
479 changes: 301 additions & 178 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@babel/runtime": "7.16.0",
"@sentry/browser": "6.13.3",
"@stremio/stremio-colors": "4.0.1",
"@stremio/stremio-core-web": "0.43.0",
"@stremio/stremio-core-web": "0.44.0",
"@stremio/stremio-icons": "3.0.5",
"@stremio/stremio-video": "0.0.20-rc.4",
"a-color-picker": "1.2.1",
Expand All @@ -25,13 +25,16 @@
"eventemitter3": "4.0.7",
"filter-invalid-dom-props": "2.1.0",
"lodash.debounce": "4.0.8",
"lodash.intersection": "4.4.0",
"lodash.isequal": "4.5.0",
"lodash.throttle": "4.1.1",
"prop-types": "15.7.2",
"react": "16.12.0",
"react-dom": "16.12.0",
"react-focus-lock": "2.2.1",
"spatial-navigation-polyfill": "git+https://[email protected]/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6"
"react": "18.2.0",
"react-dom": "18.2.0",
"react-is": "18.2.0",
"react-focus-lock": "2.9.1",
"spatial-navigation-polyfill": "git+https://[email protected]/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
"url": "0.11.0"
},
"devDependencies": {
"@babel/core": "7.16.0",
Expand Down
19 changes: 11 additions & 8 deletions src/App/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,21 @@ const React = require('react');
const { Router } = require('stremio-router');
const { Core, Shell, Chromecast, KeyboardShortcuts, ServicesProvider } = require('stremio/services');
const { NotFound } = require('stremio/routes');
const { ToastProvider, sanitizeLocationPath, CONSTANTS } = require('stremio/common');
const { ToastProvider, CONSTANTS } = require('stremio/common');
const CoreEventsToaster = require('./CoreEventsToaster');
const ErrorDialog = require('./ErrorDialog');
const routerViewsConfig = require('./routerViewsConfig');
const styles = require('./styles');

window.core_imports = {
app_version: process.env.VERSION,
shell_version: null,
sanitize_location_path: sanitizeLocationPath
};

const App = () => {
const onPathNotMatch = React.useCallback(() => {
return NotFound;
}, []);
const services = React.useMemo(() => ({
core: new Core(),
core: new Core({
appVersion: process.env.VERSION,
shellVersion: null
}),
shell: new Shell(),
chromecast: new Chromecast(),
keyboardShortcuts: new KeyboardShortcuts()
Expand Down Expand Up @@ -93,6 +90,12 @@ const App = () => {
action: 'PullAddonsFromAPI'
}
});
services.core.transport.dispatch({
action: 'Ctx',
args: {
action: 'PullUserFromAPI'
}
});
}
}, [initialized]);
return (
Expand Down
1 change: 1 addition & 0 deletions src/App/styles.less
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';

:global {
@import (once, less) '~stremio/common/animations.less';
@import (once, less) '~stremio-router/styles.css';
}

Expand Down
17 changes: 16 additions & 1 deletion src/common/AddonDetailsModal/AddonDetailsModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const React = require('react');
const PropTypes = require('prop-types');
const ModalDialog = require('stremio/common/ModalDialog');
const { withCoreSuspender } = require('stremio/common/CoreSuspender');
const { useServices } = require('stremio/services');
const AddonDetailsWithRemoteAndLocalAddon = withRemoteAndLocalAddon(require('./AddonDetails'));
const useAddonDetails = require('./useAddonDetails');
Expand Down Expand Up @@ -144,4 +145,18 @@ AddonDetailsModal.propTypes = {
onCloseRequest: PropTypes.func
};

module.exports = AddonDetailsModal;
const AddonDetailsModalFallback = ({ onCloseRequest }) => (
<ModalDialog
className={styles['addon-details-modal-container']}
title={'Stremio addon'}
onCloseRequest={onCloseRequest}
>
<div className={styles['addon-details-message-container']}>
Loading addon manifest
</div>
</ModalDialog>
);

AddonDetailsModalFallback.propTypes = AddonDetailsModal.propTypes;

module.exports = withCoreSuspender(AddonDetailsModal, AddonDetailsModalFallback);
8 changes: 1 addition & 7 deletions src/common/AddonDetailsModal/useAddonDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
const React = require('react');
const useModelState = require('stremio/common/useModelState');

const init = () => ({
selected: null,
localAddon: null,
remoteAddon: null
});

const useAddonDetails = (transportUrl) => {
const action = React.useMemo(() => {
if (typeof transportUrl === 'string') {
Expand All @@ -27,7 +21,7 @@ const useAddonDetails = (transportUrl) => {
};
}
}, [transportUrl]);
return useModelState({ model: 'addon_details', action, init });
return useModelState({ model: 'addon_details', action });
};

module.exports = useAddonDetails;
78 changes: 78 additions & 0 deletions src/common/CoreSuspender.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (C) 2017-2022 Smart code 203358507

const React = require('react');
const { useServices } = require('stremio/services');

const CoreSuspenderContext = React.createContext(null);

CoreSuspenderContext.displayName = 'CoreSuspenderContext';

function wrapPromise(promise) {
let status = 'pending';
let result;
const suspender = promise.then(
(resp) => {
status = 'success';
result = resp;
},
(error) => {
status = 'error';
result = error;
}
);
return {
read() {
if (status === 'pending') {
throw suspender;
} else if (status === 'error') {
throw result;
} else if (status === 'success') {
return result;
}
}
};
}

const useCoreSuspender = () => {
return React.useContext(CoreSuspenderContext);
};

const withCoreSuspender = (Component, Fallback = () => { }) => {
return function withCoreSuspender(props) {
const { core } = useServices();
const parentSuspender = useCoreSuspender();
const [render, setRender] = React.useState(parentSuspender === null);
const statesRef = React.useRef({});
const streamsRef = React.useRef({});
const getState = React.useCallback((model) => {
if (!statesRef.current[model]) {
statesRef.current[model] = wrapPromise(core.transport.getState(model));
}

return statesRef.current[model].read();
}, []);
const decodeStream = React.useCallback((stream) => {
if (!streamsRef.current[stream]) {
streamsRef.current[stream] = wrapPromise(core.transport.decodeStream(stream));
}

return streamsRef.current[stream].read();
}, []);
const suspender = React.useMemo(() => ({ getState, decodeStream }), []);
React.useLayoutEffect(() => {
if (!render) {
setRender(true);
}
}, []);
return render ?
<React.Suspense fallback={<Fallback {...props} />}>
<CoreSuspenderContext.Provider value={suspender}>
<Component {...props} />
</CoreSuspenderContext.Provider>
</React.Suspense>
:
null;
};
};

module.exports = { withCoreSuspender, useCoreSuspender };
23 changes: 23 additions & 0 deletions src/common/DelayedRenderer/DelayedRenderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (C) 2017-2022 Smart code 203358507

const React = require('react');
const PropTypes = require('prop-types');

const DelayedRenderer = ({ children, delay }) => {
const [render, setRender] = React.useState(false);
React.useEffect(() => {
const timeout = setTimeout(() => {
setRender(true);
}, delay);
return () => {
clearTimeout(timeout);
};
}, []);
return render ? children : null;
};

DelayedRenderer.propTypes = {
children: PropTypes.node
};

module.exports = DelayedRenderer;
5 changes: 5 additions & 0 deletions src/common/DelayedRenderer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (C) 2017-2022 Smart code 203358507

const DelayedRenderer = require('./DelayedRenderer');

module.exports = DelayedRenderer;
80 changes: 4 additions & 76 deletions src/common/NavBar/HorizontalNavBar/NavMenu/NavMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,14 @@
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const Icon = require('@stremio/stremio-icons/dom');
const { useRouteFocused } = require('stremio-router');
const { useServices } = require('stremio/services');
const Button = require('stremio/common/Button');
const Popup = require('stremio/common/Popup');
const useBinaryState = require('stremio/common/useBinaryState');
const useFullscreen = require('stremio/common/useFullscreen');
const useProfile = require('stremio/common/useProfile');
const styles = require('./styles');
const NavMenuContent = require('./NavMenuContent');

const NavMenu = (props) => {
const { core } = useServices();
const routeFocused = useRouteFocused();
const profile = useProfile();
const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false);
const [fullscreen, requestFullscreen, exitFullscreen] = useFullscreen();
const popupLabelOnClick = React.useCallback((event) => {
if (!event.nativeEvent.togglePopupPrevented) {
toggleMenu();
Expand All @@ -27,14 +19,6 @@ const NavMenu = (props) => {
const popupMenuOnClick = React.useCallback((event) => {
event.nativeEvent.togglePopupPrevented = true;
}, []);
const logoutButtonOnClick = React.useCallback(() => {
core.transport.dispatch({
action: 'Ctx',
args: {
action: 'Logout'
}
});
}, []);
const renderLabel = React.useMemo(() => ({ ref, className, children }) => (
props.renderLabel({
ref,
Expand All @@ -44,65 +28,8 @@ const NavMenu = (props) => {
})
), [menuOpen, popupLabelOnClick, props.renderLabel]);
const renderMenu = React.useCallback(() => (
<div className={styles['nav-menu-container']} onClick={popupMenuOnClick}>
<div className={styles['user-info-container']}>
<div
className={styles['avatar-container']}
style={{
backgroundImage: profile.auth === null ?
`url('${require('/images/anonymous.png')}')`
:
`url('${profile.auth.user.avatar}'), url('${require('/images/default_avatar.png')}')`
}}
/>
<div className={styles['email-container']}>
<div className={styles['email-label']}>{profile.auth === null ? 'Anonymous user' : profile.auth.user.email}</div>
</div>
<Button className={styles['logout-button-container']} title={profile.auth === null ? 'Log in / Sign up' : 'Log out'} href={'#/intro'} onClick={logoutButtonOnClick}>
<div className={styles['logout-label']}>{profile.auth === null ? 'Log in / Sign up' : 'Log out'}</div>
</Button>
</div>
<div className={styles['nav-menu-section']}>
<Button className={styles['nav-menu-option-container']} title={fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'} onClick={fullscreen ? exitFullscreen : requestFullscreen}>
<Icon className={styles['icon']} icon={fullscreen ? 'ic_exit_fullscreen' : 'ic_fullscreen'} />
<div className={styles['nav-menu-option-label']}>{fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'}</div>
</Button>
</div>
<div className={styles['nav-menu-section']}>
<Button className={styles['nav-menu-option-container']} title={'Settings'} href={'#/settings'}>
<Icon className={styles['icon']} icon={'ic_settings'} />
<div className={styles['nav-menu-option-label']}>Settings</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Addons'} href={'#/addons'}>
<Icon className={styles['icon']} icon={'ic_addons'} />
<div className={styles['nav-menu-option-label']}>Addons</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Remote Control'} disabled={true}>
<Icon className={styles['icon']} icon={'ic_remote'} />
<div className={styles['nav-menu-option-label']}>Remote Control</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Play Magnet Link'} disabled={true}>
<Icon className={styles['icon']} icon={'ic_magnet'} />
<div className={styles['nav-menu-option-label']}>Play Magnet Link</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Help & Feedback'} href={'https://stremio.zendesk.com/'} target={'_blank'}>
<Icon className={styles['icon']} icon={'ic_help'} />
<div className={styles['nav-menu-option-label']}>Help & Feedback</div>
</Button>
</div>
<div className={styles['nav-menu-section']}>
<Button className={styles['nav-menu-option-container']} title={'Terms of Service'} href={'https://www.stremio.com/tos'} target={'_blank'}>
<div className={styles['nav-menu-option-label']}>Terms of Service</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Privacy Policy'} href={'https://www.stremio.com/privacy'} target={'_blank'}>
<div className={styles['nav-menu-option-label']}>Privacy Policy</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'About Stremio'} href={'https://www.stremio.com/'} target={'_blank'}>
<div className={styles['nav-menu-option-label']}>About Stremio</div>
</Button>
</div>
</div>
), [profile, fullscreen]);
<NavMenuContent onClick={popupMenuOnClick} />
), []);
React.useEffect(() => {
if (!routeFocused) {
closeMenu();
Expand All @@ -111,6 +38,7 @@ const NavMenu = (props) => {
return (
<Popup
open={menuOpen}
direction={'bottom-left'}
onCloseRequest={closeMenu}
renderLabel={renderLabel}
renderMenu={renderMenu}
Expand Down
Loading

0 comments on commit a1c008e

Please sign in to comment.