@repzo/widget-react-native 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/README.md +35 -29
- package/android/build.gradle +22 -0
- package/android/src/main/java/expo/modules/repzowidget/RepzoVoicePlayer.kt +222 -0
- package/android/src/main/java/expo/modules/repzowidget/RepzoWidgetAudioModule.kt +68 -0
- package/expo-module.config.json +9 -0
- package/ios/RepzoVoicePlayer.swift +175 -0
- package/ios/RepzoWidgetAudio.podspec +26 -0
- package/ios/RepzoWidgetAudioModule.swift +65 -0
- package/lib/commonjs/audio/native-player.js +97 -0
- package/lib/commonjs/audio/native-player.js.map +1 -0
- package/lib/commonjs/audio/voice.js +34 -7
- package/lib/commonjs/audio/voice.js.map +1 -1
- package/lib/commonjs/state/widget-store.js +23 -1
- package/lib/commonjs/state/widget-store.js.map +1 -1
- package/lib/commonjs/ui/WidgetProvider.js +1 -3
- package/lib/commonjs/ui/WidgetProvider.js.map +1 -1
- package/lib/commonjs/ui/components/ImageViewer.js +17 -35
- package/lib/commonjs/ui/components/ImageViewer.js.map +1 -1
- package/lib/commonjs/ui/components/MessagesSkeleton.js +126 -0
- package/lib/commonjs/ui/components/MessagesSkeleton.js.map +1 -0
- package/lib/commonjs/ui/components/VoiceNote.js +8 -9
- package/lib/commonjs/ui/components/VoiceNote.js.map +1 -1
- package/lib/commonjs/ui/components/ZoomableImage.js +299 -0
- package/lib/commonjs/ui/components/ZoomableImage.js.map +1 -0
- package/lib/commonjs/ui/screens/chat/ChatView.js +6 -4
- package/lib/commonjs/ui/screens/chat/ChatView.js.map +1 -1
- package/lib/commonjs/ui/screens/chat/ChatView.styles.js +5 -7
- package/lib/commonjs/ui/screens/chat/ChatView.styles.js.map +1 -1
- package/lib/module/audio/native-player.js +93 -0
- package/lib/module/audio/native-player.js.map +1 -0
- package/lib/module/audio/voice.js +32 -6
- package/lib/module/audio/voice.js.map +1 -1
- package/lib/module/state/widget-store.js +23 -1
- package/lib/module/state/widget-store.js.map +1 -1
- package/lib/module/ui/WidgetProvider.js +1 -3
- package/lib/module/ui/WidgetProvider.js.map +1 -1
- package/lib/module/ui/components/ImageViewer.js +18 -36
- package/lib/module/ui/components/ImageViewer.js.map +1 -1
- package/lib/module/ui/components/MessagesSkeleton.js +124 -0
- package/lib/module/ui/components/MessagesSkeleton.js.map +1 -0
- package/lib/module/ui/components/VoiceNote.js +9 -10
- package/lib/module/ui/components/VoiceNote.js.map +1 -1
- package/lib/module/ui/components/ZoomableImage.js +291 -0
- package/lib/module/ui/components/ZoomableImage.js.map +1 -0
- package/lib/module/ui/screens/chat/ChatView.js +6 -4
- package/lib/module/ui/screens/chat/ChatView.js.map +1 -1
- package/lib/module/ui/screens/chat/ChatView.styles.js +5 -7
- package/lib/module/ui/screens/chat/ChatView.styles.js.map +1 -1
- package/lib/typescript/commonjs/audio/native-player.d.ts +36 -0
- package/lib/typescript/commonjs/audio/native-player.d.ts.map +1 -0
- package/lib/typescript/commonjs/audio/voice.d.ts +17 -4
- package/lib/typescript/commonjs/audio/voice.d.ts.map +1 -1
- package/lib/typescript/commonjs/state/widget-store.d.ts +11 -0
- package/lib/typescript/commonjs/state/widget-store.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/WidgetProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ImageViewer.d.ts +2 -2
- package/lib/typescript/commonjs/ui/components/ImageViewer.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/MessagesSkeleton.d.ts +10 -0
- package/lib/typescript/commonjs/ui/components/MessagesSkeleton.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/VoiceNote.d.ts +2 -2
- package/lib/typescript/commonjs/ui/components/VoiceNote.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ZoomableImage.d.ts +45 -0
- package/lib/typescript/commonjs/ui/components/ZoomableImage.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.styles.d.ts +4 -7
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.styles.d.ts.map +1 -1
- package/lib/typescript/module/audio/native-player.d.ts +36 -0
- package/lib/typescript/module/audio/native-player.d.ts.map +1 -0
- package/lib/typescript/module/audio/voice.d.ts +17 -4
- package/lib/typescript/module/audio/voice.d.ts.map +1 -1
- package/lib/typescript/module/state/widget-store.d.ts +11 -0
- package/lib/typescript/module/state/widget-store.d.ts.map +1 -1
- package/lib/typescript/module/ui/WidgetProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ImageViewer.d.ts +2 -2
- package/lib/typescript/module/ui/components/ImageViewer.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/MessagesSkeleton.d.ts +10 -0
- package/lib/typescript/module/ui/components/MessagesSkeleton.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/VoiceNote.d.ts +2 -2
- package/lib/typescript/module/ui/components/VoiceNote.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ZoomableImage.d.ts +45 -0
- package/lib/typescript/module/ui/components/ZoomableImage.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/chat/ChatView.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/chat/ChatView.styles.d.ts +4 -7
- package/lib/typescript/module/ui/screens/chat/ChatView.styles.d.ts.map +1 -1
- package/package.json +10 -3
- package/react-native.config.js +12 -0
- package/src/audio/native-player.ts +106 -0
- package/src/audio/voice.ts +30 -6
- package/src/state/widget-store.ts +24 -1
- package/src/ui/WidgetProvider.tsx +1 -3
- package/src/ui/components/ImageViewer.tsx +11 -30
- package/src/ui/components/MessagesSkeleton.tsx +92 -0
- package/src/ui/components/VoiceNote.tsx +6 -14
- package/src/ui/components/ZoomableImage.tsx +250 -0
- package/src/ui/screens/chat/ChatView.styles.ts +5 -7
- package/src/ui/screens/chat/ChatView.tsx +4 -3
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plays the chat's voice notes, so apps don't need an audio package of their
|
|
5
|
+
* own. JS addresses each note's player by an id it picks (see
|
|
6
|
+
* `src/audio/native-player.ts`); every change comes back as an `onStatus`
|
|
7
|
+
* event carrying that id.
|
|
8
|
+
*/
|
|
9
|
+
public class RepzoWidgetAudioModule: Module {
|
|
10
|
+
// Only touched on the main queue: every function below runs there.
|
|
11
|
+
private var players: [String: RepzoVoicePlayer] = [:]
|
|
12
|
+
|
|
13
|
+
public func definition() -> ModuleDefinition {
|
|
14
|
+
Name("RepzoWidgetAudio")
|
|
15
|
+
|
|
16
|
+
Events("onStatus")
|
|
17
|
+
|
|
18
|
+
AsyncFunction("create") { (id: String, url: URL) in
|
|
19
|
+
self.players.removeValue(forKey: id)?.release()
|
|
20
|
+
self.players[id] = RepzoVoicePlayer(url: url) { [weak self] status in
|
|
21
|
+
var body = status
|
|
22
|
+
body["id"] = id
|
|
23
|
+
self?.sendEvent("onStatus", body)
|
|
24
|
+
}
|
|
25
|
+
}.runOnQueue(.main)
|
|
26
|
+
|
|
27
|
+
AsyncFunction("play") { (id: String) in
|
|
28
|
+
self.players[id]?.play()
|
|
29
|
+
}.runOnQueue(.main)
|
|
30
|
+
|
|
31
|
+
AsyncFunction("pause") { (id: String) in
|
|
32
|
+
self.players[id]?.pause()
|
|
33
|
+
}.runOnQueue(.main)
|
|
34
|
+
|
|
35
|
+
AsyncFunction("seekTo") { (id: String, seconds: Double, promise: Promise) in
|
|
36
|
+
guard let player = self.players[id] else {
|
|
37
|
+
promise.resolve(nil)
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
player.seek(to: seconds) { promise.resolve(nil) }
|
|
41
|
+
}.runOnQueue(.main)
|
|
42
|
+
|
|
43
|
+
AsyncFunction("setRate") { (id: String, rate: Double) in
|
|
44
|
+
self.players[id]?.setRate(Float(rate))
|
|
45
|
+
}.runOnQueue(.main)
|
|
46
|
+
|
|
47
|
+
AsyncFunction("release") { (id: String) in
|
|
48
|
+
self.players.removeValue(forKey: id)?.release()
|
|
49
|
+
}.runOnQueue(.main)
|
|
50
|
+
|
|
51
|
+
// Leaving the app pauses every note, including one still loading, so it
|
|
52
|
+
// doesn't keep playing with its controls out of sight (apps with the
|
|
53
|
+
// background audio mode would otherwise carry on).
|
|
54
|
+
OnAppEntersBackground {
|
|
55
|
+
self.players.values.forEach { $0.pause() }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
OnDestroy {
|
|
59
|
+
DispatchQueue.main.async {
|
|
60
|
+
self.players.values.forEach { $0.release() }
|
|
61
|
+
self.players.removeAll()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.hasNativeVoicePlayer = hasNativeVoicePlayer;
|
|
7
|
+
exports.useNativeVoicePlayer = useNativeVoicePlayer;
|
|
8
|
+
var _react = require("react");
|
|
9
|
+
/**
|
|
10
|
+
* The SDK's own voice-note player (`ios/` and `android/`), on the system
|
|
11
|
+
* players: AVPlayer on iOS, MediaPlayer on Android. It's an Expo module, so it
|
|
12
|
+
* exists in apps with Expo modules after a native build; in Expo Go, bare
|
|
13
|
+
* React Native apps and an over-the-air update to an app built with an older
|
|
14
|
+
* SDK it's missing, and `src/audio/voice.ts` falls back to `expo-audio` or the
|
|
15
|
+
* file row.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** What the player reports; the same fields `expo-audio`'s status has. */
|
|
19
|
+
|
|
20
|
+
function loadNativeAudio() {
|
|
21
|
+
let mod = null;
|
|
22
|
+
try {
|
|
23
|
+
mod = require('expo-modules-core').requireOptionalNativeModule('RepzoWidgetAudio');
|
|
24
|
+
} catch {
|
|
25
|
+
mod = null;
|
|
26
|
+
}
|
|
27
|
+
// Without the JS package (an unusual setup), the module can still be installed.
|
|
28
|
+
mod = mod ?? globalThis.expo?.modules?.RepzoWidgetAudio ?? null;
|
|
29
|
+
return mod && typeof mod.create === 'function' && typeof mod.play === 'function' ? mod : null;
|
|
30
|
+
}
|
|
31
|
+
const nativeAudio = loadNativeAudio();
|
|
32
|
+
|
|
33
|
+
/** Subscribes to every player's status; null when events can't be received. */
|
|
34
|
+
function addStatusListener(listener) {
|
|
35
|
+
if (!nativeAudio) return null;
|
|
36
|
+
try {
|
|
37
|
+
if (typeof nativeAudio.addListener === 'function') return nativeAudio.addListener('onStatus', listener);
|
|
38
|
+
// Expo SDK 51 and older: native modules aren't event emitters themselves.
|
|
39
|
+
const {
|
|
40
|
+
EventEmitter
|
|
41
|
+
} = require('expo-modules-core');
|
|
42
|
+
return new EventEmitter(nativeAudio).addListener('onStatus', listener);
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Whether the app has the SDK's own player. */
|
|
49
|
+
function hasNativeVoicePlayer() {
|
|
50
|
+
return nativeAudio != null;
|
|
51
|
+
}
|
|
52
|
+
let nextPlayerId = 0;
|
|
53
|
+
const ignore = () => {};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A player for one note, released when the component goes away. Shaped like
|
|
57
|
+
* `expo-audio`'s `useAudioPlayer` + `useAudioPlayerStatus`, so the voice note
|
|
58
|
+
* drives either the same way.
|
|
59
|
+
*/
|
|
60
|
+
function useNativeVoicePlayer(url) {
|
|
61
|
+
const id = (0, _react.useMemo)(() => `repzo-voice-${nextPlayerId += 1}`, []);
|
|
62
|
+
const [status, setStatus] = (0, _react.useState)({
|
|
63
|
+
isLoaded: false
|
|
64
|
+
});
|
|
65
|
+
(0, _react.useEffect)(() => {
|
|
66
|
+
const audio = nativeAudio;
|
|
67
|
+
const subscription = addStatusListener(next => {
|
|
68
|
+
if (next?.id === id) setStatus(next);
|
|
69
|
+
});
|
|
70
|
+
if (!subscription) {
|
|
71
|
+
setStatus({
|
|
72
|
+
isLoaded: false,
|
|
73
|
+
error: 'Voice notes need a newer Expo SDK'
|
|
74
|
+
});
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
audio.create(id, url).catch(error => setStatus({
|
|
78
|
+
isLoaded: false,
|
|
79
|
+
error: String(error)
|
|
80
|
+
}));
|
|
81
|
+
return () => {
|
|
82
|
+
subscription.remove();
|
|
83
|
+
audio.release(id).catch(ignore);
|
|
84
|
+
};
|
|
85
|
+
}, [id, url]);
|
|
86
|
+
const player = (0, _react.useMemo)(() => ({
|
|
87
|
+
play: () => void nativeAudio.play(id).catch(ignore),
|
|
88
|
+
pause: () => void nativeAudio.pause(id).catch(ignore),
|
|
89
|
+
seekTo: seconds => nativeAudio.seekTo(id, seconds).catch(ignore),
|
|
90
|
+
setPlaybackRate: rate => void nativeAudio.setRate(id, rate).catch(ignore)
|
|
91
|
+
}), [id]);
|
|
92
|
+
return {
|
|
93
|
+
player,
|
|
94
|
+
status
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=native-player.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_react","require","loadNativeAudio","mod","requireOptionalNativeModule","globalThis","expo","modules","RepzoWidgetAudio","create","play","nativeAudio","addStatusListener","listener","addListener","EventEmitter","hasNativeVoicePlayer","nextPlayerId","ignore","useNativeVoicePlayer","url","id","useMemo","status","setStatus","useState","isLoaded","useEffect","audio","subscription","next","error","undefined","catch","String","remove","release","player","pause","seekTo","seconds","setPlaybackRate","rate","setRate"],"sourceRoot":"../../../src","sources":["audio/native-player.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -7,11 +7,16 @@ exports.audioPlayerOptions = audioPlayerOptions;
|
|
|
7
7
|
exports.claimVoicePlayback = claimVoicePlayback;
|
|
8
8
|
exports.formatVoiceClock = formatVoiceClock;
|
|
9
9
|
exports.isPlayableAudio = isPlayableAudio;
|
|
10
|
-
exports.
|
|
10
|
+
exports.useVoicePlayer = void 0;
|
|
11
|
+
exports.voicePlaybackBackend = voicePlaybackBackend;
|
|
11
12
|
exports.voiceWaveformLevels = voiceWaveformLevels;
|
|
13
|
+
var _nativePlayer = require("./native-player.js");
|
|
12
14
|
/**
|
|
13
|
-
* Voice notes (and any audio file) play inside the chat
|
|
14
|
-
* `
|
|
15
|
+
* Voice notes (and any audio file) play inside the chat with the SDK's own
|
|
16
|
+
* native player (`src/audio/native-player.ts`). Where that isn't built into
|
|
17
|
+
* the app (Expo Go, bare React Native, an app built with SDK 1.2.2 or older),
|
|
18
|
+
* they play with the host app's `expo-audio` if it has one; otherwise they
|
|
19
|
+
* stay a file row that opens in the browser.
|
|
15
20
|
*
|
|
16
21
|
* Resolved once at module load, like the attachment pickers: each `require` is
|
|
17
22
|
* static so Metro can see it, and with `allowOptionalDependencies` (see
|
|
@@ -42,9 +47,10 @@ try {
|
|
|
42
47
|
/** How often the playhead moves while a note plays. */
|
|
43
48
|
const UPDATE_INTERVAL_MS = 100;
|
|
44
49
|
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
/** Which player voice notes use in this app, or null when audio stays a file row. */
|
|
51
|
+
const backend = (0, _nativePlayer.hasNativeVoicePlayer)() ? 'native' : expoAudio ? 'expo-audio' : null;
|
|
52
|
+
function voicePlaybackBackend() {
|
|
53
|
+
return backend;
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
/** The second argument to `useAudioPlayer` for the installed expo-audio. */
|
|
@@ -54,9 +60,30 @@ function audioPlayerOptions() {
|
|
|
54
60
|
} : UPDATE_INTERVAL_MS;
|
|
55
61
|
}
|
|
56
62
|
|
|
63
|
+
/** expo-audio's player for a note, on the same terms as the native one. */
|
|
64
|
+
function useExpoAudioPlayer(url) {
|
|
65
|
+
const player = expoAudio.useAudioPlayer({
|
|
66
|
+
uri: url
|
|
67
|
+
}, audioPlayerOptions());
|
|
68
|
+
const status = expoAudio.useAudioPlayerStatus(player);
|
|
69
|
+
return {
|
|
70
|
+
player,
|
|
71
|
+
status
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The player for one note: `play()`, `pause()`, `seekTo(seconds)`,
|
|
77
|
+
* `setPlaybackRate(rate)`, and its status (`currentTime`, `duration`,
|
|
78
|
+
* `playing`, `isLoaded`, `isBuffering`, `didJustFinish`, `error`). Only use it
|
|
79
|
+
* when `voicePlaybackBackend()` isn't null. Picked once, since the backend
|
|
80
|
+
* never changes while the app runs.
|
|
81
|
+
*/
|
|
82
|
+
const useVoicePlayer = exports.useVoicePlayer = backend === 'native' ? _nativePlayer.useNativeVoicePlayer : useExpoAudioPlayer;
|
|
83
|
+
|
|
57
84
|
/** Audio the chat can play in place: an `audio/*` attachment with a web address. */
|
|
58
85
|
function isPlayableAudio(contentType, url) {
|
|
59
|
-
return
|
|
86
|
+
return backend != null && (contentType ?? '').toLowerCase().startsWith('audio/') && /^https?:\/\//i.test(url ?? '');
|
|
60
87
|
}
|
|
61
88
|
|
|
62
89
|
// ─── One note at a time ─────────────────────────────────────────────────────
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["
|
|
1
|
+
{"version":3,"names":["_nativePlayer","require","expoAudio","mod","useAudioPlayer","useAudioPlayerStatus","takesOptionsObject","version","Number","split","UPDATE_INTERVAL_MS","backend","hasNativeVoicePlayer","voicePlaybackBackend","audioPlayerOptions","updateInterval","useExpoAudioPlayer","url","player","uri","status","useVoicePlayer","exports","useNativeVoicePlayer","isPlayableAudio","contentType","toLowerCase","startsWith","test","playing","claimVoicePlayback","pause","claim","previous","voiceWaveformLevels","seed","count","h","i","length","charCodeAt","Math","imul","levels","n","push","pow","formatVoiceClock","seconds","s","max","floor","isFinite","toString","padStart"],"sourceRoot":"../../../src","sources":["audio/voice.ts"],"mappings":";;;;;;;;;;;;AAaA,IAAAA,aAAA,GAAAC,OAAA;AAbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA,IAAIC,SAAc,GAAG,IAAI;AACzB,IAAI;EACF,MAAMC,GAAG,GAAGF,OAAO,CAAC,YAAY,CAAC;EACjC,IAAI,OAAOE,GAAG,EAAEC,cAAc,KAAK,UAAU,IAAI,OAAOD,GAAG,EAAEE,oBAAoB,KAAK,UAAU,EAAE;IAChGH,SAAS,GAAGC,GAAG;EACjB;AACF,CAAC,CAAC,MAAM;EACND,SAAS,GAAG,IAAI;AAClB;;AAEA;AACA;AACA,IAAII,kBAAkB,GAAG,IAAI;AAC7B,IAAI;EACF,MAAMC,OAAgB,GAAGN,OAAO,CAAC,yBAAyB,CAAC,EAAEM,OAAO;EACpE,IAAI,OAAOA,OAAO,KAAK,QAAQ,EAAED,kBAAkB,GAAGE,MAAM,CAACD,OAAO,CAACE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1F,CAAC,CAAC,MAAM;EACNH,kBAAkB,GAAG,IAAI;AAC3B;;AAEA;AACA,MAAMI,kBAAkB,GAAG,GAAG;;AAE9B;AACA,MAAMC,OAAuC,GAAG,IAAAC,kCAAoB,EAAC,CAAC,GAAG,QAAQ,GAAGV,SAAS,GAAG,YAAY,GAAG,IAAI;AAE5G,SAASW,oBAAoBA,CAAA,EAAmC;EACrE,OAAOF,OAAO;AAChB;;AAEA;AACO,SAASG,kBAAkBA,CAAA,EAAY;EAC5C,OAAOR,kBAAkB,GAAG;IAAES,cAAc,EAAEL;EAAmB,CAAC,GAAGA,kBAAkB;AACzF;;AAEA;AACA,SAASM,kBAAkBA,CAACC,GAAW,EAAgC;EACrE,MAAMC,MAAM,GAAGhB,SAAS,CAACE,cAAc,CAAC;IAAEe,GAAG,EAAEF;EAAI,CAAC,EAAEH,kBAAkB,CAAC,CAAC,CAAC;EAC3E,MAAMM,MAAM,GAAGlB,SAAS,CAACG,oBAAoB,CAACa,MAAM,CAAC;EACrD,OAAO;IAAEA,MAAM;IAAEE;EAAO,CAAC;AAC3B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,cAA6D,GAAAC,OAAA,CAAAD,cAAA,GACxEV,OAAO,KAAK,QAAQ,GAAGY,kCAAoB,GAAGP,kBAAkB;;AAElE;AACO,SAASQ,eAAeA,CAACC,WAAsC,EAAER,GAA8B,EAAW;EAC/G,OAAON,OAAO,IAAI,IAAI,IAAI,CAACc,WAAW,IAAI,EAAE,EAAEC,WAAW,CAAC,CAAC,CAACC,UAAU,CAAC,QAAQ,CAAC,IAAI,eAAe,CAACC,IAAI,CAACX,GAAG,IAAI,EAAE,CAAC;AACrH;;AAEA;AACA;AACA;;AAEA,IAAIY,OAAqC,GAAG,IAAI;;AAEhD;AACA;AACA;AACA;AACO,SAASC,kBAAkBA,CAACC,KAAiB,EAAc;EAChE,MAAMC,KAAK,GAAG;IAAED;EAAM,CAAC;EACvB,MAAME,QAAQ,GAAGJ,OAAO;EACxBA,OAAO,GAAGG,KAAK;EACf,IAAIC,QAAQ,EAAE;IACZ,IAAI;MACFA,QAAQ,CAACF,KAAK,CAAC,CAAC;IAClB,CAAC,CAAC,MAAM;MACN;IAAA;EAEJ;EACA,OAAO,MAAM;IACX,IAAIF,OAAO,KAAKG,KAAK,EAAEH,OAAO,GAAG,IAAI;EACvC,CAAC;AACH;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACO,SAASK,mBAAmBA,CAACC,IAAY,EAAEC,KAAa,EAAY;EACzE,IAAIC,CAAC,GAAG,UAAU;EAClB,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGH,IAAI,CAACI,MAAM,EAAED,CAAC,IAAI,CAAC,EAAE;IACvCD,CAAC,IAAIF,IAAI,CAACK,UAAU,CAACF,CAAC,CAAC;IACvBD,CAAC,GAAGI,IAAI,CAACC,IAAI,CAACL,CAAC,EAAE,QAAQ,CAAC;EAC5B;EACA,MAAMM,MAAgB,GAAG,EAAE;EAC3B,KAAK,IAAIL,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGF,KAAK,EAAEE,CAAC,IAAI,CAAC,EAAE;IACjCD,CAAC,IAAIA,CAAC,IAAI,EAAE;IACZA,CAAC,IAAIA,CAAC,KAAK,EAAE;IACbA,CAAC,IAAIA,CAAC,IAAI,CAAC;IACX,MAAMO,CAAC,GAAI,CAACP,CAAC,KAAK,CAAC,IAAI,IAAI,GAAI,IAAI;IACnCM,MAAM,CAACE,IAAI,CAAC,IAAI,GAAGJ,IAAI,CAACK,GAAG,CAACF,CAAC,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC;EAC9C;EACA,OAAOD,MAAM;AACf;;AAEA;AACO,SAASI,gBAAgBA,CAACC,OAAe,EAAU;EACxD,MAAMC,CAAC,GAAGR,IAAI,CAACS,GAAG,CAAC,CAAC,EAAET,IAAI,CAACU,KAAK,CAAC3C,MAAM,CAAC4C,QAAQ,CAACJ,OAAO,CAAC,GAAGA,OAAO,GAAG,CAAC,CAAC,CAAC;EACzE,OAAO,GAAGP,IAAI,CAACU,KAAK,CAACF,CAAC,GAAG,EAAE,CAAC,IAAI,CAACA,CAAC,GAAG,EAAE,EAAEI,QAAQ,CAAC,CAAC,CAACC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;AACxE","ignoreList":[]}
|
|
@@ -25,7 +25,7 @@ var _safeUrl = require("../ui/utils/safe-url.js");
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
/** Sent on every init; must match `package.json` (tests/version.test.js checks). */
|
|
28
|
-
const SDK_VERSION = '@repzo/widget-react-native@1.
|
|
28
|
+
const SDK_VERSION = '@repzo/widget-react-native@1.3.0';
|
|
29
29
|
|
|
30
30
|
/** The platform this session reports — mobile sessions get the Mobile SDKs overrides. */
|
|
31
31
|
function sessionPlatform() {
|
|
@@ -171,6 +171,13 @@ class WidgetStore {
|
|
|
171
171
|
/** True once `currentUserKey` mirrors storage — memory is then authoritative. */
|
|
172
172
|
userKeyLoaded = false;
|
|
173
173
|
pendingConversationId = null;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* The screen a thread was opened from, so its back button returns there.
|
|
177
|
+
* Null when it was opened with the messenger closed (a push, a notification
|
|
178
|
+
* card, `presentConversation`): there is nothing to go back to.
|
|
179
|
+
*/
|
|
180
|
+
chatOpenedFrom = null;
|
|
174
181
|
appStateSubscription = null;
|
|
175
182
|
|
|
176
183
|
/** When the app last went to the background (0: hasn't this session). */
|
|
@@ -269,6 +276,13 @@ class WidgetStore {
|
|
|
269
276
|
next.inAppNotification = null;
|
|
270
277
|
}
|
|
271
278
|
this.state = next;
|
|
279
|
+
|
|
280
|
+
// Recorded here because a thread opens from many places (home cards, the
|
|
281
|
+
// list, the Messages tab, posts) and each would otherwise have to say so.
|
|
282
|
+
const intoThread = next.view === 'chat' || next.view === 'prechat';
|
|
283
|
+
if (intoThread && previous.view !== 'chat' && previous.view !== 'prechat') {
|
|
284
|
+
this.chatOpenedFrom = previous.isOpen && (previous.view === 'home' || previous.view === 'conversations') ? previous.view : null;
|
|
285
|
+
}
|
|
272
286
|
if (next.unreadCount !== previous.unreadCount) {
|
|
273
287
|
this.events.emit(_events.RepzoWidgetEvents.UnreadCountDidChange, {
|
|
274
288
|
count: next.unreadCount
|
|
@@ -1400,6 +1414,14 @@ class WidgetStore {
|
|
|
1400
1414
|
} : {})
|
|
1401
1415
|
});
|
|
1402
1416
|
}
|
|
1417
|
+
|
|
1418
|
+
/**
|
|
1419
|
+
* The thread's back button: the screen it was opened from, or the
|
|
1420
|
+
* conversation list when it was opened from outside the messenger.
|
|
1421
|
+
*/
|
|
1422
|
+
leaveConversation() {
|
|
1423
|
+
this.setView(this.chatOpenedFrom ?? (this.state.activeTab === 'messages' ? 'conversations' : 'home'));
|
|
1424
|
+
}
|
|
1403
1425
|
setActiveTab(tab) {
|
|
1404
1426
|
if (tab === 'home') {
|
|
1405
1427
|
this.setState({
|