refactor: add typed event bus

Signed-off-by: Varun Patil <radialapps@gmail.com>
pull/803/head
Varun Patil 2023-08-28 16:14:50 -07:00
parent 9b12b2ab41
commit 41a255aaa3
21 changed files with 156 additions and 100 deletions

View File

@ -71,7 +71,6 @@ const NcAppNavigationItem = () => import('@nextcloud/vue/dist/Components/NcAppNa
import { generateUrl } from '@nextcloud/router';
import { translate as t } from '@nextcloud/l10n';
import { emit, subscribe } from '@nextcloud/event-bus';
import * as utils from './services/utils';
import * as nativex from './native';
@ -227,14 +226,14 @@ export default defineComponent({
const onResize = () => {
globalThis.windowInnerWidth = window.innerWidth;
globalThis.windowInnerHeight = window.innerHeight;
emit('memories:window:resize', {});
utils.bus.emit('memories:window:resize', null);
};
window.addEventListener('resize', () => {
utils.setRenewingTimeout(this, 'resizeTimer', onResize, 100);
});
// Register navigation items on config change
subscribe(this.configEventName, this.refreshNav);
utils.bus.on('memories:user-config-changed', this.refreshNav);
// Register global functions
globalThis.showSettings = () => this.showSettings();
@ -287,7 +286,7 @@ export default defineComponent({
// Close navigation by default if init is disabled
// This is the case for public folder/album shares
if (this.$route.query.noinit) {
emit('toggle-navigation', { open: false });
utils.bus.emit('toggle-navigation', { open: false });
}
},
@ -400,7 +399,7 @@ export default defineComponent({
linkClick() {
if (globalThis.windowInnerWidth <= 1024) {
emit('toggle-navigation', { open: false });
utils.bus.emit('toggle-navigation', { open: false });
}
},

View File

@ -19,8 +19,6 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { subscribe, unsubscribe } from '@nextcloud/event-bus';
import UserConfig from '../mixins/UserConfig';
import TopMatter from './top-matter/TopMatter.vue';
import ClusterGrid from './ClusterGrid.vue';
@ -29,6 +27,7 @@ import EmptyContent from './top-matter/EmptyContent.vue';
import DynamicTopMatter from './top-matter/DynamicTopMatter.vue';
import * as dav from '../services/dav';
import * as utils from '../services/utils';
import type { ICluster } from '../types';
@ -69,11 +68,11 @@ export default defineComponent({
},
created() {
subscribe(this.configEventName, this.routeChange);
utils.bus.on('memories:user-config-changed', this.routeChange);
},
beforeDestroy() {
unsubscribe(this.configEventName, this.routeChange);
utils.bus.off('memories:user-config-changed', this.routeChange);
},
watch: {

View File

@ -73,7 +73,6 @@ import NcActions from '@nextcloud/vue/dist/Components/NcActions';
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton';
import axios from '@nextcloud/axios';
import { subscribe, unsubscribe } from '@nextcloud/event-bus';
import { getCanonicalLocale } from '@nextcloud/l10n';
import { DateTime } from 'luxon';
@ -126,13 +125,13 @@ export default defineComponent({
}),
mounted() {
subscribe('files:file:updated', this.handleFileUpdated);
subscribe('memories:albums:update', this.refresh);
utils.bus.on('files:file:updated', this.handleFileUpdated);
utils.bus.on('memories:albums:update', this.refresh);
},
beforeDestroy() {
unsubscribe('files:file:updated', this.handleFileUpdated);
unsubscribe('memories:albums:update', this.refresh);
utils.bus.off('files:file:updated', this.handleFileUpdated);
utils.bus.off('memories:albums:update', this.refresh);
},
computed: {

View File

@ -16,10 +16,11 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { subscribe, unsubscribe } from '@nextcloud/event-bus';
import { generateUrl } from '@nextcloud/router';
import nextcloudsvg from '../assets/nextcloud.svg';
import * as utils from '../services/utils';
export default defineComponent({
name: 'MobileHeader',
@ -35,11 +36,11 @@ export default defineComponent({
},
mounted() {
subscribe('memories.recycler.scroll', this.onScroll);
utils.bus.on('memories.recycler.scroll', this.onScroll);
},
beforeDestroy() {
unsubscribe('memories.recycler.scroll', this.onScroll);
utils.bus.off('memories.recycler.scroll', this.onScroll);
},
methods: {

View File

@ -50,7 +50,6 @@
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { IRow, IRowType, ITick } from '../types';
import { emit } from '@nextcloud/event-bus';
import ScrollUpIcon from 'vue-material-design-icons/MenuUp.vue';
import ScrollDownIcon from 'vue-material-design-icons/MenuDown.vue';
@ -209,7 +208,7 @@ export default defineComponent({
const scroll = this.recycler?.$el?.scrollTop || 0;
// Emit scroll event
emit('memories.recycler.scroll', {
utils.bus.emit('memories.recycler.scroll', {
current: scroll,
previous: this.lastKnownRecyclerScroll,
dynTopMatterVisible: scroll < this.dynTopMatterHeight,

View File

@ -49,7 +49,6 @@ import NcActions from '@nextcloud/vue/dist/Components/NcActions';
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton';
import { translate as t, translatePlural as n } from '@nextcloud/l10n';
import { subscribe, unsubscribe } from '@nextcloud/event-bus';
import * as dav from '../services/dav';
import * as utils from '../services/utils';
@ -277,14 +276,14 @@ export default defineComponent({
}
// Subscribe to global events
subscribe('memories:albums:update', this.clearSelection);
utils.bus.on('memories:albums:update', this.clearSelection);
},
beforeDestroy() {
this.setHasTopBar(false);
// Unsubscribe from global events
unsubscribe('memories:albums:update', this.clearSelection);
utils.bus.off('memories:albums:update', this.clearSelection);
},
watch: {
@ -295,11 +294,11 @@ export default defineComponent({
methods: {
refresh() {
this.$emit('refresh');
utils.bus.emit('memories:timeline:soft-refresh', null);
},
deletePhotos(photos: IPhoto[]) {
this.$emit('delete', photos);
utils.bus.emit('memories:timeline:deleted', photos);
},
deleteSelectedPhotosById(delIds: number[], selection: Selection) {

View File

@ -17,7 +17,6 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { subscribe, unsubscribe, emit } from '@nextcloud/event-bus';
import NcActions from '@nextcloud/vue/dist/Components/NcActions';
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton';
@ -25,6 +24,8 @@ import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton';
import Metadata from './Metadata.vue';
import { IImageInfo, IPhoto } from '../types';
import * as utils from '../services/utils';
import CloseIcon from 'vue-material-design-icons/Close.vue';
export default defineComponent({
@ -73,8 +74,8 @@ export default defineComponent({
},
mounted() {
subscribe('files:sidebar:opened', this.handleNativeOpen);
subscribe('files:sidebar:closed', this.handleNativeClose);
utils.bus.on('files:sidebar:opened', this.handleNativeOpen);
utils.bus.on('files:sidebar:closed', this.handleNativeClose);
globalThis.mSidebar = {
open: this.open.bind(this),
@ -85,8 +86,8 @@ export default defineComponent({
},
beforeDestroy() {
unsubscribe('files:sidebar:opened', this.handleNativeOpen);
unsubscribe('files:sidebar:closed', this.handleNativeClose);
utils.bus.off('files:sidebar:opened', this.handleNativeOpen);
utils.bus.off('files:sidebar:closed', this.handleNativeClose);
},
methods: {
@ -132,7 +133,7 @@ export default defineComponent({
},
handleClose() {
emit('memories:sidebar:closed', {});
utils.bus.emit('memories:sidebar:closed', null);
},
handleOpen() {
@ -142,7 +143,7 @@ export default defineComponent({
if (e.key.length === 1) e.stopPropagation();
});
emit('memories:sidebar:opened', {});
utils.bus.emit('memories:sidebar:opened', null);
},
handleNativeOpen() {

View File

@ -31,9 +31,10 @@
import { defineComponent } from 'vue';
import Timeline from './Timeline.vue';
const MapSplitMatter = () => import('./top-matter/MapSplitMatter.vue');
import { emit } from '@nextcloud/event-bus';
import Hammer from 'hammerjs';
import * as utils from '../services/utils';
export default defineComponent({
name: 'SplitTimeline',
@ -133,7 +134,7 @@ export default defineComponent({
this.pointerDown = false;
document.removeEventListener('pointermove', this.documentPointerMove);
document.removeEventListener('pointerup', this.pointerUp);
emit('memories:window:resize', {});
utils.bus.emit('memories:window:resize', null);
},
setFlexBasis(pos: { clientX: number; clientY: number }) {
@ -154,7 +155,7 @@ export default defineComponent({
// so that we can prepare in advance for showing more photos
// on the timeline
await this.$nextTick();
emit('memories:window:resize', {});
utils.bus.emit('memories:window:resize', null);
},
async mobileSwipeDown() {
@ -165,7 +166,7 @@ export default defineComponent({
// ends. Note that this is necesary: the height of the timeline inner
// div is also animated to the smaller size.
await new Promise((resolve) => setTimeout(resolve, 300));
emit('memories:window:resize', {});
utils.bus.emit('memories:window:resize', null);
},
},
});

View File

@ -81,8 +81,6 @@
:rows="list"
:isreverse="isMonthView"
:recycler="$refs.recycler?.$el"
@refresh="softRefresh"
@delete="deleteFromViewWithAnimation"
@updateLoading="updateLoading"
/>
</div>
@ -94,7 +92,6 @@ import type { Route } from 'vue-router';
import axios from '@nextcloud/axios';
import { showError } from '@nextcloud/dialogs';
import { subscribe, unsubscribe } from '@nextcloud/event-bus';
import { getLayout } from '../services/layout';
import { IDay, IHeadRow, IPhoto, IRow, IRowType } from '../types';
@ -189,21 +186,23 @@ export default defineComponent({
},
created() {
subscribe(this.configEventName, this.softRefresh);
subscribe('files:file:created', this.softRefresh);
subscribe('memories:window:resize', this.handleResizeWithDelay);
subscribe('memories:viewer:deleted', this.deleteFromViewWithAnimation);
subscribe('memories:viewer:fetch-day', this.fetchDay);
subscribe('memories:timeline:hard-refresh', this.refresh);
utils.bus.on('memories:user-config-changed', this.softRefresh);
utils.bus.on('files:file:created', this.softRefresh);
utils.bus.on('memories:window:resize', this.handleResizeWithDelay);
utils.bus.on('memories:viewer:fetch-day', this.fetchDay);
utils.bus.on('memories:timeline:deleted', this.deleteFromViewWithAnimation);
utils.bus.on('memories:timeline:soft-refresh', this.softRefresh);
utils.bus.on('memories:timeline:hard-refresh', this.refresh);
},
beforeDestroy() {
unsubscribe(this.configEventName, this.softRefresh);
unsubscribe('files:file:created', this.softRefresh);
unsubscribe('memories:window:resize', this.handleResizeWithDelay);
unsubscribe('memories:viewer:deleted', this.deleteFromViewWithAnimation);
unsubscribe('memories:viewer:fetch-day', this.fetchDay);
unsubscribe('memories:timeline:hard-refresh', this.refresh);
utils.bus.off('memories:user-config-changed', this.softRefresh);
utils.bus.off('files:file:created', this.softRefresh);
utils.bus.off('memories:window:resize', this.handleResizeWithDelay);
utils.bus.off('memories:viewer:fetch-day', this.fetchDay);
utils.bus.off('memories:timeline:deleted', this.deleteFromViewWithAnimation);
utils.bus.off('memories:timeline:soft-refresh', this.softRefresh);
utils.bus.off('memories:timeline:hard-refresh', this.refresh);
this.resetState();
this.state = 0;
},

View File

@ -18,9 +18,9 @@
import { defineComponent } from 'vue';
import * as dav from '../../services/dav';
import * as utils from '../../services/utils';
import { showInfo } from '@nextcloud/dialogs';
import { emit } from '@nextcloud/event-bus';
import { IAlbum, IPhoto } from '../../types';
@ -110,7 +110,7 @@ export default defineComponent({
// emit only the successfully processed photos here
// so that only these are deselected by the manager
const processedPhotos = this.photos.filter((p) => processedIds.has(p.fileid));
emit('memories:albums:update', processedPhotos);
utils.bus.emit('memories:albums:update', processedPhotos);
// close the modal only if all ops are successful
if (opsSuccess === this.opsTotal) {

View File

@ -63,10 +63,10 @@ import EditExif from './EditExif.vue';
import EditLocation from './EditLocation.vue';
import { showError } from '@nextcloud/dialogs';
import { emit } from '@nextcloud/event-bus';
import axios from '@nextcloud/axios';
import * as dav from '../../services/dav';
import * as utils from '../../services/utils';
import { API } from '../../services/API';
export default defineComponent({
@ -209,7 +209,7 @@ export default defineComponent({
// Refresh UX
if (dirty) {
p.imageInfo = null;
emit('files:file:updated', { fileid });
utils.bus.emit('files:file:updated', { fileid });
}
} catch (e) {
console.error('Failed to save metadata for', p.fileid, e);
@ -232,7 +232,7 @@ export default defineComponent({
this.close();
// Trigger a soft refresh
emit('files:file:created', { fileid: 0 });
utils.bus.emit('memories:timeline:soft-refresh', null);
},
filterValid(photos: IPhoto[]) {

View File

@ -24,8 +24,9 @@
<script lang="ts">
import { defineComponent } from 'vue';
import * as utils from '../../services/utils';
const NcModal = () => import('@nextcloud/vue/dist/Components/NcModal');
import { subscribe, unsubscribe } from '@nextcloud/event-bus';
export default defineComponent({
name: 'Modal',
@ -53,8 +54,8 @@ export default defineComponent({
beforeMount() {
if (this.sidebar) {
subscribe('memories:sidebar:opened', this.handleAppSidebarOpen);
subscribe('memories:sidebar:closed', this.handleAppSidebarClose);
utils.bus.on('memories:sidebar:opened', this.handleAppSidebarOpen);
utils.bus.on('memories:sidebar:closed', this.handleAppSidebarClose);
}
this._mutationObserver = new MutationObserver(this.handleBodyMutation);
this._mutationObserver.observe(document.body, { childList: true });
@ -62,8 +63,8 @@ export default defineComponent({
beforeDestroy() {
if (this.sidebar) {
unsubscribe('memories:sidebar:opened', this.handleAppSidebarOpen);
unsubscribe('memories:sidebar:closed', this.handleAppSidebarClose);
utils.bus.off('memories:sidebar:opened', this.handleAppSidebarOpen);
utils.bus.off('memories:sidebar:closed', this.handleAppSidebarClose);
globalThis.mSidebar.close();
}
this._mutationObserver.disconnect();

View File

@ -66,8 +66,6 @@ import NcActions from '@nextcloud/vue/dist/Components/NcActions';
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton';
import NcActionCheckbox from '@nextcloud/vue/dist/Components/NcActionCheckbox';
import { emit } from '@nextcloud/event-bus';
import FaceEditModal from '../modal/FaceEditModal.vue';
import FaceDeleteModal from '../modal/FaceDeleteModal.vue';
import FaceMergeModal from '../modal/FaceMergeModal.vue';
@ -139,7 +137,7 @@ export default defineComponent({
changeShowFaceRect() {
this.updateSetting('show_face_rect');
emit('memories:timeline:hard-refresh', {});
utils.bus.emit('memories:timeline:hard-refresh', null);
},
},
});

View File

@ -41,7 +41,6 @@ import { latLngBounds, Icon } from 'leaflet';
import { IPhoto } from '../../types';
import axios from '@nextcloud/axios';
import { subscribe, unsubscribe } from '@nextcloud/event-bus';
import { API } from '../../services/API';
import * as utils from '../../services/utils';
@ -103,11 +102,11 @@ export default defineComponent({
},
created() {
subscribe('memories:window:resize', this.handleContainerResize);
utils.bus.on('memories:window:resize', this.handleContainerResize);
},
beforeDestroy() {
unsubscribe('memories:window:resize', this.handleContainerResize);
utils.bus.off('memories:window:resize', this.handleContainerResize);
},
computed: {

View File

@ -1,5 +1,5 @@
import { subscribe } from '@nextcloud/event-bus';
import { loadState } from '@nextcloud/initial-state';
import * as utils from '../../services/utils';
// Shown in dynamic top matter (Timeline::viewName)
export const title = loadState('memories', 'share_title', '');
@ -10,7 +10,7 @@ if (title) {
let isHidden = false; // cache state to avoid unnecessary DOM updates
// Hide header when recycler is scrolled down
subscribe('memories.recycler.scroll', ({ dynTopMatterVisible }: { dynTopMatterVisible: boolean }) => {
utils.bus.on('memories.recycler.scroll', ({ dynTopMatterVisible }: { dynTopMatterVisible: boolean }) => {
if (dynTopMatterVisible === isHidden) return;
header.classList.toggle('hidden', (isHidden = dynTopMatterVisible));
});

View File

@ -13,13 +13,13 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { subscribe, unsubscribe } from '@nextcloud/event-bus';
import FolderTopMatter from './FolderTopMatter.vue';
import ClusterTopMatter from './ClusterTopMatter.vue';
import FaceTopMatter from './FaceTopMatter.vue';
import AlbumTopMatter from './AlbumTopMatter.vue';
import * as utils from '../../services/utils';
export default defineComponent({
name: 'TopMatter',
components: {
@ -34,11 +34,11 @@ export default defineComponent({
}),
mounted() {
subscribe('memories.recycler.scroll', this.onRecyclerScroll);
utils.bus.on('memories.recycler.scroll', this.onRecyclerScroll);
},
beforeUnmount() {
unsubscribe('memories.recycler.scroll', this.onRecyclerScroll);
utils.bus.off('memories.recycler.scroll', this.onRecyclerScroll);
},
computed: {

View File

@ -5,7 +5,6 @@
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { emit } from '@nextcloud/event-bus';
import { showError, showSuccess } from '@nextcloud/dialogs';
import axios from '@nextcloud/axios';
@ -273,10 +272,10 @@ export default defineComponent({
showSuccess(this.t('memories', 'Image saved successfully'));
if (fileid !== this.photo.fileid) {
emit('files:file:created', { fileid });
utils.bus.emit('files:file:created', { fileid });
} else {
utils.updatePhotoFromImageInfo(this.photo, res.data);
emit('files:file:updated', { fileid });
utils.bus.emit('files:file:updated', { fileid });
}
this.onClose(undefined, false);
} catch (err) {

View File

@ -179,7 +179,6 @@ import type { PsContent } from './types';
import UserConfig from '../../mixins/UserConfig';
import NcActions from '@nextcloud/vue/dist/Components/NcActions';
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton';
import { subscribe, unsubscribe, emit } from '@nextcloud/event-bus';
import { showError } from '@nextcloud/dialogs';
import axios from '@nextcloud/axios';
@ -279,11 +278,11 @@ export default defineComponent({
}),
mounted() {
subscribe('memories:sidebar:opened', this.handleAppSidebarOpen);
subscribe('memories:sidebar:closed', this.handleAppSidebarClose);
subscribe('files:file:created', this.handleFileUpdated);
subscribe('files:file:updated', this.handleFileUpdated);
subscribe('memories:window:resize', this.handleWindowResize);
utils.bus.on('memories:sidebar:opened', this.handleAppSidebarOpen);
utils.bus.on('memories:sidebar:closed', this.handleAppSidebarClose);
utils.bus.on('files:file:created', this.handleFileUpdated);
utils.bus.on('files:file:updated', this.handleFileUpdated);
utils.bus.on('memories:window:resize', this.handleWindowResize);
// The viewer is a singleton
globalThis.mViewer = {
@ -295,11 +294,11 @@ export default defineComponent({
},
beforeDestroy() {
unsubscribe('memories:sidebar:opened', this.handleAppSidebarOpen);
unsubscribe('memories:sidebar:closed', this.handleAppSidebarClose);
unsubscribe('files:file:created', this.handleFileUpdated);
unsubscribe('files:file:updated', this.handleFileUpdated);
unsubscribe('memories:window:resize', this.handleWindowResize);
utils.bus.off('memories:sidebar:opened', this.handleAppSidebarOpen);
utils.bus.off('memories:sidebar:closed', this.handleAppSidebarClose);
utils.bus.off('files:file:created', this.handleFileUpdated);
utils.bus.off('files:file:updated', this.handleFileUpdated);
utils.bus.off('memories:window:resize', this.handleWindowResize);
},
computed: {
@ -383,12 +382,8 @@ export default defineComponent({
},
methods: {
deleted(photos: IPhoto[]) {
emit('memories:viewer:deleted', photos);
},
fetchDay(dayId: number) {
emit('memories:viewer:fetch-day', dayId as any);
utils.bus.emit('memories:viewer:fetch-day', dayId);
},
updateLoading(delta: number) {
@ -964,7 +959,7 @@ export default defineComponent({
}
// Remove from main view
this.deleted([photo]);
utils.bus.emit('memories:timeline:deleted', [photo]);
// If this is the only photo, close viewer
if (this.list.length === 1) {

View File

@ -1,11 +1,15 @@
import axios from '@nextcloud/axios';
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus';
import { API } from '../services/API';
import { defineComponent } from 'vue';
import axios from '@nextcloud/axios';
import { API } from '../services/API';
import * as utils from '../services/utils';
import { IConfig } from '../types';
import staticConfig from '../services/static-config';
const eventName = 'memories:user-config-changed';
const localSettings: (keyof IConfig)[] = [
'square_thumbs',
'full_res_on_zoom',
@ -19,16 +23,15 @@ export default defineComponent({
data: () => ({
config: { ...staticConfig.getDefault() },
configEventName: eventName,
}),
created() {
subscribe(eventName, this.updateLocalSetting);
utils.bus.on(eventName, this.updateLocalSetting);
this.refreshFromConfig();
},
beforeDestroy() {
unsubscribe(eventName, this.updateLocalSetting);
utils.bus.off(eventName, this.updateLocalSetting);
},
methods: {
@ -38,7 +41,7 @@ export default defineComponent({
if (changed.length === 0) return;
changed.forEach((key) => (this.config[key] = config[key]));
emit(eventName, { setting: null, value: null });
utils.bus.emit(eventName, null);
},
updateLocalSetting({ setting, value }) {
@ -58,7 +61,7 @@ export default defineComponent({
staticConfig.setLs(setting, value);
emit(eventName, { setting, value });
utils.bus.emit(eventName, { setting, value });
},
},
});

View File

@ -0,0 +1,63 @@
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus';
import { IConfig, IPhoto } from '../../types';
type BusEvent = {
/** Open/close the navigation drawer */
'toggle-navigation': { open: boolean };
/** File was created */
'files:file:created': { fileid: number };
/** File was updated */
'files:file:updated': { fileid: number };
/** Native sidebar was opened */
'files:sidebar:opened': null;
/** Native sidebar was closed */
'files:sidebar:closed': null;
/** Final event after sidebar is opened */
'memories:sidebar:opened': null;
/** Final event after sidebar is closed */
'memories:sidebar:closed': null;
/** Window was resized */
'memories:window:resize': null;
/** User configuration was changed */
'memories:user-config-changed': {
setting: keyof IConfig;
value: IConfig[keyof IConfig];
} | null;
/** Delete these photos from the timeline */
'memories:timeline:deleted': IPhoto[];
/** Viewer has requested fetching day */
'memories:viewer:fetch-day': number;
/** Soft-refresh the timeline */
'memories:timeline:soft-refresh': null;
/** Hard-refresh the timeline */
'memories:timeline:hard-refresh': null;
/** Timeline recycler scrolling */
'memories.recycler.scroll': {
current: number;
previous: number;
dynTopMatterVisible: boolean;
};
/** Albums were updated for these photos */
'memories:albums:update': IPhoto[];
};
/**
* Emit an event on the Nextcloud event bus.
* @param name Name of event
* @param data arguments
*/
export const bus = {
emit<T extends keyof BusEvent>(name: T, data: BusEvent[T]): void {
emit(name, data as any);
},
on<T extends keyof BusEvent>(name: T, callback: (data: BusEvent[T]) => void): void {
subscribe(name, callback);
},
off<T extends keyof BusEvent>(name: T, callback: (data: BusEvent[T]) => void): void {
unsubscribe(name, callback);
},
};

View File

@ -4,3 +4,4 @@ export * from './const';
export * from './date';
export * from './helpers';
export * from './dialog';
export * from './event-bus';