Skip to content

Commit

Permalink
Merge pull request #31055 from margelo/origin/feat/update-loading-scr…
Browse files Browse the repository at this point in the history
…een-to-play-animation-and-sound

feat: app sounds
  • Loading branch information
cristipaval authored Feb 12, 2024
2 parents d6bd1c6 + 748d554 commit 9c8d7d2
Show file tree
Hide file tree
Showing 29 changed files with 318 additions and 7 deletions.
Binary file added android/app/src/main/res/raw/attention.mp3
Binary file not shown.
Binary file added android/app/src/main/res/raw/done.mp3
Binary file not shown.
Binary file added android/app/src/main/res/raw/receive.mp3
Binary file not shown.
Binary file added android/app/src/main/res/raw/success.mp3
Binary file not shown.
Binary file added assets/sounds/attention.mp3
Binary file not shown.
Binary file added assets/sounds/done.mp3
Binary file not shown.
Binary file added assets/sounds/receive.mp3
Binary file not shown.
Binary file added assets/sounds/success.mp3
Binary file not shown.
4 changes: 3 additions & 1 deletion config/webpack/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ const webpackConfig = ({envFile = '.env', platform = 'web'}) => ({
{from: 'web/manifest.json'},
{from: 'assets/css', to: 'css'},
{from: 'assets/fonts/web', to: 'fonts'},
{from: 'assets/sounds', to: 'sounds'},
{from: 'node_modules/react-pdf/dist/esm/Page/AnnotationLayer.css', to: 'css/AnnotationLayer.css'},
{from: 'node_modules/react-pdf/dist/esm/Page/TextLayer.css', to: 'css/TextLayer.css'},
{from: 'assets/images/shadow.png', to: 'images/shadow.png'},
Expand Down Expand Up @@ -200,7 +201,7 @@ const webpackConfig = ({envFile = '.env', platform = 'web'}) => ({
alias: {
'react-native-config': 'react-web-config',
'react-native$': 'react-native-web',

'react-native-sound': 'react-native-web-sound',
// Module alias for web & desktop
// https://webpack.js.org/configuration/resolve/#resolvealias
'@assets': path.resolve(__dirname, '../../assets'),
Expand Down Expand Up @@ -239,6 +240,7 @@ const webpackConfig = ({envFile = '.env', platform = 'web'}) => ({
'process/browser': require.resolve('process/browser'),
},
},

optimization: {
runtimeChunk: 'single',
splitChunks: {
Expand Down
16 changes: 16 additions & 0 deletions ios/NewExpensify.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

/* Begin PBXBuildFile section */
059DC4EFD39EF39437E6823D /* libPods-NotificationServiceExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A997AA8204EA3D90907FA80 /* libPods-NotificationServiceExtension.a */; };
083353EB2B5AB22A00C603C0 /* attention.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 083353E72B5AB22900C603C0 /* attention.mp3 */; };
083353EC2B5AB22A00C603C0 /* done.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 083353E82B5AB22900C603C0 /* done.mp3 */; };
083353ED2B5AB22A00C603C0 /* receive.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 083353E92B5AB22900C603C0 /* receive.mp3 */; };
083353EE2B5AB22A00C603C0 /* success.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 083353EA2B5AB22900C603C0 /* success.mp3 */; };
0C7C65547D7346EB923BE808 /* ExpensifyMono-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = E704648954784DDFBAADF568 /* ExpensifyMono-Regular.otf */; };
0CDA8E34287DD650004ECBEC /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0CDA8E33287DD650004ECBEC /* AppDelegate.mm */; };
0CDA8E35287DD650004ECBEC /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0CDA8E33287DD650004ECBEC /* AppDelegate.mm */; };
Expand Down Expand Up @@ -79,6 +83,10 @@
00E356EE1AD99517003FC87E /* NewExpensifyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NewExpensifyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
076FD9E41E08971BBF51D580 /* libPods-NewExpensify-NewExpensifyTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NewExpensify-NewExpensifyTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
083353E72B5AB22900C603C0 /* attention.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = attention.mp3; path = ../assets/sounds/attention.mp3; sourceTree = "<group>"; };
083353E82B5AB22900C603C0 /* done.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = done.mp3; path = ../assets/sounds/done.mp3; sourceTree = "<group>"; };
083353E92B5AB22900C603C0 /* receive.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = receive.mp3; path = ../assets/sounds/receive.mp3; sourceTree = "<group>"; };
083353EA2B5AB22900C603C0 /* success.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = success.mp3; path = ../assets/sounds/success.mp3; sourceTree = "<group>"; };
0CDA8E33287DD650004ECBEC /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = NewExpensify/AppDelegate.mm; sourceTree = "<group>"; };
0CDA8E36287DD6A0004ECBEC /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = NewExpensify/Images.xcassets; sourceTree = "<group>"; };
0F5BE0CD252686320097D869 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -311,6 +319,10 @@
A9EA265D209D4558995C9BD4 /* Resources */ = {
isa = PBXGroup;
children = (
083353E72B5AB22900C603C0 /* attention.mp3 */,
083353E82B5AB22900C603C0 /* done.mp3 */,
083353E92B5AB22900C603C0 /* receive.mp3 */,
083353EA2B5AB22900C603C0 /* success.mp3 */,
44BF435285B94E5B95F90994 /* ExpensifyNewKansas-Medium.otf */,
D2AFB39EC1D44BF9B91D3227 /* ExpensifyNewKansas-MediumItalic.otf */,
DCF33E34FFEC48128CDD41D4 /* ExpensifyMono-Bold.otf */,
Expand Down Expand Up @@ -496,13 +508,17 @@
0F5BE0CE252686330097D869 /* GoogleService-Info.plist in Resources */,
E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */,
F0C450EA2705020500FD2970 /* colors.json in Resources */,
083353EB2B5AB22A00C603C0 /* attention.mp3 in Resources */,
0CDA8E37287DD6A0004ECBEC /* Images.xcassets in Resources */,
70CF6E82262E297300711ADC /* BootSplash.storyboard in Resources */,
FF941A8D48F849269AB85C9A /* ExpensifyNewKansas-Medium.otf in Resources */,
BDB853621F354EBB84E619C2 /* ExpensifyNewKansas-MediumItalic.otf in Resources */,
26AF3C3540374A9FACB6C19E /* ExpensifyMono-Bold.otf in Resources */,
083353EE2B5AB22A00C603C0 /* success.mp3 in Resources */,
0C7C65547D7346EB923BE808 /* ExpensifyMono-Regular.otf in Resources */,
2A9F8CDA983746B0B9204209 /* ExpensifyNeue-Bold.otf in Resources */,
083353EC2B5AB22A00C603C0 /* done.mp3 in Resources */,
083353ED2B5AB22A00C603C0 /* receive.mp3 in Resources */,
ED222ED90E074A5481A854FA /* ExpensifyNeue-BoldItalic.otf in Resources */,
30581EA8AAFD4FCE88C5D191 /* ExpensifyNeue-Italic.otf in Resources */,
1246A3EF20E54E7A9494C8B9 /* ExpensifyNeue-Regular.otf in Resources */,
Expand Down
9 changes: 9 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,11 @@ PODS:
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
- RNSound (0.11.2):
- React-Core
- RNSound/Core (= 0.11.2)
- RNSound/Core (0.11.2):
- React-Core
- RNSVG (14.0.0):
- React-Core
- SDWebImage (5.17.0):
Expand Down Expand Up @@ -1580,6 +1585,7 @@ DEPENDENCIES:
- RNReactNativeHapticFeedback (from `../node_modules/react-native-haptic-feedback`)
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
- RNSound (from `../node_modules/react-native-sound`)
- RNSVG (from `../node_modules/react-native-svg`)
- VisionCamera (from `../node_modules/react-native-vision-camera`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
Expand Down Expand Up @@ -1827,6 +1833,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-reanimated"
RNScreens:
:path: "../node_modules/react-native-screens"
RNSound:
:path: "../node_modules/react-native-sound"
RNSVG:
:path: "../node_modules/react-native-svg"
VisionCamera:
Expand Down Expand Up @@ -1973,6 +1981,7 @@ SPEC CHECKSUMS:
RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c
RNReanimated: 57f436e7aa3d277fbfed05e003230b43428157c0
RNScreens: b582cb834dc4133307562e930e8fa914b8c04ef2
RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852
RNSVG: 255767813dac22db1ec2062c8b7e7b856d4e5ae6
SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9
SDWebImageAVIFCoder: 8348fef6d0ec69e129c66c9fe4d74fbfbf366112
Expand Down
8 changes: 8 additions & 0 deletions jest/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,11 @@ jest.mock('react-native-fs', () => ({
unlink: jest.fn(() => new Promise<void>((res) => res())),
CachesDirectoryPath: jest.fn(),
}));

jest.mock('react-native-sound', () => {
class SoundMock {
play = jest.fn();
}

return SoundMock;
});
26 changes: 26 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,15 @@
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.7.4",
"react-native-screens": "3.29.0",
"react-native-sound": "^0.11.2",
"react-native-svg": "14.1.0",
"react-native-tab-view": "^3.5.2",
"react-native-url-polyfill": "^2.0.0",
"react-native-view-shot": "3.8.0",
"react-native-vision-camera": "2.16.8",
"react-native-web": "^0.19.9",
"react-native-web-linear-gradient": "^1.1.2",
"react-native-web-sound": "^0.1.3",
"react-native-webview": "13.6.3",
"react-pdf": "7.3.3",
"react-plaid-link": "3.3.2",
Expand Down
38 changes: 38 additions & 0 deletions patches/react-native-sound+0.11.2.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
diff --git a/node_modules/react-native-sound/RNSound/RNSound.h b/node_modules/react-native-sound/RNSound/RNSound.h
index 7f5b97b..1a3c840 100644
--- a/node_modules/react-native-sound/RNSound/RNSound.h
+++ b/node_modules/react-native-sound/RNSound/RNSound.h
@@ -1,17 +1,7 @@
-#if __has_include(<React/RCTBridgeModule.h>)
#import <React/RCTBridgeModule.h>
-#else
-#import "RCTBridgeModule.h"
-#endif
-
#import <AVFoundation/AVFoundation.h>
-
-#if __has_include(<React/RCTEventEmitter.h>)
#import <React/RCTEventEmitter.h>
-#else
-#import "RCTEventEmitter.h"
-#endif

@interface RNSound : RCTEventEmitter <RCTBridgeModule, AVAudioPlayerDelegate>
-@property (nonatomic, weak) NSNumber *_key;
+@property(nonatomic, weak) NSNumber *_key;
@end
diff --git a/node_modules/react-native-sound/RNSound/RNSound.m b/node_modules/react-native-sound/RNSound/RNSound.m
index df3784e..d34ac01 100644
--- a/node_modules/react-native-sound/RNSound/RNSound.m
+++ b/node_modules/react-native-sound/RNSound/RNSound.m
@@ -1,10 +1,6 @@
#import "RNSound.h"

-#if __has_include("RCTUtils.h")
-#import "RCTUtils.h"
-#else
#import <React/RCTUtils.h>
-#endif

@implementation RNSound {
NSMutableDictionary *_playerPool;
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReceiptUtils from '@libs/ReceiptUtils';
import * as ReportUtils from '@libs/ReportUtils';
import playSound, {SOUNDS} from '@libs/Sound';
import * as TransactionUtils from '@libs/TransactionUtils';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -549,6 +550,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
return;
}

playSound(SOUNDS.DONE);
setDidConfirm(true);
onConfirm(selectedParticipants);
}
Expand Down
2 changes: 2 additions & 0 deletions src/components/SettlementButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {withOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import * as ReportUtils from '@libs/ReportUtils';
import playSound, {SOUNDS} from '@libs/Sound';
import * as BankAccounts from '@userActions/BankAccounts';
import * as IOU from '@userActions/IOU';
import * as PaymentMethods from '@userActions/PaymentMethods';
Expand Down Expand Up @@ -201,6 +202,7 @@ function SettlementButton({
return;
}

playSound(SOUNDS.DONE);
onPress(iouPaymentType);
};

Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,7 @@ export default {
},
preferencesPage: {
receiveRelevantFeatureUpdatesAndExpensifyNews: 'Receive relevant feature updates and Expensify news',
muteAllSounds: 'Mute all sounds from Expensify',
},
priorityModePage: {
priorityMode: 'Priority mode',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,7 @@ export default {
},
preferencesPage: {
receiveRelevantFeatureUpdatesAndExpensifyNews: 'Recibir noticias sobre Expensify y actualizaciones del producto',
muteAllSounds: 'Silenciar todos los sonidos de Expensify',
},
priorityModePage: {
priorityMode: 'Modo prioridad',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function canUseBrowserNotifications(): Promise<boolean> {
* @param icon Path to icon
* @param data extra data to attach to the notification
*/
function push(title: string, body = '', icon: string | ImageSourcePropType = '', data: LocalNotificationData = {}, onClick: LocalNotificationClickHandler = () => {}) {
function push(title: string, body = '', icon: string | ImageSourcePropType = '', data: LocalNotificationData = {}, onClick: LocalNotificationClickHandler = () => {}, silent = false) {
canUseBrowserNotifications().then((canUseNotifications) => {
if (!canUseNotifications) {
return;
Expand All @@ -54,6 +54,7 @@ function push(title: string, body = '', icon: string | ImageSourcePropType = '',
body,
icon: String(icon),
data,
silent,
});
notificationCache[notificationID].onclick = () => {
onClick();
Expand Down Expand Up @@ -104,7 +105,7 @@ export default {
reportID: report.reportID,
};

push(title, body, icon, data, onClick);
push(title, body, icon, data, onClick, true);
},

pushModifiedExpenseNotification(report: Report, reportAction: ReportAction, onClick: LocalNotificationClickHandler, usesIcon = false) {
Expand Down
3 changes: 3 additions & 0 deletions src/libs/Sound/config/index.native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const config = {prefix: ''};

export default config;
3 changes: 3 additions & 0 deletions src/libs/Sound/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const config = {prefix: '/sounds/'};

export default config;
71 changes: 71 additions & 0 deletions src/libs/Sound/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Onyx from 'react-native-onyx';
import Sound from 'react-native-sound';
import type {ValueOf} from 'type-fest';
import ONYXKEYS from '@src/ONYXKEYS';
import config from './config';

let isMuted = false;

Onyx.connect({
key: ONYXKEYS.USER,
callback: (val) => (isMuted = !!val?.isMutedAllSounds),
});

const SOUNDS = {
DONE: 'done',
SUCCESS: 'success',
ATTENTION: 'attention',
RECEIVE: 'receive',
} as const;

/**
* Creates a version of the given function that, when called, queues the execution and ensures that
* calls are spaced out by at least the specified `minExecutionTime`, even if called more frequently. This allows
* for throttling frequent calls to a function, ensuring each is executed with a minimum `minExecutionTime` between calls.
* Each call returns a promise that resolves when the function call is executed, allowing for asynchronous handling.
*/
function withMinimalExecutionTime<F extends (...args: Parameters<F>) => ReturnType<F>>(func: F, minExecutionTime: number) {
const queue: Array<[() => ReturnType<F>, (value?: unknown) => void]> = [];
let timerId: NodeJS.Timeout | null = null;

function processQueue() {
if (queue.length > 0) {
const next = queue.shift();

if (!next) {
return;
}

const [nextFunc, resolve] = next;
nextFunc();
resolve();
timerId = setTimeout(processQueue, minExecutionTime);
} else {
timerId = null;
}
}

return function (...args: Parameters<F>) {
return new Promise((resolve) => {
queue.push([() => func(...args), resolve]);

if (!timerId) {
// If the timer isn't running, start processing the queue
processQueue();
}
});
};
}

const playSound = (soundFile: ValueOf<typeof SOUNDS>) => {
const sound = new Sound(`${config.prefix}${soundFile}.mp3`, Sound.MAIN_BUNDLE, (error) => {
if (error || isMuted) {
return;
}

sound.play();
});
};

export {SOUNDS};
export default withMinimalExecutionTime(playSound, 300);
Loading

0 comments on commit 9c8d7d2

Please sign in to comment.