Outbound live stream notifications (#1663)

* First pass at browser, discord, twilio notifications

* Commit updated Javascript packages

* Remove twilio notification support

* Email notifications/smtp support

* Fix Firefox notification support, remove chrome checks

* WIP more email work

* Add support for twitter notifications

* Add stream title to discord and twitter notifications

* Update notification registration modal

* Fix hide/show email section

* Commit updated API documentation

* Commit updated Javascript packages

* Fix post-rebase missing var

* Remove unused var

* Handle unsubscribe errors for browser push

* Standardize email config prop names

* Allow overriding go live email template

* Some notifications cleanup

* Commit updated Javascript packages

* Remove email/smtp/mailjet support

* Remove more references to email notifications

Co-authored-by: Owncast <owncast@owncast.online>
This commit is contained in:
Gabe Kangas
2022-03-18 13:33:23 -07:00
committed by GitHub
parent 4e415f7257
commit 4a17f30da8
39 changed files with 2209 additions and 3313 deletions

View File

@@ -25,6 +25,8 @@ import FediverseFollowModal, {
FediverseFollowButton,
} from './components/fediverse-follow-modal.js';
import { NotifyButton, NotifyModal } from './components/notification.js';
import { isPushNotificationSupported } from './notification/registerWeb.js';
import {
addNewlines,
checkUrlPathForDisplay,
@@ -59,8 +61,10 @@ import {
URL_STATUS,
URL_VIEWER_PING,
WIDTH_SINGLE_COL,
USER_VISIT_COUNT_KEY,
} from './utils/constants.js';
import { checkIsModerator } from './utils/chat.js';
import TabBar from './components/tab-bar.js';
export default class App extends Component {
@@ -138,6 +142,8 @@ export default class App extends Component {
this.displayFediverseFollowModal =
this.displayFediverseFollowModal.bind(this);
this.closeFediverseFollowModal = this.closeFediverseFollowModal.bind(this);
this.displayNotificationModal = this.displayNotificationModal.bind(this);
this.closeNotificationModal = this.closeNotificationModal.bind(this);
// player events
this.handlePlayerReady = this.handlePlayerReady.bind(this);
@@ -179,8 +185,22 @@ export default class App extends Component {
});
this.player.init();
this.registerServiceWorker();
// check routing
this.getRoute();
// Increment the visit counter
this.incrementVisitCounter();
}
incrementVisitCounter() {
let visits = parseInt(getLocalStorage(USER_VISIT_COUNT_KEY));
if (isNaN(visits)) {
visits = 0;
}
setLocalStorage(USER_VISIT_COUNT_KEY, visits + 1);
}
componentWillUnmount() {
@@ -248,7 +268,8 @@ export default class App extends Component {
}
setConfigData(data = {}) {
const { name, summary, chatDisabled, socketHostOverride } = data;
const { name, summary, chatDisabled, socketHostOverride, notifications } =
data;
window.document.title = name;
this.socketHostOverride = socketHostOverride;
@@ -263,6 +284,7 @@ export default class App extends Component {
this.setState({
canChat: !chatDisabled,
notifications,
configData: {
...data,
summary: summary && addNewlines(summary),
@@ -579,6 +601,23 @@ export default class App extends Component {
this.setState({ fediverseModalData: null });
}
displayNotificationModal(data) {
this.setState({ notificationModalData: data });
}
closeNotificationModal() {
this.setState({ notificationModalData: null });
}
async registerServiceWorker() {
try {
const reg = await navigator.serviceWorker.register('/serviceWorker.js', {
scope: '/',
});
} catch (err) {
console.error('Owncast service worker registration failed!', err);
}
}
handleWebsocketMessage(e) {
if (e.type === SOCKET_MESSAGE_TYPES.ERROR_USER_DISABLED) {
// User has been actively disabled on the backend. Turn off chat for them.
@@ -670,6 +709,7 @@ export default class App extends Component {
render(props, state) {
const {
accessToken,
chatInputEnabled,
configData,
displayChatPanel,
@@ -690,6 +730,8 @@ export default class App extends Component {
windowWidth,
fediverseModalData,
externalActionModalData,
notificationModalData,
notifications,
lastDisconnectTime,
section,
sectionId,
@@ -753,6 +795,14 @@ export default class App extends Component {
: html` <${VideoPoster} offlineImage=${logo} active=${streamOnline} /> `;
// modal buttons
const notificationsButton =
notifications &&
notifications.browser.enabled &&
isPushNotificationSupported() &&
html`<${NotifyButton}
serverName=${name}
onClick=${this.displayNotificationModal}
/>`;
const externalActionButtons = html`<div
id="external-actions-container"
class="flex flex-row flex-wrap justify-end"
@@ -774,6 +824,7 @@ export default class App extends Component {
federationInfo=${federation}
serverName=${name}
/>`}
${notificationsButton}
</div>`;
// modal component
@@ -800,6 +851,19 @@ export default class App extends Component {
/>
`;
const notificationModal =
notificationModalData &&
html` <${ExternalActionModal}
onClose=${this.closeNotificationModal}
action=${notificationModalData}
useIframe=${false}
customContent=${html`<${NotifyModal}
notifications=${notifications}
streamName=${name}
accessToken=${accessToken}
/>`}
/>`;
const chat = this.state.websocket
? html`
<${Chat}
@@ -807,7 +871,7 @@ export default class App extends Component {
username=${username}
chatInputEnabled=${chatInputEnabled && !chatDisabled}
instanceTitle=${name}
accessToken=${this.state.accessToken}
accessToken=${accessToken}
inputMaxBytes=${maxSocketPayloadSize - EST_SOCKET_PAYLOAD_BUFFER ||
CHAT_MAX_MESSAGE_LENGTH}
/>
@@ -977,6 +1041,7 @@ export default class App extends Component {
</footer>
${chat} ${externalActionModal} ${fediverseFollowModal}
${notificationModal}
</div>
`;
}