Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3538790
D13246.id43998.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
8 KB
Referenced Files
None
Subscribers
None
D13246.id43998.diff
View Options
diff --git a/lib/handlers/dm-activity-handler.js b/lib/handlers/dm-activity-handler.js
new file mode 100644
--- /dev/null
+++ b/lib/handlers/dm-activity-handler.js
@@ -0,0 +1,107 @@
+// @flow
+import invariant from 'invariant';
+import * as React from 'react';
+
+import { updateActivityActionTypes } from '../actions/activity-actions.js';
+import {
+ type OutboundDMOperationSpecification,
+ dmOperationSpecificationTypes,
+} from '../shared/dm-ops/dm-op-utils.js';
+import { useProcessAndSendDMOperation } from '../shared/dm-ops/process-dm-ops.js';
+import { getMostRecentNonLocalMessageID } from '../shared/message-utils.js';
+import { threadIsPending } from '../shared/thread-utils.js';
+import type { ActivityUpdateSuccessPayload } from '../types/activity-types.js';
+import type { DMChangeThreadReadStatusOperation } from '../types/dm-ops.js';
+import type { RawThreadInfo } from '../types/minimally-encoded-thread-permissions-types.js';
+import { threadTypeIsThick } from '../types/thread-types-enum.js';
+import { useDispatchActionPromise } from '../utils/redux-promise-utils.js';
+import { useSelector } from '../utils/redux-utils.js';
+
+function useUpdateDMActivity(): (
+ viewerID: string,
+ activeThreadInfo: RawThreadInfo,
+) => Promise<ActivityUpdateSuccessPayload> {
+ const processAndSendDMOperation = useProcessAndSendDMOperation();
+ return React.useCallback(
+ async (viewerID: string, activeThreadInfo: RawThreadInfo) => {
+ invariant(
+ threadTypeIsThick(activeThreadInfo.type),
+ 'thread must be thick',
+ );
+ const op: DMChangeThreadReadStatusOperation = {
+ type: 'change_thread_read_status',
+ time: Date.now(),
+ threadID: activeThreadInfo.id,
+ creatorID: viewerID,
+ unread: false,
+ };
+
+ const opSpecification: OutboundDMOperationSpecification = {
+ type: dmOperationSpecificationTypes.OUTBOUND,
+ op,
+ recipients: { type: 'self_devices' },
+ };
+
+ await processAndSendDMOperation(opSpecification);
+ return { activityUpdates: {}, result: { unfocusedToUnread: [] } };
+ },
+ [processAndSendDMOperation],
+ );
+}
+
+function useDMActivityHandler(activeThread: ?string): void {
+ const activeThreadInfo = useSelector(state =>
+ activeThread ? state.threadStore.threadInfos[activeThread] : null,
+ );
+ const activeThreadLatestMessage = useSelector(state =>
+ activeThread
+ ? getMostRecentNonLocalMessageID(activeThread, state.messageStore)
+ : null,
+ );
+ const processAndSendDMOperation = useProcessAndSendDMOperation();
+
+ const prevActiveThreadRef = React.useRef<?string>();
+ const prevActiveThreadLatestMessageRef = React.useRef<?string>();
+
+ const viewerID = useSelector(
+ state => state.currentUserInfo && state.currentUserInfo.id,
+ );
+ const updateDMActivity = useUpdateDMActivity();
+ const dispatchActionPromise = useDispatchActionPromise();
+
+ React.useEffect(() => {
+ const prevActiveThread = prevActiveThreadRef.current;
+ const prevActiveThreadLatestMessage =
+ prevActiveThreadLatestMessageRef.current;
+
+ prevActiveThreadRef.current = activeThread;
+ prevActiveThreadLatestMessageRef.current = activeThreadLatestMessage;
+
+ if (
+ !viewerID ||
+ !activeThread ||
+ !activeThreadInfo ||
+ !threadTypeIsThick(activeThreadInfo.type) ||
+ threadIsPending(activeThread) ||
+ (activeThread === prevActiveThread &&
+ activeThreadLatestMessage === prevActiveThreadLatestMessage)
+ ) {
+ return;
+ }
+
+ void dispatchActionPromise(
+ updateActivityActionTypes,
+ updateDMActivity(viewerID, activeThreadInfo),
+ );
+ }, [
+ updateDMActivity,
+ dispatchActionPromise,
+ activeThread,
+ viewerID,
+ processAndSendDMOperation,
+ activeThreadInfo,
+ activeThreadLatestMessage,
+ ]);
+}
+
+export default useDMActivityHandler;
diff --git a/native/components/dm-activity-handler.react.js b/native/components/dm-activity-handler.react.js
new file mode 100644
--- /dev/null
+++ b/native/components/dm-activity-handler.react.js
@@ -0,0 +1,27 @@
+// @flow
+import * as React from 'react';
+
+import useDMActivityHandler from 'lib/handlers/dm-activity-handler.js';
+import { isLoggedIn } from 'lib/selectors/user-selectors.js';
+
+import { activeMessageListSelector } from '../navigation/nav-selectors.js';
+import { NavContext } from '../navigation/navigation-context.js';
+import { useSelector } from '../redux/redux-utils.js';
+
+function DMActivityHandler(): React.Node {
+ const active = useSelector(
+ state => isLoggedIn(state) && state.lifecycleState !== 'background',
+ );
+ const navContext = React.useContext(NavContext);
+ const activeThread = React.useMemo(() => {
+ if (!active) {
+ return null;
+ }
+ return activeMessageListSelector(navContext);
+ }, [active, navContext]);
+
+ useDMActivityHandler(activeThread);
+ return null;
+}
+
+export default DMActivityHandler;
diff --git a/native/root.react.js b/native/root.react.js
--- a/native/root.react.js
+++ b/native/root.react.js
@@ -54,6 +54,7 @@
import { AutoJoinCommunityHandler } from './components/auto-join-community-handler.react.js';
import BackgroundIdentityLoginHandler from './components/background-identity-login-handler.react.js';
import ConnectFarcasterAlertHandler from './components/connect-farcaster-alert-handler.react.js';
+import DMActivityHandler from './components/dm-activity-handler.react.js';
import { FeatureFlagsProvider } from './components/feature-flags-provider.react.js';
import { NUXTipsContextProvider } from './components/nux-tips-context.react.js';
import PersistedStateGate from './components/persisted-state-gate.js';
@@ -373,6 +374,7 @@
detectUnsupervisedBackgroundRef
}
/>
+ <DMActivityHandler />
<VersionSupportedChecker />
<PlatformDetailsSynchronizer />
<BackgroundIdentityLoginHandler />
diff --git a/web/app.react.js b/web/app.react.js
--- a/web/app.react.js
+++ b/web/app.react.js
@@ -55,6 +55,7 @@
import { MemberListSidebarProvider } from './chat/member-list-sidebar/member-list-sidebar-provider.react.js';
import { AutoJoinCommunityHandler } from './components/auto-join-community-handler.react.js';
import CommunitiesRefresher from './components/communities-refresher.react.js';
+import DMActivityHandler from './components/dm-activity-handler.react.js';
import LogOutIfMissingCSATHandler from './components/log-out-if-missing-csat-handler.react.js';
import NavigationArrows from './components/navigation-arrows.react.js';
import MinVersionHandler from './components/version-handler.react.js';
@@ -256,6 +257,7 @@
<TunnelbrokerDeviceTokenHandler />
<FarcasterDataHandler />
<AutoJoinCommunityHandler />
+ <DMActivityHandler />
{content}
</ChatMentionContextProvider>
</MessageSearchStateProvider>
diff --git a/web/components/dm-activity-handler.react.js b/web/components/dm-activity-handler.react.js
new file mode 100644
--- /dev/null
+++ b/web/components/dm-activity-handler.react.js
@@ -0,0 +1,27 @@
+// @flow
+import * as React from 'react';
+
+import useDMActivityHandler from 'lib/handlers/dm-activity-handler.js';
+import { isLoggedIn } from 'lib/selectors/user-selectors.js';
+
+import { useSelector } from '../redux/redux-utils.js';
+import { activeThreadSelector } from '../selectors/nav-selectors.js';
+
+function DMActivityHandler(): React.Node {
+ const active = useSelector(
+ state => isLoggedIn(state) && state.lifecycleState !== 'background',
+ );
+ const reduxActiveThread = useSelector(activeThreadSelector);
+ const windowActive = useSelector(state => state.windowActive);
+ const activeThread = React.useMemo(() => {
+ if (!active || !windowActive) {
+ return null;
+ }
+ return reduxActiveThread;
+ }, [active, windowActive, reduxActiveThread]);
+
+ useDMActivityHandler(activeThread);
+ return null;
+}
+
+export default DMActivityHandler;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Dec 26, 11:52 PM (10 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2709556
Default Alt Text
D13246.id43998.diff (8 KB)
Attached To
Mode
D13246: Implement DMActivityHandler
Attached
Detach File
Event Timeline
Log In to Comment