├── less ├── admin.less └── forum.less ├── js ├── admin.ts ├── forum.ts ├── webpack.config.js ├── dist-typings │ ├── admin │ │ ├── index.d.ts │ │ └── extend.d.ts │ └── forum │ │ └── index.d.ts ├── dist │ ├── forum.js.LICENSE.txt │ ├── admin.js │ ├── admin.js.map │ └── forum.js ├── src │ ├── admin │ │ ├── index.ts │ │ └── extend.tsx │ └── forum │ │ ├── shims.d.ts │ │ └── index.tsx ├── tsconfig.json └── package.json ├── icon.png ├── locale └── en.yml ├── src ├── PusherNotificationDriver.php ├── SendPusherNotificationsJob.php ├── Provider │ └── PusherProvider.php ├── Api │ └── Controller │ │ └── AuthController.php └── Listener │ └── PushNewPost.php ├── LICENSE ├── extend.php ├── composer.json └── CHANGELOG.md /less/admin.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /js/admin.ts: -------------------------------------------------------------------------------- 1 | export * from './src/admin'; 2 | -------------------------------------------------------------------------------- /js/forum.ts: -------------------------------------------------------------------------------- 1 | export * from './src/forum'; 2 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flarum/pusher/HEAD/icon.png -------------------------------------------------------------------------------- /js/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('flarum-webpack-config')(); 2 | -------------------------------------------------------------------------------- /js/dist-typings/admin/index.d.ts: -------------------------------------------------------------------------------- 1 | export { default as extend } from './extend'; 2 | -------------------------------------------------------------------------------- /js/dist-typings/admin/extend.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: import("flarum/common/extenders/Admin").default[]; 2 | export default _default; 3 | -------------------------------------------------------------------------------- /js/dist/forum.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * Pusher JavaScript Library v8.4.0 3 | * https://pusher.com/ 4 | * 5 | * Copyright 2020, Pusher 6 | * Released under the MIT licence. 7 | */ 8 | -------------------------------------------------------------------------------- /js/src/admin/index.ts: -------------------------------------------------------------------------------- 1 | import app from 'flarum/admin/app'; 2 | 3 | export { default as extend } from './extend'; 4 | 5 | app.initializers.add('flarum-pusher', () => { 6 | // ... 7 | }); 8 | -------------------------------------------------------------------------------- /less/forum.less: -------------------------------------------------------------------------------- 1 | .DiscussionList-update { 2 | .Button--color(@alert-color, @alert-bg); 3 | margin-bottom: 5px; 4 | 5 | .DiscussionPage & { 6 | border-radius: 0; 7 | margin-bottom: 0; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /js/dist-typings/forum/index.d.ts: -------------------------------------------------------------------------------- 1 | import Pusher, { Channel } from 'pusher-js'; 2 | export type PusherBinding = { 3 | channels: { 4 | main: Channel; 5 | user: Channel | null; 6 | }; 7 | pusher: Pusher; 8 | }; 9 | -------------------------------------------------------------------------------- /js/src/forum/shims.d.ts: -------------------------------------------------------------------------------- 1 | import Pusher, { Channel } from 'pusher-js'; 2 | 3 | declare module 'flarum/forum/ForumApplication' { 4 | export default interface ForumApplication { 5 | pusher: Promise<{ 6 | channels: { 7 | main: Channel; 8 | user: Channel | null; 9 | }; 10 | pusher: Pusher; 11 | }>; 12 | 13 | pushedUpdates: Array; 14 | } 15 | } 16 | 17 | declare module 'flarum/forum/components/DiscussionList' { 18 | export default interface DiscussionList { 19 | loadingUpdated?: boolean; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /js/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use Flarum's tsconfig as a starting point 3 | "extends": "flarum-tsconfig", 4 | // This will match all .ts, .tsx, .d.ts, .js, .jsx files in your `src` folder 5 | // and also tells your Typescript server to read core's global typings for 6 | // access to `dayjs` and `$` in the global namespace. 7 | "include": ["src/**/*", "../../../*/*/js/dist-typings/@types/**/*", "@types/**/*"], 8 | "compilerOptions": { 9 | // This will output typings to `dist-typings` 10 | "declarationDir": "./dist-typings", 11 | "paths": { 12 | "flarum/*": ["../../../framework/core/js/dist-typings/*"], 13 | "ext:flarum/tags/*": ["../../tags/js/dist-typings/*"] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /locale/en.yml: -------------------------------------------------------------------------------- 1 | flarum-pusher: 2 | 3 | ## 4 | # UNIQUE KEYS - The following keys are used in only one location each. 5 | ## 6 | 7 | # Translations in this namespace are used by the admin interface. 8 | admin: 9 | 10 | # These translations are used in the Pusher Settings modal dialog. 11 | pusher_settings: 12 | app_cluster_label: Cluster 13 | app_id_label: App ID 14 | app_key_label: App Key 15 | app_secret_label: App Secret 16 | title: Pusher Settings 17 | 18 | # Translations in this namespace are used by the admin interface. 19 | forum: 20 | 21 | # These translations are used in the discussion list. 22 | discussion_list: 23 | show_updates_text: "{count, plural, one {Show # updated discussion} other {Show # updated discussions}}" 24 | -------------------------------------------------------------------------------- /src/PusherNotificationDriver.php: -------------------------------------------------------------------------------- 1 | queue->push(new SendPusherNotificationsJob($blueprint, $users)); 27 | } 28 | } 29 | 30 | public function registerType(string $blueprintClass, array $driversEnabledByDefault): void 31 | { 32 | // ... 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/SendPusherNotificationsJob.php: -------------------------------------------------------------------------------- 1 | recipients as $user) { 29 | if ($user->shouldAlert($this->blueprint::getType())) { 30 | $pusher->trigger('private-user'.$user->id, 'notification', null); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019-2024 Stichting Flarum (Flarum Foundation) 4 | Copyright (c) 2014-2019 Toby Zerner (toby.zerner@gmail.com) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/Provider/PusherProvider.php: -------------------------------------------------------------------------------- 1 | container->bind(Pusher::class, function () { 21 | $settings = $this->container->make(SettingsRepositoryInterface::class); 22 | 23 | $options = []; 24 | 25 | if ($cluster = $settings->get('flarum-pusher.app_cluster')) { 26 | $options['cluster'] = $cluster; 27 | } 28 | if ($host = $settings->get('flarum-pusher.server_hostname')) { 29 | $options['host'] = $host; 30 | } 31 | 32 | return new Pusher( 33 | $settings->get('flarum-pusher.app_key'), 34 | $settings->get('flarum-pusher.app_secret'), 35 | $settings->get('flarum-pusher.app_id'), 36 | $options 37 | ); 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /js/src/admin/extend.tsx: -------------------------------------------------------------------------------- 1 | import Extend from 'flarum/common/extenders'; 2 | import app from 'flarum/admin/app'; 3 | 4 | export default [ 5 | new Extend.Admin() 6 | .setting( 7 | () => ({ 8 | setting: 'flarum-pusher.app_id', 9 | label: app.translator.trans('flarum-pusher.admin.pusher_settings.app_id_label'), 10 | type: 'text', 11 | }), 12 | 40 13 | ) 14 | .setting( 15 | () => ({ 16 | setting: 'flarum-pusher.app_key', 17 | label: app.translator.trans('flarum-pusher.admin.pusher_settings.app_key_label'), 18 | type: 'text', 19 | }), 20 | 30 21 | ) 22 | .setting( 23 | () => ({ 24 | setting: 'flarum-pusher.app_secret', 25 | label: app.translator.trans('flarum-pusher.admin.pusher_settings.app_secret_label'), 26 | type: 'text', 27 | }), 28 | 20 29 | ) 30 | .setting( 31 | () => ({ 32 | setting: 'flarum-pusher.app_cluster', 33 | label: app.translator.trans('flarum-pusher.admin.pusher_settings.app_cluster_label'), 34 | type: 'text', 35 | }), 36 | 10 37 | ) 38 | .setting( 39 | () => ({ 40 | setting: 'flarum-pusher.server_hostname', 41 | label: app.translator.trans('flarum-pusher.admin.pusher_settings.server_hostname_label'), 42 | type: 'text', 43 | }), 44 | 0 45 | ), 46 | ]; 47 | -------------------------------------------------------------------------------- /js/dist/admin.js: -------------------------------------------------------------------------------- 1 | (()=>{var e={n:t=>{var r=t&&t.__esModule?()=>t.default:()=>t;return e.d(r,{a:r}),r},d:(t,r)=>{for(var a in r)e.o(r,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:r[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};(()=>{"use strict";e.r(t),e.d(t,{extend:()=>n});const r=flarum.reg.get("core","admin/app");var a=e.n(r);const s=flarum.reg.get("core","common/extenders"),n=[(new(e.n(s)().Admin)).setting((()=>({setting:"flarum-pusher.app_id",label:a().translator.trans("flarum-pusher.admin.pusher_settings.app_id_label"),type:"text"})),40).setting((()=>({setting:"flarum-pusher.app_key",label:a().translator.trans("flarum-pusher.admin.pusher_settings.app_key_label"),type:"text"})),30).setting((()=>({setting:"flarum-pusher.app_secret",label:a().translator.trans("flarum-pusher.admin.pusher_settings.app_secret_label"),type:"text"})),20).setting((()=>({setting:"flarum-pusher.app_cluster",label:a().translator.trans("flarum-pusher.admin.pusher_settings.app_cluster_label"),type:"text"})),10).setting((()=>({setting:"flarum-pusher.server_hostname",label:a().translator.trans("flarum-pusher.admin.pusher_settings.server_hostname_label"),type:"text"})),0)];a().initializers.add("flarum-pusher",(()=>{}))})(),module.exports=t})(); 2 | //# sourceMappingURL=admin.js.map -------------------------------------------------------------------------------- /extend.php: -------------------------------------------------------------------------------- 1 | js(__DIR__.'/js/dist/forum.js') 20 | ->css(__DIR__.'/less/forum.less'), 21 | 22 | (new Extend\Frontend('admin')) 23 | ->js(__DIR__.'/js/dist/admin.js'), 24 | 25 | (new Extend\Routes('api')) 26 | ->post('/pusher/auth', 'pusher.auth', AuthController::class), 27 | 28 | new Extend\Locales(__DIR__.'/locale'), 29 | 30 | (new Extend\Notification()) 31 | ->driver('pusher', PusherNotificationDriver::class), 32 | 33 | (new Extend\Settings()) 34 | ->serializeToForum('pusherKey', 'flarum-pusher.app_key') 35 | ->serializeToForum('pusherCluster', 'flarum-pusher.app_cluster') 36 | ->serializeToForum('pusherHostname', 'flarum-pusher.server_hostname'), 37 | 38 | (new Extend\Event()) 39 | ->listen(Posted::class, Listener\PushNewPost::class), 40 | 41 | (new Extend\ServiceProvider()) 42 | ->register(PusherProvider::class), 43 | ]; 44 | -------------------------------------------------------------------------------- /js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@flarum/pusher", 4 | "version": "0.0.0", 5 | "prettier": "@flarum/prettier-config", 6 | "scripts": { 7 | "dev": "webpack --mode development --watch", 8 | "build": "webpack --mode production", 9 | "analyze": "cross-env ANALYZER=true yarn run build", 10 | "format": "prettier --write src", 11 | "format-check": "prettier --check src", 12 | "clean-typings": "npx rimraf dist-typings && mkdir dist-typings", 13 | "build-typings": "yarn run clean-typings && ([ -e src/@types ] && cp -r src/@types dist-typings/@types || true) && tsc && yarn run post-build-typings", 14 | "post-build-typings": "find dist-typings -type f -name '*.d.ts' -print0 | xargs -0 sed -i 's,../src/@types,@types,g'", 15 | "check-typings": "tsc --noEmit --emitDeclarationOnly false", 16 | "check-typings-coverage": "typescript-coverage-report" 17 | }, 18 | "devDependencies": { 19 | "@flarum/prettier-config": "^1.0.0", 20 | "@types/pusher-js": "^5.1.0", 21 | "flarum-tsconfig": "^2.0.0", 22 | "flarum-webpack-config": "^3.0.0", 23 | "prettier": "^2.5.1", 24 | "typescript": "^4.5.4", 25 | "typescript-coverage-report": "^0.6.1", 26 | "webpack": "^5.76.0", 27 | "webpack-cli": "^4.9.1" 28 | }, 29 | "dependencies": { 30 | "pusher-js": "^8.4.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Api/Controller/AuthController.php: -------------------------------------------------------------------------------- 1 | id; 32 | $body = $request->getParsedBody(); 33 | 34 | if (Arr::get($body, 'channel_name') === $userChannel) { 35 | $pusher = new Pusher( 36 | $this->settings->get('flarum-pusher.app_key'), 37 | $this->settings->get('flarum-pusher.app_secret'), 38 | $this->settings->get('flarum-pusher.app_id'), 39 | [ 40 | 'cluster' => $this->settings->get('flarum-pusher.app_cluster'), 41 | 'host' => $this->settings->get('flarum-pusher.server_hostname'), 42 | ] 43 | ); 44 | 45 | $payload = json_decode($pusher->socket_auth($userChannel, Arr::get($body, 'socket_id')), true); 46 | 47 | return new JsonResponse($payload); 48 | } 49 | 50 | return new EmptyResponse(403); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Listener/PushNewPost.php: -------------------------------------------------------------------------------- 1 | post->isVisibleTo(new Guest)) { 32 | $channels[] = 'public'; 33 | } else { 34 | // Retrieve private channels, used for each user. 35 | $response = $this->pusher->getChannels([ 36 | 'filter_by_prefix' => 'private-user' 37 | ]); 38 | 39 | // @phpstan-ignore-next-line 40 | if (! $response) { 41 | return; 42 | } 43 | 44 | foreach ($response->channels as $name => $channel) { 45 | $userId = Str::after($name, 'private-user'); 46 | 47 | if (($user = User::find($userId)) && $event->post->isVisibleTo($user)) { 48 | $channels[] = $name; 49 | } 50 | } 51 | } 52 | 53 | if (count($channels)) { 54 | $tags = $this->extensions->isEnabled('flarum-tags') ? $event->post->discussion->tags : null; 55 | 56 | $this->pusher->trigger($channels, 'newPost', [ 57 | 'postId' => $event->post->id, 58 | 'discussionId' => $event->post->discussion->id, 59 | 'tagIds' => $tags ? $tags->pluck('id') : null 60 | ]); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flarum/pusher", 3 | "description": "See new discussions and posts in real-time using Pusher.", 4 | "type": "flarum-extension", 5 | "keywords": [ 6 | "discussion" 7 | ], 8 | "license": "MIT", 9 | "support": { 10 | "issues": "https://github.com/flarum/framework/issues", 11 | "source": "https://github.com/flarum/pusher", 12 | "forum": "https://discuss.flarum.org" 13 | }, 14 | "homepage": "https://flarum.org", 15 | "funding": [ 16 | { 17 | "type": "website", 18 | "url": "https://flarum.org/donate/" 19 | } 20 | ], 21 | "require": { 22 | "flarum/core": "^2.0.0-beta.5", 23 | "pusher/pusher-php-server": "^7.2" 24 | }, 25 | "require-dev": { 26 | "flarum/tags": "^1.0" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "Flarum\\Pusher\\": "src/" 31 | } 32 | }, 33 | "extra": { 34 | "branch-alias": { 35 | "dev-main": "2.x-dev" 36 | }, 37 | "flarum-extension": { 38 | "title": "Pusher", 39 | "category": "feature", 40 | "icon": { 41 | "image": "icon.png", 42 | "backgroundSize": "46% 63%", 43 | "backgroundPosition": "center", 44 | "backgroundRepeat": "no-repeat", 45 | "backgroundColor": "#40bad8", 46 | "color": "#fff" 47 | } 48 | }, 49 | "flarum-cli": { 50 | "modules": { 51 | "admin": true, 52 | "forum": true, 53 | "js": true, 54 | "jsCommon": false, 55 | "css": true, 56 | "gitConf": true, 57 | "githubActions": true, 58 | "prettier": true, 59 | "typescript": true, 60 | "bundlewatch": false, 61 | "backendTesting": false, 62 | "editorConfig": true, 63 | "styleci": true 64 | } 65 | } 66 | }, 67 | "repositories": [ 68 | { 69 | "type": "path", 70 | "url": "../../*/*" 71 | } 72 | ], 73 | "minimum-stability": "dev", 74 | "prefer-stable": true 75 | } 76 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.2.0](https://github.com/flarum/pusher/compare/v1.1.0...v1.2.0) 4 | 5 | ### Changed 6 | - Convert to TypeScript (https://github.com/flarum/pusher/pull/34). 7 | - Replace jQuery with vanilla JS (https://github.com/flarum/pusher/pull/35). 8 | 9 | ### Fixed 10 | - Discussion list pagination breaks when clicking on the update button (https://github.com/flarum/pusher/pull/33). 11 | 12 | ## [1.1.0](https://github.com/flarum/pusher/compare/v1.0.0...v1.1.0) 13 | 14 | No changes. 15 | 16 | ## [1.0.0](https://github.com/flarum/pusher/compare/v0.1.0-beta.16...v1.0.0) 17 | 18 | ### Added 19 | - Make non public discussions realtime (https://github.com/flarum/pusher/pull/17) 20 | 21 | ### Changed 22 | - Compatibility with Flarum v1.0.0. 23 | 24 | ## [0.1.0-beta.16](https://github.com/flarum/pusher/compare/v0.1.0-beta.15...v0.1.0-beta.16) 25 | 26 | ### Changed 27 | - Moved locale files from translation pack to extension (https://github.com/flarum/pusher/pull/26) 28 | 29 | ## [0.1.0-beta.15](https://github.com/flarum/pusher/compare/v0.1.0-beta.14.1...v0.1.0-beta.15) 30 | 31 | ### Changed 32 | - Updated composer.json and admin javascript for new admin area. 33 | - Updated to use newest extenders. 34 | 35 | ## [0.1.0-beta.14.1](https://github.com/flarum/pusher/compare/v0.1.0-beta.14...v0.1.0-beta.14.1) 36 | 37 | ### Fixed 38 | - Children were incorrectly passed to show update highlight 39 | - Update discussion list caused an error (#27) 40 | 41 | ## [0.1.0-beta.14](https://github.com/flarum/pusher/compare/v0.1.0-beta.13...v0.1.0-beta.14) 42 | 43 | ### Changed 44 | - Updated mithril to version 2 45 | - Load language strings correctly on en-/disable 46 | - Updated JS dependencies 47 | 48 | ### Fixed 49 | - Publish no events for tags when flarum/tags isn't installed (#25) 50 | 51 | ## [0.1.0-beta.13](https://github.com/flarum/pusher/compare/v0.1.0-beta.12...v0.1.0-beta.13) 52 | 53 | ### Changed 54 | - Use different CDN for loading Pusher JS library (#20) 55 | - Updated JS dependencies 56 | 57 | ## [0.1.0-beta.9](https://github.com/flarum/pusher/compare/v0.1.0-beta.8.1...v0.1.0-beta.9) 58 | 59 | ### Changed 60 | - Replace event subscribers (that resolve services too early) with listeners ([da0f0af](https://github.com/flarum/pusher/commit/da0f0afb24bae39535b4beaf750f311c403adef1) and [28a70ff](https://github.com/flarum/pusher/commit/28a70ff074014bc75acee6eff7a74faecf5ae341)) 61 | 62 | ## [0.1.0-beta.8.1](https://github.com/flarum/pusher/compare/v0.1.0-beta.8...v0.1.0-beta.8.1) 63 | 64 | ### Fixed 65 | - Fix broken functionality ([00b127c](https://github.com/flarum/pusher/commit/00b127c576e5554bc04b491ec47ae57f8525fac3)) 66 | -------------------------------------------------------------------------------- /js/dist/admin.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"admin.js","mappings":"MACA,IAAIA,EAAsB,CCA1BA,EAAyBC,IACxB,IAAIC,EAASD,GAAUA,EAAOE,WAC7B,IAAOF,EAAiB,QACxB,IAAM,EAEP,OADAD,EAAoBI,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,CAAM,ECLdF,EAAwB,CAACM,EAASC,KACjC,IAAI,IAAIC,KAAOD,EACXP,EAAoBS,EAAEF,EAAYC,KAASR,EAAoBS,EAAEH,EAASE,IAC5EE,OAAOC,eAAeL,EAASE,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,IAE1E,ECNDR,EAAwB,CAACc,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,GCClFf,EAAyBM,IACH,oBAAXa,QAA0BA,OAAOC,aAC1CV,OAAOC,eAAeL,EAASa,OAAOC,YAAa,CAAEC,MAAO,WAE7DX,OAAOC,eAAeL,EAAS,aAAc,CAAEe,OAAO,GAAO,G,qDCL9D,MAAM,EAA+BC,OAAOC,IAAIV,IAAI,OAAQ,a,aCA5D,MAAM,EAA+BS,OAAOC,IAAIV,IAAI,OAAQ,oBCE5D,IAAgB,I,MAAI,WAAeW,SAAQ,KAAM,CAC/CA,QAAS,uBACTC,MAAO,eAAeC,MAAM,oDAC5BC,KAAM,UACJ,IAAIH,SAAQ,KAAM,CACpBA,QAAS,wBACTC,MAAO,eAAeC,MAAM,qDAC5BC,KAAM,UACJ,IAAIH,SAAQ,KAAM,CACpBA,QAAS,2BACTC,MAAO,eAAeC,MAAM,wDAC5BC,KAAM,UACJ,IAAIH,SAAQ,KAAM,CACpBA,QAAS,4BACTC,MAAO,eAAeC,MAAM,yDAC5BC,KAAM,UACJ,IAAIH,SAAQ,KAAM,CACpBA,QAAS,gCACTC,MAAO,eAAeC,MAAM,6DAC5BC,KAAM,UACJ,ICpBJ,iBAAiBC,IAAI,iBAAiB,Q","sources":["webpack://@flarum/pusher/webpack/bootstrap","webpack://@flarum/pusher/webpack/runtime/compat get default export","webpack://@flarum/pusher/webpack/runtime/define property getters","webpack://@flarum/pusher/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/pusher/webpack/runtime/make namespace object","webpack://@flarum/pusher/external root \"flarum.reg.get('core', 'admin/app')\"","webpack://@flarum/pusher/external root \"flarum.reg.get('core', 'common/extenders')\"","webpack://@flarum/pusher/./src/admin/extend.tsx","webpack://@flarum/pusher/./src/admin/index.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.reg.get('core', 'admin/app');","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.reg.get('core', 'common/extenders');","import Extend from 'flarum/common/extenders';\nimport app from 'flarum/admin/app';\nexport default [new Extend.Admin().setting(() => ({\n setting: 'flarum-pusher.app_id',\n label: app.translator.trans('flarum-pusher.admin.pusher_settings.app_id_label'),\n type: 'text'\n}), 40).setting(() => ({\n setting: 'flarum-pusher.app_key',\n label: app.translator.trans('flarum-pusher.admin.pusher_settings.app_key_label'),\n type: 'text'\n}), 30).setting(() => ({\n setting: 'flarum-pusher.app_secret',\n label: app.translator.trans('flarum-pusher.admin.pusher_settings.app_secret_label'),\n type: 'text'\n}), 20).setting(() => ({\n setting: 'flarum-pusher.app_cluster',\n label: app.translator.trans('flarum-pusher.admin.pusher_settings.app_cluster_label'),\n type: 'text'\n}), 10).setting(() => ({\n setting: 'flarum-pusher.server_hostname',\n label: app.translator.trans('flarum-pusher.admin.pusher_settings.server_hostname_label'),\n type: 'text'\n}), 0)];","import app from 'flarum/admin/app';\nexport { default as extend } from './extend';\napp.initializers.add('flarum-pusher', () => {\n // ...\n});"],"names":["__webpack_require__","module","getter","__esModule","d","a","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","flarum","reg","setting","label","trans","type","add"],"sourceRoot":""} -------------------------------------------------------------------------------- /js/src/forum/index.tsx: -------------------------------------------------------------------------------- 1 | import Pusher, { Channel } from 'pusher-js'; 2 | import app from 'flarum/forum/app'; 3 | import { extend } from 'flarum/common/extend'; 4 | import DiscussionList from 'flarum/forum/components/DiscussionList'; 5 | import DiscussionPage from 'flarum/forum/components/DiscussionPage'; 6 | import IndexPage from 'flarum/forum/components/IndexPage'; 7 | import Button from 'flarum/common/components/Button'; 8 | import ItemList from 'flarum/common/utils/ItemList'; 9 | import type { Children } from 'mithril'; 10 | import type Tag from 'ext:flarum/tags/common/models/Tag'; 11 | 12 | export type PusherBinding = { 13 | channels: { 14 | main: Channel; 15 | user: Channel | null; 16 | }; 17 | pusher: Pusher; 18 | }; 19 | 20 | app.initializers.add('flarum-pusher', () => { 21 | app.pusher = (async () => { 22 | const socket: Pusher = new Pusher(app.forum.attribute('pusherKey'), { 23 | authEndpoint: `${app.forum.attribute('apiUrl')}/pusher/auth`, 24 | cluster: app.forum.attribute('pusherCluster'), 25 | auth: { 26 | headers: { 27 | 'X-CSRF-Token': app.session.csrfToken, 28 | }, 29 | }, 30 | httpHost: app.forum.attribute('pusherHostname'), 31 | wsHost: app.forum.attribute('pusherHostname'), 32 | }); 33 | 34 | return { 35 | channels: { 36 | main: socket.subscribe('public'), 37 | user: app.session.user ? socket.subscribe(`private-user${app.session.user.id()}`) : null, 38 | }, 39 | pusher: socket, 40 | }; 41 | })(); 42 | 43 | app.pushedUpdates = []; 44 | 45 | extend(DiscussionList.prototype, 'oncreate', function () { 46 | app.pusher.then((binding: PusherBinding) => { 47 | const pusher = binding.pusher; 48 | 49 | pusher.bind('newPost', (data: { tagIds: string[]; discussionId: number }) => { 50 | const params = app.discussions.getParams(); 51 | 52 | if (!params.q && !params.sort && !params.filter) { 53 | if (params.tags) { 54 | const tag = app.store.getBy('tags', 'slug', params.tags); 55 | const tagId = tag?.id(); 56 | 57 | if (!tagId || !data.tagIds.includes(tagId)) return; 58 | } 59 | 60 | const id = String(data.discussionId); 61 | 62 | if ((!app.current.get('discussion') || id !== app.current.get('discussion').id()) && app.pushedUpdates.indexOf(id) === -1) { 63 | app.pushedUpdates.push(id); 64 | 65 | if (app.current.matches(IndexPage)) { 66 | app.setTitleCount(app.pushedUpdates.length); 67 | } 68 | 69 | m.redraw(); 70 | } 71 | } 72 | }); 73 | }); 74 | }); 75 | 76 | extend(DiscussionList.prototype, 'onremove', function () { 77 | app.pusher.then((binding: PusherBinding) => { 78 | binding.pusher.unbind('newPost'); 79 | }); 80 | }); 81 | 82 | extend(DiscussionList.prototype, 'view', function (this: DiscussionList, vdom: Children) { 83 | if (app.pushedUpdates) { 84 | const count = app.pushedUpdates.length; 85 | 86 | if (count && typeof vdom === 'object' && vdom && 'children' in vdom && vdom.children instanceof Array) { 87 | vdom.children.unshift( 88 | 103 | ); 104 | } 105 | } 106 | }); 107 | 108 | extend(DiscussionPage.prototype, 'oncreate', function (this: DiscussionPage) { 109 | app.pusher.then((binding: PusherBinding) => { 110 | const pusher = binding.pusher; 111 | 112 | pusher.bind('newPost', (data: { discussionId: number }) => { 113 | const id = String(data.discussionId); 114 | const discussionId = this.discussion?.id(); 115 | 116 | if (this.discussion && discussionId === id && this.stream) { 117 | const oldCount = this.discussion.commentCount() ?? 0; 118 | 119 | app.store.find('discussions', discussionId).then(() => { 120 | this.stream?.update().then(m.redraw); 121 | 122 | if (!document.hasFocus()) { 123 | app.setTitleCount(Math.max(0, (this.discussion?.commentCount() ?? 0) - oldCount)); 124 | 125 | window.addEventListener('focus', () => app.setTitleCount(0), { once: true }); 126 | } 127 | }); 128 | } 129 | }); 130 | }); 131 | }); 132 | 133 | extend(DiscussionPage.prototype, 'onremove', function () { 134 | app.pusher.then((binding: PusherBinding) => { 135 | binding.pusher.unbind('newPost'); 136 | }); 137 | }); 138 | 139 | extend(IndexPage.prototype, 'actionItems', (items: ItemList) => { 140 | items.remove('refresh'); 141 | }); 142 | 143 | app.pusher.then((binding: PusherBinding) => { 144 | const channels = binding.channels; 145 | 146 | if (channels.user) { 147 | channels.user.bind('notification', () => { 148 | if (app.session.user) { 149 | app.session.user.pushAttributes({ 150 | unreadNotificationCount: app.session.user.unreadNotificationCount() ?? 0 + 1, 151 | newNotificationCount: app.session.user.newNotificationCount() ?? 0 + 1, 152 | }); 153 | } 154 | app.notifications.clear(); 155 | m.redraw(); 156 | }); 157 | } 158 | }); 159 | }); 160 | -------------------------------------------------------------------------------- /js/dist/forum.js: -------------------------------------------------------------------------------- 1 | /*! For license information please see forum.js.LICENSE.txt */ 2 | (()=>{var t={102:t=>{var e;window,e=function(){return function(t){var e={};function n(i){if(e[i])return e[i].exports;var r=e[i]={i,l:!1,exports:{}};return t[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(i,r,function(e){return t[e]}.bind(null,r));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=2)}([function(t,e,n){"use strict";var i,r=this&&this.__extends||(i=function(t,e){return i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])},i(t,e)},function(t,e){function n(){this.constructor=t}i(t,e),t.prototype=null===e?Object.create(e):(n.prototype=e.prototype,new n)});Object.defineProperty(e,"__esModule",{value:!0});var s=256,o=function(){function t(t){void 0===t&&(t="="),this._paddingCharacter=t}return t.prototype.encodedLength=function(t){return this._paddingCharacter?(t+2)/3*4|0:(8*t+5)/6|0},t.prototype.encode=function(t){for(var e="",n=0;n>>18&63),e+=this._encodeByte(i>>>12&63),e+=this._encodeByte(i>>>6&63),e+=this._encodeByte(i>>>0&63)}var r=t.length-n;return r>0&&(i=t[n]<<16|(2===r?t[n+1]<<8:0),e+=this._encodeByte(i>>>18&63),e+=this._encodeByte(i>>>12&63),e+=2===r?this._encodeByte(i>>>6&63):this._paddingCharacter||"",e+=this._paddingCharacter||""),e},t.prototype.maxDecodedLength=function(t){return this._paddingCharacter?t/4*3|0:(6*t+7)/8|0},t.prototype.decodedLength=function(t){return this.maxDecodedLength(t.length-this._getPaddingLength(t))},t.prototype.decode=function(t){if(0===t.length)return new Uint8Array(0);for(var e=this._getPaddingLength(t),n=t.length-e,i=new Uint8Array(this.maxDecodedLength(n)),r=0,o=0,a=0,c=0,h=0,u=0,l=0;o>>4,i[r++]=h<<4|u>>>2,i[r++]=u<<6|l,a|=c&s,a|=h&s,a|=u&s,a|=l&s;if(o>>4,a|=c&s,a|=h&s),o>>2,a|=u&s),o>>8&6,e+=51-t>>>8&-75,e+=61-t>>>8&-15,e+=62-t>>>8&3,String.fromCharCode(e)},t.prototype._decodeChar=function(t){var e=s;return e+=(42-t&t-44)>>>8&-256+t-43+62,e+=(46-t&t-48)>>>8&-256+t-47+63,e+=(47-t&t-58)>>>8&-256+t-48+52,(e+=(64-t&t-91)>>>8&-256+t-65+0)+((96-t&t-123)>>>8&-256+t-97+26)},t.prototype._getPaddingLength=function(t){var e=0;if(this._paddingCharacter){for(var n=t.length-1;n>=0&&t[n]===this._paddingCharacter;n--)e++;if(t.length<4||e>2)throw new Error("Base64Coder: incorrect padding")}return e},t}();e.Coder=o;var a=new o;e.encode=function(t){return a.encode(t)},e.decode=function(t){return a.decode(t)};var c=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r(e,t),e.prototype._encodeByte=function(t){var e=t;return e+=65,e+=25-t>>>8&6,e+=51-t>>>8&-75,e+=61-t>>>8&-13,e+=62-t>>>8&49,String.fromCharCode(e)},e.prototype._decodeChar=function(t){var e=s;return e+=(44-t&t-46)>>>8&-256+t-45+62,e+=(94-t&t-96)>>>8&-256+t-95+63,e+=(47-t&t-58)>>>8&-256+t-48+52,(e+=(64-t&t-91)>>>8&-256+t-65+0)+((96-t&t-123)>>>8&-256+t-97+26)},e}(o);e.URLSafeCoder=c;var h=new c;e.encodeURLSafe=function(t){return h.encode(t)},e.decodeURLSafe=function(t){return h.decode(t)},e.encodedLength=function(t){return a.encodedLength(t)},e.maxDecodedLength=function(t){return a.maxDecodedLength(t)},e.decodedLength=function(t){return a.decodedLength(t)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i="utf8: invalid string",r="utf8: invalid source encoding";function s(t){for(var e=0,n=0;n=t.length-1)throw new Error(i);n++,e+=4}}return e}e.encode=function(t){for(var e=new Uint8Array(s(t)),n=0,i=0;i>6,e[n++]=128|63&r):r<55296?(e[n++]=224|r>>12,e[n++]=128|r>>6&63,e[n++]=128|63&r):(i++,r=(1023&r)<<10,r|=1023&t.charCodeAt(i),r+=65536,e[n++]=240|r>>18,e[n++]=128|r>>12&63,e[n++]=128|r>>6&63,e[n++]=128|63&r)}return e},e.encodedLength=s,e.decode=function(t){for(var e=[],n=0;n=t.length)throw new Error(r);if(128!=(192&(o=t[++n])))throw new Error(r);i=(31&i)<<6|63&o,s=128}else if(i<240){if(n>=t.length-1)throw new Error(r);var o=t[++n],a=t[++n];if(128!=(192&o)||128!=(192&a))throw new Error(r);i=(15&i)<<12|(63&o)<<6|63&a,s=2048}else{if(!(i<248))throw new Error(r);if(n>=t.length-2)throw new Error(r);o=t[++n],a=t[++n];var c=t[++n];if(128!=(192&o)||128!=(192&a)||128!=(192&c))throw new Error(r);i=(15&i)<<18|(63&o)<<12|(63&a)<<6|63&c,s=65536}if(i=55296&&i<=57343)throw new Error(r);if(i>=65536){if(i>1114111)throw new Error(r);i-=65536,e.push(String.fromCharCode(55296|i>>10)),i=56320|1023&i}}e.push(String.fromCharCode(i))}return e.join("")}},function(t,e,n){t.exports=n(3).default},function(t,e,n){"use strict";n.r(e);class i{constructor(t,e){this.lastId=0,this.prefix=t,this.name=e}create(t){this.lastId++;var e=this.lastId,n=this.prefix+e,i=this.name+"["+e+"]",r=!1,s=function(){r||(t.apply(null,arguments),r=!0)};return this[e]=s,{number:e,id:n,name:i,callback:s}}remove(t){delete this[t.number]}}var r=new i("_pusher_script_","Pusher.ScriptReceivers"),s="8.4.0",o=7,a=80,c=443,h="",u="sockjs.pusher.com",l=80,d=443,p="/pusher",f="stats.pusher.com",g="/pusher/auth",m="ajax",b=12e4,v=3e4,y=1e4,w={endpoint:"/pusher/user-auth",transport:"ajax"},S={endpoint:"/pusher/auth",transport:"ajax"},_="http://js.pusher.com",k="https://js.pusher.com",C="",T=new i("_pusher_dependencies","Pusher.DependenciesReceivers"),P=new class{constructor(t){this.options=t,this.receivers=t.receivers||r,this.loading={}}load(t,e,n){var i=this;if(i.loading[t]&&i.loading[t].length>0)i.loading[t].push(n);else{i.loading[t]=[n];var r=Ce.createScriptRequest(i.getPath(t,e)),s=i.receivers.create((function(e){if(i.receivers.remove(s),i.loading[t]){var n=i.loading[t];delete i.loading[t];for(var o=function(t){t||r.cleanup()},a=0;a>>6)+z(128|63&e):z(224|e>>>12&15)+z(128|e>>>6&63)+z(128|63&e)},X=function(t){var e=[0,2,1][t.length%3],n=t.charCodeAt(0)<<16|(t.length>1?t.charCodeAt(1):0)<<8|(t.length>2?t.charCodeAt(2):0);return[q.charAt(n>>>18),q.charAt(n>>>12&63),e>=2?"=":q.charAt(n>>>6&63),e>=1?"=":q.charAt(63&n)].join("")},J=window.btoa||function(t){return t.replace(/[\s\S]{1,3}/g,X)},W=class{constructor(t,e,n,i){this.clear=e,this.timer=t((()=>{this.timer&&(this.timer=i(this.timer))}),n)}isRunning(){return null!==this.timer}ensureAborted(){this.timer&&(this.clear(this.timer),this.timer=null)}};function G(t){window.clearTimeout(t)}function Y(t){window.clearInterval(t)}class K extends W{constructor(t,e){super(setTimeout,G,t,(function(t){return e(),null}))}}class Q extends W{constructor(t,e){super(setInterval,Y,t,(function(t){return e(),t}))}}var V={now:()=>Date.now?Date.now():(new Date).valueOf(),defer:t=>new K(0,t),method(t){for(var e=arguments.length,n=new Array(e>1?e-1:0),i=1;i{window.console&&window.console.log&&window.console.log(t)}}debug(){for(var t=arguments.length,e=new Array(t),n=0;n1?e-1:0),i=1;ie!==t)),this):(this.global_callbacks=[],this)}unbind_all(){return this.unbind(),this.unbind_global(),this}emit(t,e,n){for(var i=0;i0)for(i=0;i{this.onError(t),this.changeState("closed")})),!1}return this.bindListeners(),dt.debug("Connecting",{transport:this.name,url:t}),this.changeState("connecting"),!0}close(){return!!this.socket&&(this.socket.close(),!0)}send(t){return"open"===this.state&&(Z.defer((()=>{this.socket&&this.socket.send(t)})),!0)}ping(){"open"===this.state&&this.supportsPing()&&this.socket.ping()}onOpen(){this.hooks.beforeOpen&&this.hooks.beforeOpen(this.socket,this.hooks.urls.getPath(this.key,this.options)),this.changeState("open"),this.socket.onopen=void 0}onError(t){this.emit("error",{type:"WebSocketError",error:t}),this.timeline.error(this.buildTimelineMessage({error:t.toString()}))}onClose(t){t?this.changeState("closed",{code:t.code,reason:t.reason,wasClean:t.wasClean}):this.changeState("closed"),this.unbindListeners(),this.socket=void 0}onMessage(t){this.emit("message",t)}onActivity(){this.emit("activity")}bindListeners(){this.socket.onopen=()=>{this.onOpen()},this.socket.onerror=t=>{this.onError(t)},this.socket.onclose=t=>{this.onClose(t)},this.socket.onmessage=t=>{this.onMessage(t)},this.supportsPing()&&(this.socket.onactivity=()=>{this.onActivity()})}unbindListeners(){this.socket&&(this.socket.onopen=void 0,this.socket.onerror=void 0,this.socket.onclose=void 0,this.socket.onmessage=void 0,this.supportsPing()&&(this.socket.onactivity=void 0))}changeState(t,e){this.state=t,this.timeline.info(this.buildTimelineMessage({state:t,params:e})),this.emit(t,e)}buildTimelineMessage(t){return tt({cid:this.id},t)}}class Pt{constructor(t){this.hooks=t}isSupported(t){return this.hooks.isSupported(t)}createConnection(t,e,n,i){return new Tt(this.hooks,t,e,n,i)}}var xt=new Pt({urls:yt,handlesActivityChecks:!1,supportsPing:!1,isInitialized:function(){return Boolean(Ce.getWebSocketAPI())},isSupported:function(){return Boolean(Ce.getWebSocketAPI())},getSocket:function(t){return Ce.createWebSocket(t)}}),Et={urls:wt,handlesActivityChecks:!1,supportsPing:!0,isInitialized:function(){return!0}},Ot=tt({getSocket:function(t){return Ce.HTTPFactory.createStreamingSocket(t)}},Et),Lt=tt({getSocket:function(t){return Ce.HTTPFactory.createPollingSocket(t)}},Et),At={isSupported:function(){return Ce.isXHRSupported()}},Rt={ws:xt,xhr_streaming:new Pt(tt({},Ot,At)),xhr_polling:new Pt(tt({},Lt,At))},It=new Pt({file:"sockjs",urls:St,handlesActivityChecks:!0,supportsPing:!1,isSupported:function(){return!0},isInitialized:function(){return void 0!==window.SockJS},getSocket:function(t,e){return new window.SockJS(t,null,{js_path:P.getPath("sockjs",{useTLS:e.useTLS}),ignore_null_origin:e.ignoreNullOrigin})},beforeOpen:function(t,e){t.send(JSON.stringify({path:e}))}}),Dt={isSupported:function(t){return Ce.isXDRSupported(t.useTLS)}},jt=new Pt(tt({},Ot,Dt)),Nt=new Pt(tt({},Lt,Dt));Rt.xdr_streaming=jt,Rt.xdr_polling=Nt,Rt.sockjs=It;var Ut=Rt,Ht=new class extends Ct{constructor(){super();var t=this;void 0!==window.addEventListener&&(window.addEventListener("online",(function(){t.emit("online")}),!1),window.addEventListener("offline",(function(){t.emit("offline")}),!1))}isOnline(){return void 0===window.navigator.onLine||window.navigator.onLine}};class Mt{constructor(t,e,n){this.manager=t,this.transport=e,this.minPingDelay=n.minPingDelay,this.maxPingDelay=n.maxPingDelay,this.pingDelay=void 0}createConnection(t,e,n,i){i=tt({},i,{activityTimeout:this.pingDelay});var r=this.transport.createConnection(t,e,n,i),s=null,o=function(){r.unbind("open",o),r.bind("closed",a),s=Z.now()},a=t=>{if(r.unbind("closed",a),1002===t.code||1003===t.code)this.manager.reportDeath();else if(!t.wasClean&&s){var e=Z.now()-s;e<2*this.maxPingDelay&&(this.manager.reportDeath(),this.pingDelay=Math.max(e/2,this.minPingDelay))}};return r.bind("open",o),r}isSupported(t){return this.manager.isAlive()&&this.transport.isSupported(t)}}const zt={decodeMessage:function(t){try{var e=JSON.parse(t.data),n=e.data;if("string"==typeof n)try{n=JSON.parse(e.data)}catch(t){}var i={event:e.event,channel:e.channel,data:n};return e.user_id&&(i.user_id=e.user_id),i}catch(e){throw{type:"MessageParseError",error:e,data:t.data}}},encodeMessage:function(t){return JSON.stringify(t)},processHandshake:function(t){var e=zt.decodeMessage(t);if("pusher:connection_established"===e.event){if(!e.data.activity_timeout)throw"No activity timeout specified in handshake";return{action:"connected",id:e.data.socket_id,activityTimeout:1e3*e.data.activity_timeout}}if("pusher:error"===e.event)return{action:this.getCloseAction(e.data),error:this.getCloseError(e.data)};throw"Invalid handshake"},getCloseAction:function(t){return t.code<4e3?t.code>=1002&&t.code<=1004?"backoff":null:4e3===t.code?"tls_only":t.code<4100?"refused":t.code<4200?"backoff":t.code<4300?"retry":"refused"},getCloseError:function(t){return 1e3!==t.code&&1001!==t.code?{type:"PusherError",data:{code:t.code,message:t.reason||t.message}}:null}};var qt=zt;class Bt extends Ct{constructor(t,e){super(),this.id=t,this.transport=e,this.activityTimeout=e.activityTimeout,this.bindListeners()}handlesActivityChecks(){return this.transport.handlesActivityChecks()}send(t){return this.transport.send(t)}send_event(t,e,n){var i={event:t,data:e};return n&&(i.channel=n),dt.debug("Event sent",i),this.send(qt.encodeMessage(i))}ping(){this.transport.supportsPing()?this.transport.ping():this.send_event("pusher:ping",{})}close(){this.transport.close()}bindListeners(){var t={message:t=>{var e;try{e=qt.decodeMessage(t)}catch(e){this.emit("error",{type:"MessageParseError",error:e,data:t.data})}if(void 0!==e){switch(dt.debug("Event recd",e),e.event){case"pusher:error":this.emit("error",{type:"PusherError",data:e.data});break;case"pusher:ping":this.emit("ping");break;case"pusher:pong":this.emit("pong")}this.emit("message",e)}},activity:()=>{this.emit("activity")},error:t=>{this.emit("error",t)},closed:t=>{e(),t&&t.code&&this.handleCloseEvent(t),this.transport=null,this.emit("closed")}},e=()=>{it(t,((t,e)=>{this.transport.unbind(e,t)}))};it(t,((t,e)=>{this.transport.bind(e,t)}))}handleCloseEvent(t){var e=qt.getCloseAction(t),n=qt.getCloseError(t);n&&this.emit("error",n),e&&this.emit(e,{action:e,error:n})}}class $t{constructor(t,e){this.transport=t,this.callback=e,this.bindListeners()}close(){this.unbindListeners(),this.transport.close()}bindListeners(){this.onMessage=t=>{var e;this.unbindListeners();try{e=qt.processHandshake(t)}catch(t){return this.finish("error",{error:t}),void this.transport.close()}"connected"===e.action?this.finish("connected",{connection:new Bt(e.id,this.transport),activityTimeout:e.activityTimeout}):(this.finish(e.action,{error:e.error}),this.transport.close())},this.onClosed=t=>{this.unbindListeners();var e=qt.getCloseAction(t)||"backoff",n=qt.getCloseError(t);this.finish(e,{error:n})},this.transport.bind("message",this.onMessage),this.transport.bind("closed",this.onClosed)}unbindListeners(){this.transport.unbind("message",this.onMessage),this.transport.unbind("closed",this.onClosed)}finish(t,e){this.callback(tt({transport:this.transport,action:t},e))}}class Ft{constructor(t,e){this.timeline=t,this.options=e||{}}send(t,e){this.timeline.isEmpty()||this.timeline.send(Ce.TimelineTransport.getAgent(this,t),e)}}class Xt extends Ct{constructor(t,e){super((function(e,n){dt.debug("No callbacks on "+t+" for "+e)})),this.name=t,this.pusher=e,this.subscribed=!1,this.subscriptionPending=!1,this.subscriptionCancelled=!1}authorize(t,e){return e(null,{auth:""})}trigger(t,e){if(0!==t.indexOf("client-"))throw new L("Event '"+t+"' does not start with 'client-'");if(!this.subscribed){var n=O("triggeringClientEvents");dt.warn(`Client event triggered before channel 'subscription_succeeded' event . ${n}`)}return this.pusher.send_event(t,e,this.name)}disconnect(){this.subscribed=!1,this.subscriptionPending=!1}handleEvent(t){var e=t.event,n=t.data;"pusher_internal:subscription_succeeded"===e?this.handleSubscriptionSucceededEvent(t):"pusher_internal:subscription_count"===e?this.handleSubscriptionCountEvent(t):0!==e.indexOf("pusher_internal:")&&this.emit(e,n,{})}handleSubscriptionSucceededEvent(t){this.subscriptionPending=!1,this.subscribed=!0,this.subscriptionCancelled?this.pusher.unsubscribe(this.name):this.emit("pusher:subscription_succeeded",t.data)}handleSubscriptionCountEvent(t){t.data.subscription_count&&(this.subscriptionCount=t.data.subscription_count),this.emit("pusher:subscription_count",t.data)}subscribe(){this.subscribed||(this.subscriptionPending=!0,this.subscriptionCancelled=!1,this.authorize(this.pusher.connection.socket_id,((t,e)=>{t?(this.subscriptionPending=!1,dt.error(t.toString()),this.emit("pusher:subscription_error",Object.assign({},{type:"AuthError",error:t.message},t instanceof H?{status:t.status}:{}))):this.pusher.send_event("pusher:subscribe",{auth:e.auth,channel_data:e.channel_data,channel:this.name})})))}unsubscribe(){this.subscribed=!1,this.pusher.send_event("pusher:unsubscribe",{channel:this.name})}cancelSubscription(){this.subscriptionCancelled=!0}reinstateSubscription(){this.subscriptionCancelled=!1}}class Jt extends Xt{authorize(t,e){return this.pusher.config.channelAuthorizer({channelName:this.name,socketId:t},e)}}class Wt{constructor(){this.reset()}get(t){return Object.prototype.hasOwnProperty.call(this.members,t)?{id:t,info:this.members[t]}:null}each(t){it(this.members,((e,n)=>{t(this.get(n))}))}setMyID(t){this.myID=t}onSubscription(t){this.members=t.presence.hash,this.count=t.presence.count,this.me=this.get(this.myID)}addMember(t){return null===this.get(t.user_id)&&this.count++,this.members[t.user_id]=t.user_info,this.get(t.user_id)}removeMember(t){var e=this.get(t.user_id);return e&&(delete this.members[t.user_id],this.count--),e}reset(){this.members={},this.count=0,this.myID=null,this.me=null}}class Gt extends Jt{constructor(t,e){super(t,e),this.members=new Wt}authorize(t,e){super.authorize(t,((t,n)=>{return i=this,r=void 0,o=function*(){if(!t)if(null!=n.channel_data){var i=JSON.parse(n.channel_data);this.members.setMyID(i.user_id)}else{if(yield this.pusher.user.signinDonePromise,null==this.pusher.user.user_data){let t=O("authorizationEndpoint");return dt.error(`Invalid auth response for channel '${this.name}', expected 'channel_data' field. ${t}, or the user should be signed in.`),void e("Invalid auth response")}this.members.setMyID(this.pusher.user.user_data.id)}e(t,n)},new((s=void 0)||(s=Promise))((function(t,e){function n(t){try{c(o.next(t))}catch(t){e(t)}}function a(t){try{c(o.throw(t))}catch(t){e(t)}}function c(e){var i;e.done?t(e.value):(i=e.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,a)}c((o=o.apply(i,r||[])).next())}));var i,r,s,o}))}handleEvent(t){var e=t.event;if(0===e.indexOf("pusher_internal:"))this.handleInternalEvent(t);else{var n=t.data,i={};t.user_id&&(i.user_id=t.user_id),this.emit(e,n,i)}}handleInternalEvent(t){var e=t.event,n=t.data;switch(e){case"pusher_internal:subscription_succeeded":this.handleSubscriptionSucceededEvent(t);break;case"pusher_internal:subscription_count":this.handleSubscriptionCountEvent(t);break;case"pusher_internal:member_added":var i=this.members.addMember(n);this.emit("pusher:member_added",i);break;case"pusher_internal:member_removed":var r=this.members.removeMember(n);r&&this.emit("pusher:member_removed",r)}}handleSubscriptionSucceededEvent(t){this.subscriptionPending=!1,this.subscribed=!0,this.subscriptionCancelled?this.pusher.unsubscribe(this.name):(this.members.onSubscription(t.data),this.emit("pusher:subscription_succeeded",this.members))}disconnect(){this.members.reset(),super.disconnect()}}var Yt=n(1),Kt=n(0);class Qt extends Jt{constructor(t,e,n){super(t,e),this.key=null,this.nacl=n}authorize(t,e){super.authorize(t,((t,n)=>{if(t)return void e(t,n);let i=n.shared_secret;i?(this.key=Object(Kt.decode)(i),delete n.shared_secret,e(null,n)):e(new Error(`No shared_secret key in auth payload for encrypted channel: ${this.name}`),null)}))}trigger(t,e){throw new j("Client events are not currently supported for encrypted channels")}handleEvent(t){var e=t.event,n=t.data;0!==e.indexOf("pusher_internal:")&&0!==e.indexOf("pusher:")?this.handleEncryptedEvent(e,n):super.handleEvent(t)}handleEncryptedEvent(t,e){if(!this.key)return void dt.debug("Received encrypted event before key has been retrieved from the authEndpoint");if(!e.ciphertext||!e.nonce)return void dt.error("Unexpected format for encrypted event, expected object with `ciphertext` and `nonce` fields, got: "+e);let n=Object(Kt.decode)(e.ciphertext);if(n.length{e?dt.error(`Failed to make a request to the authEndpoint: ${s}. Unable to fetch new key, so dropping encrypted event`):(r=this.nacl.secretbox.open(n,i,this.key),null!==r?this.emit(t,this.getDataToEmit(r)):dt.error("Failed to decrypt event with new key. Dropping encrypted event"))}));this.emit(t,this.getDataToEmit(r))}getDataToEmit(t){let e=Object(Yt.decode)(t);try{return JSON.parse(e)}catch(t){return e}}}class Vt extends Ct{constructor(t,e){super(),this.state="initialized",this.connection=null,this.key=t,this.options=e,this.timeline=this.options.timeline,this.usingTLS=this.options.useTLS,this.errorCallbacks=this.buildErrorCallbacks(),this.connectionCallbacks=this.buildConnectionCallbacks(this.errorCallbacks),this.handshakeCallbacks=this.buildHandshakeCallbacks(this.errorCallbacks);var n=Ce.getNetwork();n.bind("online",(()=>{this.timeline.info({netinfo:"online"}),"connecting"!==this.state&&"unavailable"!==this.state||this.retryIn(0)})),n.bind("offline",(()=>{this.timeline.info({netinfo:"offline"}),this.connection&&this.sendActivityCheck()})),this.updateStrategy()}connect(){this.connection||this.runner||(this.strategy.isSupported()?(this.updateState("connecting"),this.startConnecting(),this.setUnavailableTimer()):this.updateState("failed"))}send(t){return!!this.connection&&this.connection.send(t)}send_event(t,e,n){return!!this.connection&&this.connection.send_event(t,e,n)}disconnect(){this.disconnectInternally(),this.updateState("disconnected")}isUsingTLS(){return this.usingTLS}startConnecting(){var t=(e,n)=>{e?this.runner=this.strategy.connect(0,t):"error"===n.action?(this.emit("error",{type:"HandshakeError",error:n.error}),this.timeline.error({handshakeError:n.error})):(this.abortConnecting(),this.handshakeCallbacks[n.action](n))};this.runner=this.strategy.connect(0,t)}abortConnecting(){this.runner&&(this.runner.abort(),this.runner=null)}disconnectInternally(){this.abortConnecting(),this.clearRetryTimer(),this.clearUnavailableTimer(),this.connection&&this.abandonConnection().close()}updateStrategy(){this.strategy=this.options.getStrategy({key:this.key,timeline:this.timeline,useTLS:this.usingTLS})}retryIn(t){this.timeline.info({action:"retry",delay:t}),t>0&&this.emit("connecting_in",Math.round(t/1e3)),this.retryTimer=new K(t||0,(()=>{this.disconnectInternally(),this.connect()}))}clearRetryTimer(){this.retryTimer&&(this.retryTimer.ensureAborted(),this.retryTimer=null)}setUnavailableTimer(){this.unavailableTimer=new K(this.options.unavailableTimeout,(()=>{this.updateState("unavailable")}))}clearUnavailableTimer(){this.unavailableTimer&&this.unavailableTimer.ensureAborted()}sendActivityCheck(){this.stopActivityCheck(),this.connection.ping(),this.activityTimer=new K(this.options.pongTimeout,(()=>{this.timeline.error({pong_timed_out:this.options.pongTimeout}),this.retryIn(0)}))}resetActivityCheck(){this.stopActivityCheck(),this.connection&&!this.connection.handlesActivityChecks()&&(this.activityTimer=new K(this.activityTimeout,(()=>{this.sendActivityCheck()})))}stopActivityCheck(){this.activityTimer&&this.activityTimer.ensureAborted()}buildConnectionCallbacks(t){return tt({},t,{message:t=>{this.resetActivityCheck(),this.emit("message",t)},ping:()=>{this.send_event("pusher:pong",{})},activity:()=>{this.resetActivityCheck()},error:t=>{this.emit("error",t)},closed:()=>{this.abandonConnection(),this.shouldRetry()&&this.retryIn(1e3)}})}buildHandshakeCallbacks(t){return tt({},t,{connected:t=>{this.activityTimeout=Math.min(this.options.activityTimeout,t.activityTimeout,t.connection.activityTimeout||1/0),this.clearUnavailableTimer(),this.setConnection(t.connection),this.socket_id=this.connection.id,this.updateState("connected",{socket_id:this.socket_id})}})}buildErrorCallbacks(){let t=t=>e=>{e.error&&this.emit("error",{type:"WebSocketError",error:e.error}),t(e)};return{tls_only:t((()=>{this.usingTLS=!0,this.updateStrategy(),this.retryIn(0)})),refused:t((()=>{this.disconnect()})),backoff:t((()=>{this.retryIn(1e3)})),retry:t((()=>{this.retryIn(0)}))}}setConnection(t){for(var e in this.connection=t,this.connectionCallbacks)this.connection.bind(e,this.connectionCallbacks[e]);this.resetActivityCheck()}abandonConnection(){if(this.connection){for(var t in this.stopActivityCheck(),this.connectionCallbacks)this.connection.unbind(t,this.connectionCallbacks[t]);var e=this.connection;return this.connection=null,e}}updateState(t,e){var n=this.state;if(this.state=t,n!==t){var i=t;"connected"===i&&(i+=" with new socket ID "+e.socket_id),dt.debug("State changed",n+" -> "+i),this.timeline.info({state:t,params:e}),this.emit("state_change",{previous:n,current:t}),this.emit(t,e)}}shouldRetry(){return"connecting"===this.state||"connected"===this.state}}class Zt{constructor(){this.channels={}}add(t,e){return this.channels[t]||(this.channels[t]=function(t,e){if(0===t.indexOf("private-encrypted-")){if(e.config.nacl)return te.createEncryptedChannel(t,e,e.config.nacl);let n="Tried to subscribe to a private-encrypted- channel but no nacl implementation available",i=O("encryptedChannelSupport");throw new j(`${n}. ${i}`)}if(0===t.indexOf("private-"))return te.createPrivateChannel(t,e);if(0===t.indexOf("presence-"))return te.createPresenceChannel(t,e);if(0===t.indexOf("#"))throw new A('Cannot create a channel with name "'+t+'".');return te.createChannel(t,e)}(t,e)),this.channels[t]}all(){return t=this.channels,e=[],it(t,(function(t){e.push(t)})),e;var t,e}find(t){return this.channels[t]}remove(t){var e=this.channels[t];return delete this.channels[t],e}disconnect(){it(this.channels,(function(t){t.disconnect()}))}}var te={createChannels:()=>new Zt,createConnectionManager:(t,e)=>new Vt(t,e),createChannel:(t,e)=>new Xt(t,e),createPrivateChannel:(t,e)=>new Jt(t,e),createPresenceChannel:(t,e)=>new Gt(t,e),createEncryptedChannel:(t,e,n)=>new Qt(t,e,n),createTimelineSender:(t,e)=>new Ft(t,e),createHandshake:(t,e)=>new $t(t,e),createAssistantToTheTransportManager:(t,e,n)=>new Mt(t,e,n)};class ee{constructor(t){this.options=t||{},this.livesLeft=this.options.lives||1/0}getAssistant(t){return te.createAssistantToTheTransportManager(this,t,{minPingDelay:this.options.minPingDelay,maxPingDelay:this.options.maxPingDelay})}isAlive(){return this.livesLeft>0}reportDeath(){this.livesLeft-=1}}class ne{constructor(t,e){this.strategies=t,this.loop=Boolean(e.loop),this.failFast=Boolean(e.failFast),this.timeout=e.timeout,this.timeoutLimit=e.timeoutLimit}isSupported(){return ht(this.strategies,Z.method("isSupported"))}connect(t,e){var n=this.strategies,i=0,r=this.timeout,s=null,o=(a,c)=>{c?e(null,c):(i+=1,this.loop&&(i%=n.length),i0&&(r=new K(n.timeout,(function(){s.abort(),i(!0)}))),s=t.connect(e,(function(t,e){t&&r&&r.isRunning()&&!n.failFast||(r&&r.ensureAborted(),i(t,e))})),{abort:function(){r&&r.ensureAborted(),s.abort()},forceMinPriority:function(t){s.forceMinPriority(t)}}}}class ie{constructor(t){this.strategies=t}isSupported(){return ht(this.strategies,Z.method("isSupported"))}connect(t,e){return function(t,n){var i=ot(t,(function(t,i,r,s){return t.connect(n,function(t,n){return function(i,r){n[t].error=i,i?function(t){return function(t,e){for(var n=0;n=Z.now()){var o=this.transports[i.transport];o&&(["ws","wss"].includes(i.transport)||r>3?(this.timeline.info({cached:!0,transport:i.transport,latency:i.latency}),s.push(new ne([o],{timeout:2*i.latency+1e3,failFast:!0}))):r++)}var a=Z.now(),c=s.pop().connect(t,(function i(o,h){o?(ae(n),s.length>0?(a=Z.now(),c=s.pop().connect(t,i)):e(o)):(function(t,e,n,i){var r=Ce.getLocalStorage();if(r)try{r[oe(t)]=lt({timestamp:Z.now(),transport:e,latency:n,cacheSkipCount:i})}catch(t){}}(n,h.transport.name,Z.now()-a,r),e(null,h))}));return{abort:function(){c.abort()},forceMinPriority:function(e){t=e,c&&c.forceMinPriority(e)}}}}function oe(t){return"pusherTransport"+(t?"TLS":"NonTLS")}function ae(t){var e=Ce.getLocalStorage();if(e)try{delete e[oe(t)]}catch(t){}}class ce{constructor(t,e){let{delay:n}=e;this.strategy=t,this.options={delay:n}}isSupported(){return this.strategy.isSupported()}connect(t,e){var n,i=this.strategy,r=new K(this.options.delay,(function(){n=i.connect(t,e)}));return{abort:function(){r.ensureAborted(),n&&n.abort()},forceMinPriority:function(e){t=e,n&&n.forceMinPriority(e)}}}}class he{constructor(t,e,n){this.test=t,this.trueBranch=e,this.falseBranch=n}isSupported(){return(this.test()?this.trueBranch:this.falseBranch).isSupported()}connect(t,e){return(this.test()?this.trueBranch:this.falseBranch).connect(t,e)}}class ue{constructor(t){this.strategy=t}isSupported(){return this.strategy.isSupported()}connect(t,e){var n=this.strategy.connect(t,(function(t,i){i&&n.abort(),e(t,i)}));return n}}function le(t){return function(){return t.isSupported()}}var de,pe={getRequest:function(t){var e=new window.XDomainRequest;return e.ontimeout=function(){t.emit("error",new R),t.close()},e.onerror=function(e){t.emit("error",e),t.close()},e.onprogress=function(){e.responseText&&e.responseText.length>0&&t.onChunk(200,e.responseText)},e.onload=function(){e.responseText&&e.responseText.length>0&&t.onChunk(200,e.responseText),t.emit("finished",200),t.close()},e},abortRequest:function(t){t.ontimeout=t.onerror=t.onprogress=t.onload=null,t.abort()}};class fe extends Ct{constructor(t,e,n){super(),this.hooks=t,this.method=e,this.url=n}start(t){this.position=0,this.xhr=this.hooks.getRequest(this),this.unloader=()=>{this.close()},Ce.addUnloadListener(this.unloader),this.xhr.open(this.method,this.url,!0),this.xhr.setRequestHeader&&this.xhr.setRequestHeader("Content-Type","application/json"),this.xhr.send(t)}close(){this.unloader&&(Ce.removeUnloadListener(this.unloader),this.unloader=null),this.xhr&&(this.hooks.abortRequest(this.xhr),this.xhr=null)}onChunk(t,e){for(;;){var n=this.advanceBuffer(e);if(!n)break;this.emit("chunk",{status:t,data:n})}this.isBufferTooLong(e)&&this.emit("buffer_too_long")}advanceBuffer(t){var e=t.slice(this.position),n=e.indexOf("\n");return-1!==n?(this.position+=n+1,e.slice(0,n)):null}isBufferTooLong(t){return this.position===t.length&&t.length>262144}}!function(t){t[t.CONNECTING=0]="CONNECTING",t[t.OPEN=1]="OPEN",t[t.CLOSED=3]="CLOSED"}(de||(de={}));var ge=de,me=1;function be(t){var e=-1===t.indexOf("?")?"?":"&";return t+e+"t="+ +new Date+"&n="+me++}function ve(t){return Ce.randomInt(t)}var ye,we=class{constructor(t,e){this.hooks=t,this.session=ve(1e3)+"/"+function(){for(var t=[],e=0;e<8;e++)t.push(ve(32).toString(32));return t.join("")}(),this.location=function(t){var e=/([^\?]*)\/*(\??.*)/.exec(t);return{base:e[1],queryString:e[2]}}(e),this.readyState=ge.CONNECTING,this.openStream()}send(t){return this.sendRaw(JSON.stringify([t]))}ping(){this.hooks.sendHeartbeat(this)}close(t,e){this.onClose(t,e,!0)}sendRaw(t){if(this.readyState!==ge.OPEN)return!1;try{return Ce.createSocketRequest("POST",be((e=this.location,n=this.session,e.base+"/"+n+"/xhr_send"))).start(t),!0}catch(t){return!1}var e,n}reconnect(){this.closeStream(),this.openStream()}onClose(t,e,n){this.closeStream(),this.readyState=ge.CLOSED,this.onclose&&this.onclose({code:t,reason:e,wasClean:n})}onChunk(t){var e;if(200===t.status)switch(this.readyState===ge.OPEN&&this.onActivity(),t.data.slice(0,1)){case"o":e=JSON.parse(t.data.slice(1)||"{}"),this.onOpen(e);break;case"a":e=JSON.parse(t.data.slice(1)||"[]");for(var n=0;n{this.onChunk(t)})),this.stream.bind("finished",(t=>{this.hooks.onFinished(this,t)})),this.stream.bind("buffer_too_long",(()=>{this.reconnect()}));try{this.stream.start()}catch(t){Z.defer((()=>{this.onError(t),this.onClose(1006,"Could not start streaming",!1)}))}}closeStream(){this.stream&&(this.stream.unbind_all(),this.stream.close(),this.stream=null)}},Se={getReceiveURL:function(t,e){return t.base+"/"+e+"/xhr_streaming"+t.queryString},onHeartbeat:function(t){t.sendRaw("[]")},sendHeartbeat:function(t){t.sendRaw("[]")},onFinished:function(t,e){t.onClose(1006,"Connection interrupted ("+e+")",!1)}},_e={getReceiveURL:function(t,e){return t.base+"/"+e+"/xhr"+t.queryString},onHeartbeat:function(){},sendHeartbeat:function(t){t.sendRaw("[]")},onFinished:function(t,e){200===e?t.reconnect():t.onClose(1006,"Connection interrupted ("+e+")",!1)}},ke={getRequest:function(t){var e=new(Ce.getXHRAPI());return e.onreadystatechange=e.onprogress=function(){switch(e.readyState){case 3:e.responseText&&e.responseText.length>0&&t.onChunk(e.status,e.responseText);break;case 4:e.responseText&&e.responseText.length>0&&t.onChunk(e.status,e.responseText),t.emit("finished",e.status),t.close()}},e},abortRequest:function(t){t.onreadystatechange=null,t.abort()}},Ce={nextAuthCallbackID:1,auth_callbacks:{},ScriptReceivers:r,DependenciesReceivers:T,getDefaultStrategy:function(t,e,n){var i={};function r(e,r,s,o,a){var c=n(t,e,r,s,o,a);return i[e]=c,c}var s,o=Object.assign({},e,{hostNonTLS:t.wsHost+":"+t.wsPort,hostTLS:t.wsHost+":"+t.wssPort,httpPath:t.wsPath}),a=Object.assign({},o,{useTLS:!0}),c=Object.assign({},e,{hostNonTLS:t.httpHost+":"+t.httpPort,hostTLS:t.httpHost+":"+t.httpsPort,httpPath:t.httpPath}),h={loop:!0,timeout:15e3,timeoutLimit:6e4},u=new ee({minPingDelay:1e4,maxPingDelay:t.activityTimeout}),l=new ee({lives:2,minPingDelay:1e4,maxPingDelay:t.activityTimeout}),d=r("ws","ws",3,o,u),p=r("wss","ws",3,a,u),f=r("sockjs","sockjs",1,c),g=r("xhr_streaming","xhr_streaming",1,c,l),m=r("xdr_streaming","xdr_streaming",1,c,l),b=r("xhr_polling","xhr_polling",1,c),v=r("xdr_polling","xdr_polling",1,c),y=new ne([d],h),w=new ne([p],h),S=new ne([f],h),_=new ne([new he(le(g),g,m)],h),k=new ne([new he(le(b),b,v)],h),C=new ne([new he(le(_),new ie([_,new ce(k,{delay:4e3})]),k)],h),T=new he(le(C),C,S);return s=e.useTLS?new ie([y,new ce(T,{delay:2e3})]):new ie([y,new ce(w,{delay:2e3}),new ce(T,{delay:5e3})]),new se(new ue(new he(le(d),s,T)),i,{ttl:18e5,timeline:e.timeline,useTLS:e.useTLS})},Transports:Ut,transportConnectionInitializer:function(){var t=this;t.timeline.info(t.buildTimelineMessage({transport:t.name+(t.options.useTLS?"s":"")})),t.hooks.isInitialized()?t.changeState("initialized"):t.hooks.file?(t.changeState("initializing"),P.load(t.hooks.file,{useTLS:t.options.useTLS},(function(e,n){t.hooks.isInitialized()?(t.changeState("initialized"),n(!0)):(e&&t.onError(e),t.onClose(),n(!1))}))):t.onClose()},HTTPFactory:{createStreamingSocket(t){return this.createSocket(Se,t)},createPollingSocket(t){return this.createSocket(_e,t)},createSocket:(t,e)=>new we(t,e),createXHR(t,e){return this.createRequest(ke,t,e)},createRequest:(t,e,n)=>new fe(t,e,n),createXDR:function(t,e){return this.createRequest(pe,t,e)}},TimelineTransport:mt,getXHRAPI:()=>window.XMLHttpRequest,getWebSocketAPI:()=>window.WebSocket||window.MozWebSocket,setup(t){window.Pusher=t;var e=()=>{this.onDocumentBody(t.ready)};window.JSON?e():P.load("json2",{},e)},getDocument:()=>document,getProtocol(){return this.getDocument().location.protocol},getAuthorizers:()=>({ajax:M,jsonp:pt}),onDocumentBody(t){document.body?t():setTimeout((()=>{this.onDocumentBody(t)}),0)},createJSONPRequest:(t,e)=>new gt(t,e),createScriptRequest:t=>new ft(t),getLocalStorage(){try{return window.localStorage}catch(t){return}},createXHR(){return this.getXHRAPI()?this.createXMLHttpRequest():this.createMicrosoftXHR()},createXMLHttpRequest(){return new(this.getXHRAPI())},createMicrosoftXHR:()=>new ActiveXObject("Microsoft.XMLHTTP"),getNetwork:()=>Ht,createWebSocket(t){return new(this.getWebSocketAPI())(t)},createSocketRequest(t,e){if(this.isXHRSupported())return this.HTTPFactory.createXHR(t,e);if(this.isXDRSupported(0===e.indexOf("https:")))return this.HTTPFactory.createXDR(t,e);throw"Cross-origin HTTP requests are not supported"},isXHRSupported(){var t=this.getXHRAPI();return Boolean(t)&&void 0!==(new t).withCredentials},isXDRSupported(t){var e=t?"https:":"http:",n=this.getProtocol();return Boolean(window.XDomainRequest)&&n===e},addUnloadListener(t){void 0!==window.addEventListener?window.addEventListener("unload",t,!1):void 0!==window.attachEvent&&window.attachEvent("onunload",t)},removeUnloadListener(t){void 0!==window.addEventListener?window.removeEventListener("unload",t,!1):void 0!==window.detachEvent&&window.detachEvent("onunload",t)},randomInt:t=>Math.floor((window.crypto||window.msCrypto).getRandomValues(new Uint32Array(1))[0]/Math.pow(2,32)*t)};!function(t){t[t.ERROR=3]="ERROR",t[t.INFO=6]="INFO",t[t.DEBUG=7]="DEBUG"}(ye||(ye={}));var Te=ye;class Pe{constructor(t,e,n){this.key=t,this.session=e,this.events=[],this.options=n||{},this.sent=0,this.uniqueID=0}log(t,e){t<=this.options.level&&(this.events.push(tt({},e,{timestamp:Z.now()})),this.options.limit&&this.events.length>this.options.limit&&this.events.shift())}error(t){this.log(Te.ERROR,t)}info(t){this.log(Te.INFO,t)}debug(t){this.log(Te.DEBUG,t)}isEmpty(){return 0===this.events.length}send(t,e){var n=tt({session:this.session,bundle:this.sent+1,key:this.key,lib:"js",version:this.options.version,cluster:this.options.cluster,features:this.options.features,timeline:this.events},this.options.params);return this.events=[],t(n,((t,n)=>{t||this.sent++,e&&e(t,n)})),!0}generateUniqueID(){return this.uniqueID++,this.uniqueID}}class xe{constructor(t,e,n,i){this.name=t,this.priority=e,this.transport=n,this.options=i||{}}isSupported(){return this.transport.isSupported({useTLS:this.options.useTLS})}connect(t,e){if(!this.isSupported())return Ee(new U,e);if(this.priority{n||(h(),r?r.close():i.close())},forceMinPriority:t=>{n||this.priority{if(void 0===Ce.getAuthorizers()[t.transport])throw`'${t.transport}' is not a recognized auth transport`;return(e,n)=>{const i=((t,e)=>{var n="socket_id="+encodeURIComponent(t.socketId);for(var i in e.params)n+="&"+encodeURIComponent(i)+"="+encodeURIComponent(e.params[i]);if(null!=e.paramsProvider){let t=e.paramsProvider();for(var i in t)n+="&"+encodeURIComponent(i)+"="+encodeURIComponent(t[i])}return n})(e,t);Ce.getAuthorizers()[t.transport](Ce,i,t,E.UserAuthentication,n)}})(e)}function Ue(t,e){const n=function(t,e){let n;return"channelAuthorization"in t?n=Object.assign(Object.assign({},S),t.channelAuthorization):(n={transport:t.authTransport||m,endpoint:t.authEndpoint||g},"auth"in t&&("params"in t.auth&&(n.params=t.auth.params),"headers"in t.auth&&(n.headers=t.auth.headers)),"authorizer"in t&&(n.customHandler=((t,e,n)=>{const i={authTransport:e.transport,authEndpoint:e.endpoint,auth:{params:e.params,headers:e.headers}};return(e,r)=>{const s=t.channel(e.channelName);n(s,i).authorize(e.socketId,r)}})(e,n,t.authorizer))),n}(t,e);return"customHandler"in n&&null!=n.customHandler?n.customHandler:(t=>{if(void 0===Ce.getAuthorizers()[t.transport])throw`'${t.transport}' is not a recognized auth transport`;return(e,n)=>{const i=((t,e)=>{var n="socket_id="+encodeURIComponent(t.socketId);for(var i in n+="&channel_name="+encodeURIComponent(t.channelName),e.params)n+="&"+encodeURIComponent(i)+"="+encodeURIComponent(e.params[i]);if(null!=e.paramsProvider){let t=e.paramsProvider();for(var i in t)n+="&"+encodeURIComponent(i)+"="+encodeURIComponent(t[i])}return n})(e,t);Ce.getAuthorizers()[t.transport](Ce,i,t,E.ChannelAuthorization,n)}})(n)}class He extends Ct{constructor(t){super((function(t,e){dt.debug(`No callbacks on watchlist events for ${t}`)})),this.pusher=t,this.bindWatchlistInternalEvent()}handleEvent(t){t.data.events.forEach((t=>{this.emit(t.name,t)}))}bindWatchlistInternalEvent(){this.pusher.connection.bind("message",(t=>{"pusher_internal:watchlist_events"===t.event&&this.handleEvent(t)}))}}class Me extends Ct{constructor(t){super((function(t,e){dt.debug("No callbacks on user for "+t)})),this.signin_requested=!1,this.user_data=null,this.serverToUserChannel=null,this.signinDonePromise=null,this._signinDoneResolve=null,this._onAuthorize=(t,e)=>{if(t)return dt.warn(`Error during signin: ${t}`),void this._cleanup();this.pusher.send_event("pusher:signin",{auth:e.auth,user_data:e.user_data})},this.pusher=t,this.pusher.connection.bind("state_change",(t=>{let{previous:e,current:n}=t;"connected"!==e&&"connected"===n&&this._signin(),"connected"===e&&"connected"!==n&&(this._cleanup(),this._newSigninPromiseIfNeeded())})),this.watchlist=new He(t),this.pusher.connection.bind("message",(t=>{"pusher:signin_success"===t.event&&this._onSigninSuccess(t.data),this.serverToUserChannel&&this.serverToUserChannel.name===t.channel&&this.serverToUserChannel.handleEvent(t)}))}signin(){this.signin_requested||(this.signin_requested=!0,this._signin())}_signin(){this.signin_requested&&(this._newSigninPromiseIfNeeded(),"connected"===this.pusher.connection.state&&this.pusher.config.userAuthenticator({socketId:this.pusher.connection.socket_id},this._onAuthorize))}_onSigninSuccess(t){try{this.user_data=JSON.parse(t.user_data)}catch(e){return dt.error(`Failed parsing user data after signin: ${t.user_data}`),void this._cleanup()}if("string"!=typeof this.user_data.id||""===this.user_data.id)return dt.error(`user_data doesn't contain an id. user_data: ${this.user_data}`),void this._cleanup();this._signinDoneResolve(),this._subscribeChannels()}_subscribeChannels(){this.serverToUserChannel=new Xt(`#server-to-user-${this.user_data.id}`,this.pusher),this.serverToUserChannel.bind_global(((t,e)=>{0!==t.indexOf("pusher_internal:")&&0!==t.indexOf("pusher:")&&this.emit(t,e)})),(t=>{t.subscriptionPending&&t.subscriptionCancelled?t.reinstateSubscription():t.subscriptionPending||"connected"!==this.pusher.connection.state||t.subscribe()})(this.serverToUserChannel)}_cleanup(){this.user_data=null,this.serverToUserChannel&&(this.serverToUserChannel.unbind_all(),this.serverToUserChannel.disconnect(),this.serverToUserChannel=null),this.signin_requested&&this._signinDoneResolve()}_newSigninPromiseIfNeeded(){if(!this.signin_requested)return;if(this.signinDonePromise&&!this.signinDonePromise.done)return;const{promise:t,resolve:e,reject:n}=function(){let t,e;return{promise:new Promise(((n,i)=>{t=n,e=i})),resolve:t,reject:e}}();t.done=!1;const i=()=>{t.done=!0};t.then(i).catch(i),this.signinDonePromise=t,this._signinDoneResolve=e}}class ze{static ready(){ze.isReady=!0;for(var t=0,e=ze.instances.length;tCe.getDefaultStrategy(this.config,t,Le),timeline:this.timeline,activityTimeout:this.config.activityTimeout,pongTimeout:this.config.pongTimeout,unavailableTimeout:this.config.unavailableTimeout,useTLS:Boolean(this.config.useTLS)}),this.connection.bind("connected",(()=>{this.subscribeAll(),this.timelineSender&&this.timelineSender.send(this.connection.isUsingTLS())})),this.connection.bind("message",(t=>{var e=0===t.event.indexOf("pusher_internal:");if(t.channel){var n=this.channel(t.channel);n&&n.handleEvent(t)}e||this.global_emitter.emit(t.event,t.data)})),this.connection.bind("connecting",(()=>{this.channels.disconnect()})),this.connection.bind("disconnected",(()=>{this.channels.disconnect()})),this.connection.bind("error",(t=>{dt.warn(t)})),ze.instances.push(this),this.timeline.info({instances:ze.instances.length}),this.user=new Me(this),ze.isReady&&this.connect()}channel(t){return this.channels.find(t)}allChannels(){return this.channels.all()}connect(){if(this.connection.connect(),this.timelineSender&&!this.timelineSenderTimer){var t=this.connection.isUsingTLS(),e=this.timelineSender;this.timelineSenderTimer=new Q(6e4,(function(){e.send(t)}))}}disconnect(){this.connection.disconnect(),this.timelineSenderTimer&&(this.timelineSenderTimer.ensureAborted(),this.timelineSenderTimer=null)}bind(t,e,n){return this.global_emitter.bind(t,e,n),this}unbind(t,e,n){return this.global_emitter.unbind(t,e,n),this}bind_global(t){return this.global_emitter.bind_global(t),this}unbind_global(t){return this.global_emitter.unbind_global(t),this}unbind_all(t){return this.global_emitter.unbind_all(),this}subscribeAll(){var t;for(t in this.channels.channels)this.channels.channels.hasOwnProperty(t)&&this.subscribe(t)}subscribe(t){var e=this.channels.add(t,this);return e.subscriptionPending&&e.subscriptionCancelled?e.reinstateSubscription():e.subscriptionPending||"connected"!==this.connection.state||e.subscribe(),e}unsubscribe(t){var e=this.channels.find(t);e&&e.subscriptionPending?e.cancelSubscription():(e=this.channels.remove(t))&&e.subscribed&&e.unsubscribe()}send_event(t,e,n){return this.connection.send_event(t,e,n)}shouldUseTLS(){return this.config.useTLS}signin(){this.user.signin()}}ze.instances=[],ze.isReady=!1,ze.logToConsole=!1,ze.Runtime=Ce,ze.ScriptReceivers=Ce.ScriptReceivers,ze.DependenciesReceivers=Ce.DependenciesReceivers,ze.auth_callbacks=Ce.auth_callbacks;var qe=e.default=ze;Ce.setup(ze)}])},t.exports=e()}},e={};function n(i){flarum.reg._webpack_runtimes["flarum-pusher"]||=n;var r=e[i];if(void 0!==r)return r.exports;var s=e[i]={exports:{}};return t[i](s,s.exports,n),s.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var i in e)n.o(e,i)&&!n.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:e[i]})},n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{"use strict";var t=n(102),e=n.n(t);const i=flarum.reg.get("core","forum/app");var r=n.n(i);const s=flarum.reg.get("core","common/extend"),o=flarum.reg.get("core","forum/components/DiscussionList");var a=n.n(o);const c=flarum.reg.get("core","forum/components/DiscussionPage");var h=n.n(c);const u=flarum.reg.get("core","forum/components/IndexPage");var l=n.n(u);const d=flarum.reg.get("core","common/components/Button");var p=n.n(d);r().initializers.add("flarum-pusher",(()=>{r().pusher=(async()=>{const t=new(e())(r().forum.attribute("pusherKey"),{authEndpoint:`${r().forum.attribute("apiUrl")}/pusher/auth`,cluster:r().forum.attribute("pusherCluster"),auth:{headers:{"X-CSRF-Token":r().session.csrfToken}},httpHost:r().forum.attribute("pusherHostname"),wsHost:r().forum.attribute("pusherHostname")});return{channels:{main:t.subscribe("public"),user:r().session.user?t.subscribe(`private-user${r().session.user.id()}`):null},pusher:t}})(),r().pushedUpdates=[],(0,s.extend)(a().prototype,"oncreate",(function(){r().pusher.then((t=>{t.pusher.bind("newPost",(t=>{const e=r().discussions.getParams();if(!e.q&&!e.sort&&!e.filter){if(e.tags){const n=r().store.getBy("tags","slug",e.tags),i=n?.id();if(!i||!t.tagIds.includes(i))return}const n=String(t.discussionId);r().current.get("discussion")&&n===r().current.get("discussion").id()||-1!==r().pushedUpdates.indexOf(n)||(r().pushedUpdates.push(n),r().current.matches(l())&&r().setTitleCount(r().pushedUpdates.length),m.redraw())}}))}))})),(0,s.extend)(a().prototype,"onremove",(function(){r().pusher.then((t=>{t.pusher.unbind("newPost")}))})),(0,s.extend)(a().prototype,"view",(function(t){if(r().pushedUpdates){const e=r().pushedUpdates.length;e&&"object"==typeof t&&t&&"children"in t&&t.children instanceof Array&&t.children.unshift(m(p(),{className:"Button Button--block DiscussionList-update",onclick:()=>{this.attrs.state.refresh().then((()=>{this.loadingUpdated=!1,r().pushedUpdates=[],r().setTitleCount(0),m.redraw()})),this.loadingUpdated=!0},loading:this.loadingUpdated},r().translator.trans("flarum-pusher.forum.discussion_list.show_updates_text",{count:e})))}})),(0,s.extend)(h().prototype,"oncreate",(function(){r().pusher.then((t=>{t.pusher.bind("newPost",(t=>{const e=String(t.discussionId),n=this.discussion?.id();if(this.discussion&&n===e&&this.stream){const t=this.discussion.commentCount()??0;r().store.find("discussions",n).then((()=>{this.stream?.update().then(m.redraw),document.hasFocus()||(r().setTitleCount(Math.max(0,(this.discussion?.commentCount()??0)-t)),window.addEventListener("focus",(()=>r().setTitleCount(0)),{once:!0}))}))}}))}))})),(0,s.extend)(h().prototype,"onremove",(function(){r().pusher.then((t=>{t.pusher.unbind("newPost")}))})),(0,s.extend)(l().prototype,"actionItems",(t=>{t.remove("refresh")})),r().pusher.then((t=>{const e=t.channels;e.user&&e.user.bind("notification",(()=>{r().session.user&&r().session.user.pushAttributes({unreadNotificationCount:r().session.user.unreadNotificationCount()??1,newNotificationCount:r().session.user.newNotificationCount()??1}),r().notifications.clear(),m.redraw()}))}))}))})(),module.exports={}})(); 3 | //# sourceMappingURL=forum.js.map --------------------------------------------------------------------------------