@v4fire/client
Version:
V4Fire client core library
259 lines (207 loc) • 5.43 kB
text/typescript
/*!
* V4Fire Client Core
* https://github.com/V4Fire/Client
*
* Released under the MIT license
* https://github.com/V4Fire/Client/blob/master/LICENSE
*/
/**
* [[include:super/i-block/modules/activation/README.md]]
* @packageDocumentation
*/
import symbolGenerator from 'core/symbol';
import { unwrap } from 'core/object/watch';
import { runHook, callMethodFromComponent } from 'core/component';
import type iBlock from 'super/i-block/i-block';
import { statuses } from 'super/i-block/const';
import {
suspendRgxp,
readyStatuses,
inactiveStatuses,
asyncNames,
nonMuteAsyncLinkNames
} from 'super/i-block/modules/activation/const';
export * from 'super/i-block/modules/activation/const';
export const
$$ = symbolGenerator();
/**
* Activates the component.
* The deactivated component won't load data from providers on initializing.
*
* Basically, you don't need to think about a component activation,
* because it's automatically synchronized with `keep-alive` or the special input property.
*
* @param component
* @param [force] - if true, then the component will be forced to activate, even if it is already activated
*/
export function activate(component: iBlock, force?: boolean): void {
const {
unsafe,
unsafe: {r, lfc, state, rootEmitter}
} = component;
const
isBeforeCreate = lfc.isBeforeCreate(),
canActivate = !unsafe.isActivated || force;
if (canActivate) {
if (isBeforeCreate) {
state.initFromRouter();
}
if (state.needRouterSync) {
void lfc.execCbAfterComponentCreated(() => {
rootEmitter.on('onTransition', handler, {
label: $$.activate
});
async function handler(route: typeof r.route, type: string): Promise<void> {
try {
if (type === 'hard') {
const
actualRoute = unwrap(r.route) ?? r.route;
if (route !== actualRoute) {
await unsafe.promisifyOnce('setRoute', {
label: $$.activateAfterTransition
});
} else {
await unsafe.nextTick({
label: $$.activateAfterHardChange
});
}
}
if (!inactiveStatuses[unsafe.componentStatus]) {
state.initFromRouter();
}
} catch (err) {
stderr(err);
}
}
});
}
}
if (isBeforeCreate) {
return;
}
if (canActivate) {
onActivated(component, true);
runHook('activated', component).then(() => {
callMethodFromComponent(component, 'activated');
}).catch(stderr);
}
const
children = unsafe.$children;
if (children != null) {
for (let i = 0; i < children.length; i++) {
children[i].unsafe.activate(true);
}
}
}
/**
* Deactivates the component.
* The deactivated component won't load data from providers on initializing.
*
* Basically, you don't need to think about a component activation,
* because it's automatically synchronized with keep-alive or the special input property.
*
* @param component
*/
export function deactivate(component: iBlock): void {
const
{unsafe} = component;
if (unsafe.lfc.isBeforeCreate()) {
return;
}
if (unsafe.isActivated) {
onDeactivated(component);
runHook('deactivated', component).then(() => {
callMethodFromComponent(component, 'deactivated');
}).catch(stderr);
}
const
children = unsafe.$children;
if (children != null) {
for (let i = 0; i < children.length; i++) {
children[i].unsafe.deactivate();
}
}
}
/**
* Hook handler: the component has been activated
*
* @param component
* @param [force] - if true, then the component will be forced to activate, even if it is already activated
*/
export function onActivated(component: iBlock, force?: boolean): void {
const
{unsafe} = component;
const cantActivate =
unsafe.isActivated ||
!force && !unsafe.activatedProp && !unsafe.isReadyOnce;
if (cantActivate) {
return;
}
const async = [
unsafe.$async,
unsafe.async
];
for (let i = 0; i < async.length; i++) {
const $a = async[i];
$a.unmuteAll().unsuspendAll();
}
if (unsafe.isReadyOnce && !readyStatuses[unsafe.componentStatus]) {
unsafe.componentStatus = 'beforeReady';
}
const needInitLoadOrReload =
!unsafe.isReadyOnce &&
force || unsafe.reloadOnActivation;
if (needInitLoadOrReload) {
const
group = {group: 'requestSync:get'};
for (let i = 0; i < async.length; i++) {
const $a = async[i];
$a.clearAll(group).setImmediate(load, group);
}
}
if (unsafe.isReadyOnce) {
unsafe.componentStatus = 'ready';
}
unsafe.state.initFromRouter();
unsafe.isActivated = true;
function load(): void {
const
res = unsafe.isReadyOnce ? unsafe.reload() : unsafe.initLoad();
if (Object.isPromise(res)) {
res.catch(stderr);
}
}
}
/**
* Hook handler: the component has been deactivated
* @param component
*/
export function onDeactivated(component: iBlock): void {
const
{unsafe} = component;
const async = [
unsafe.$async,
unsafe.async
];
for (let i = 0; i < async.length; i++) {
const
$a = async[i];
for (let keys = Object.keys(asyncNames), i = 0; i < keys.length; i++) {
const
key = keys[i];
if (nonMuteAsyncLinkNames[key]) {
continue;
}
const
fn = $a[`mute-${asyncNames[key]}`.camelize(false)];
if (Object.isFunction(fn)) {
fn.call($a);
}
}
$a.unmuteAll({group: suspendRgxp}).suspendAll();
}
if (statuses[component.componentStatus] >= 2) {
component.componentStatus = 'inactive';
}
component.isActivated = false;
}