Skip to content

Commit

Permalink
Show badge counter on web favicon, desktop dock and desktop tray
Browse files Browse the repository at this point in the history
Related: #90
  • Loading branch information
brunolemos committed Jul 2, 2019
1 parent d84fd17 commit f1c600a
Show file tree
Hide file tree
Showing 24 changed files with 150 additions and 28 deletions.
Binary file modified assets/icon-tray.psd
Binary file not shown.
15 changes: 9 additions & 6 deletions packages/components/src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { AppNavigator } from '../navigation/AppNavigator'
import { enableNetworkInterceptors } from '../network-interceptor'
import { configureStore } from '../redux/store'
import { AppGlobalStyles } from './AppGlobalStyles'
import { AppIconBadge } from './common/AppIconBadge'
import { ColumnFiltersProvider } from './context/ColumnFiltersContext'
import { ColumnFocusProvider } from './context/ColumnFocusContext'
import { ColumnWidthProvider } from './context/ColumnWidthContext'
Expand All @@ -35,12 +36,14 @@ export function App() {
<ColumnFiltersProvider>
<ColumnWidthProvider>
<SpringAnimatedThemeProvider>
<UnreadCountProvider>
<>
<AppGlobalStyles key="app-global-styles" />
<AppNavigator key="app-navigator" />
</>
</UnreadCountProvider>
<>
<AppGlobalStyles key="app-global-styles" />
<AppNavigator key="app-navigator" />

<UnreadCountProvider>
<AppIconBadge />
</UnreadCountProvider>
</>
</SpringAnimatedThemeProvider>
</ColumnWidthProvider>
</ColumnFiltersProvider>
Expand Down
54 changes: 47 additions & 7 deletions packages/components/src/components/common/AppIconBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,54 @@
import React from 'react'

import { Platform } from '../../libs/platform'
export interface AppIconBadgeProps {}

export const AppIconBadge = React.memo((_props: AppIconBadgeProps) => {
return null
})

// Before enabling this for mobile, needs to:
// 1. Fix badge on Android
// 2. Make sure the badge number will get automatically updated on background
/*
import React from 'react'
import firebase from 'react-native-firebase'
import { useUnreadCount } from '../context/UnreadCountContext'
interface IProps {}
export interface AppIconBadgeProps {}
export const AppIconBadge = React.memo((_props: AppIconBadgeProps) => {
const unreadCount = useUnreadCount()
askPermissionAndSetBadge(unreadCount)
export const AppIconBadge = React.memo((_props: IProps) => {
if (Platform.isElectron) {
const unreadCount = useUnreadCount()
window.ipc.send('unread-counter', unreadCount)
}
return null
})
async function askPermissionAndSetBadge(badge: number) {
let hasPermission
try {
hasPermission = await firebase.messaging().hasPermission()
} catch (error) {
if (__DEV__) alert(`Failed to check notifications permission. ${error}`)
}
if (!hasPermission) {
try {
await firebase.messaging().requestPermission()
hasPermission = await firebase.messaging().hasPermission()
} catch (error) {
if (__DEV__) alert(`Failed to get notifications permission. ${error}`)
}
}
if (!hasPermission) {
if (__DEV__) alert('Failed to get permission to set the badge.')
return
}
firebase.notifications().setBadge(badge)
return hasPermission
}
*/
49 changes: 49 additions & 0 deletions packages/components/src/components/common/AppIconBadge.web.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react'

import { Platform } from '../../libs/platform'
import { useUnreadCount } from '../context/UnreadCountContext'

export interface AppIconBadgeProps {}

export const AppIconBadge = React.memo((_props: AppIconBadgeProps) => {
const unreadCount = useUnreadCount()

if (Platform.isElectron) {
window.ipc.send('unread-counter', unreadCount)
} else {
const title = `${document.title || 'DevHub'}`
const titleWithoutBadge = title.replace(/ \([\d]+\+?\)$/, '')

document.title = `${titleWithoutBadge}${
unreadCount > 0 ? ` (${unreadCount})` : ''
}`
updateFavicon(unreadCount > 0)
}

return null
})

function updateFavicon(showBadge: boolean) {
let favicon = document.querySelector('link[rel="shortcut icon"]')

if (!favicon) {
const head = document.querySelector('head')
if (!head) return false

favicon = document.createElement('link')
favicon.setAttribute('rel', 'shortcut icon')

head.appendChild(favicon)
}

const href = favicon.getAttribute('href') || 'favicon.ico'

const baseArr = href.split('/').slice(0, -1)
const newFilename = showBadge ? 'favicon-with-badge.ico' : 'favicon.ico'
const newHref = [...baseArr, newFilename].join('/')

favicon.setAttribute('type', 'image/ico')
favicon.setAttribute('href', newHref)

return true
}
2 changes: 0 additions & 2 deletions packages/components/src/screens/MainScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import url from 'url'
import { AppBannerMessage } from '../components/banners/AppBannerMessage'
import { ColumnSeparator } from '../components/columns/ColumnSeparator'
import { ColumnsRenderer } from '../components/columns/ColumnsRenderer'
import { AppIconBadge } from '../components/common/AppIconBadge'
import { ConditionalWrap } from '../components/common/ConditionalWrap'
import { Screen } from '../components/common/Screen'
import { Separator } from '../components/common/Separator'
Expand Down Expand Up @@ -363,7 +362,6 @@ export const MainScreen = React.memo(() => {
return (
<Screen statusBarBackgroundThemeColor="header" useSafeArea={false}>
<AppBannerMessage />
<AppIconBadge />

<View
style={[
Expand Down
Binary file removed packages/desktop/assets/icons/trayIconActive.png
Binary file not shown.
Binary file removed packages/desktop/assets/icons/[email protected]
Binary file not shown.
Binary file removed packages/desktop/assets/icons/[email protected]
Binary file not shown.
Binary file modified packages/desktop/assets/icons/trayIconTemplate.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/desktop/assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/desktop/assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/desktop/assets/icons/trayIconWhite.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/desktop/assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/desktop/assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion packages/desktop/src/ipc.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { app, ipcMain } from 'electron'

import * as constants from './constants'
import { getDock } from './dock'
import * as tray from './tray'
import * as window from './window'

Expand Down Expand Up @@ -49,6 +50,9 @@ export function register() {
})

ipcMain.on('unread-counter', (_e: any, unreadCount: any) => {
tray.updateIconState(unreadCount)
tray.updateUnreadState(unreadCount)

const dock = getDock()
if (dock) dock.setBadge(unreadCount > 0 ? `${unreadCount}` : '')
})
}
30 changes: 18 additions & 12 deletions packages/desktop/src/tray.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import { BrowserWindow, nativeImage, screen, Tray } from 'electron'
import { app, BrowserWindow, nativeImage, screen, Tray } from 'electron'
import path from 'path'

import * as config from './config'
import * as helpers from './helpers'
import * as menu from './menu'
import * as window from './window'

const inactiveIcon = path.join(
const trayIcon = path.join(
__dirname,
`../assets/icons/${
process.platform === 'darwin' ? 'trayIconTemplate' : 'trayIconWhite'
}.png`,
)

const activeIcon = path.join(
const trayIconWithBadge = path.join(
__dirname,
`../assets/icons/${
process.platform === 'darwin' ? 'trayIconActive' : 'trayIconWhite'
process.platform === 'darwin'
? 'trayIconWithBadge'
: 'trayIconWhiteWithBadge'
}.png`,
)

Expand All @@ -27,11 +29,11 @@ export function getTray() {
}

export function createTray() {
const trayIcon = nativeImage.createFromPath(activeIcon)
const image = nativeImage.createFromPath(trayIcon)

if (tray && !tray.isDestroyed()) tray.destroy()

tray = new Tray(trayIcon)
tray = new Tray(image)

tray.on('click', () => {
const mainWindow = window.getMainWindow()
Expand Down Expand Up @@ -129,10 +131,14 @@ export function alignWindowWithTray(win: BrowserWindow) {
win.setPosition(fixedX, fixedY)
}

export function updateIconState(unreadCount: number) {
if (unreadCount > 0) {
tray!.setImage(activeIcon)
} else {
tray!.setImage(inactiveIcon)
}
export function updateUnreadState(unreadCount: number) {
if (!tray) return

tray.setImage(unreadCount > 0 ? trayIconWithBadge : trayIcon)
tray.setTitle(unreadCount > 0 ? `${unreadCount}` : '')
tray.setToolTip(
`${app.getName()}${
unreadCount > 0 ? ` (${unreadCount} unread items)` : ''
}`,
)
}
2 changes: 2 additions & 0 deletions packages/mobile/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ dependencies {
implementation project(':react-native-firebase')
implementation 'com.google.android.gms:play-services-base:16.0.1'
implementation 'com.google.firebase:firebase-core:16.0.7'
implementation "com.google.firebase:firebase-messaging:18.0.0"
implementation 'me.leolin:ShortcutBadger:1.1.21@aar'

implementation project(':react-native-vector-icons')
implementation project(':react-native-splash-screen')
Expand Down
9 changes: 9 additions & 0 deletions packages/mobile/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
package="com.devhubapp">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />

<application
android:name=".MainApplication"
Expand Down Expand Up @@ -38,7 +40,14 @@
<data android:scheme="devhub" />
</intent-filter>
</activity>

<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />

<service android:name="io.invertase.firebase.messaging.RNFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

import io.invertase.firebase.RNFirebasePackage;
import io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage;
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage;

public class MainApplication extends Application implements ReactApplication {

Expand All @@ -36,6 +38,8 @@ protected List<ReactPackage> getPackages() {
new MainReactPackage(),
new RNFirebasePackage(),
new RNFirebaseAnalyticsPackage(),
new RNFirebaseMessagingPackage(),
new RNFirebaseNotificationsPackage(),
new VectorIconsPackage(),
new SplashScreenReactPackage(),
new RNScreensPackage(),
Expand Down
7 changes: 7 additions & 0 deletions packages/mobile/ios/devhub/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
#import <React/RCTRootView.h>

#import "RNSplashScreen.h"

#import <Firebase.h>
#import "RNFirebaseNotifications.h"

@implementation AppDelegate

Expand All @@ -34,6 +36,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
[self.window makeKeyAndVisible];

[FIRApp configure];
[RNFirebaseNotifications configure];

[RNSplashScreen show];

Expand All @@ -56,4 +59,8 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:sourceApplication annotation:annotation];
}

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
[[RNFirebaseNotifications instance] didReceiveLocalNotification:notification];
}

@end
Binary file added packages/web/public/favicon-with-badge.ico
Binary file not shown.
Binary file added packages/web/public/favicon-with-badge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit f1c600a

Please sign in to comment.