Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32156194
D15410.1765035820.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
D15410.1765035820.diff
View Options
diff --git a/lib/shared/farcaster/farcaster-hooks.js b/lib/shared/farcaster/farcaster-hooks.js
--- a/lib/shared/farcaster/farcaster-hooks.js
+++ b/lib/shared/farcaster/farcaster-hooks.js
@@ -518,15 +518,20 @@
function useRefreshFarcasterConversation(): (
conversationID: string,
+ messagesLimit?: number,
) => Promise<void> {
const fetchConversationWithMessages = useFetchConversationWithMessages();
const dispatch = useDispatch();
return React.useCallback(
- async (conversationID: string) => {
+ async (conversationID: string, messagesLimit?: number) => {
const batchedUpdates = new BatchedUpdates();
- await fetchConversationWithMessages(conversationID, 20, batchedUpdates);
+ await fetchConversationWithMessages(
+ conversationID,
+ messagesLimit ?? 20,
+ batchedUpdates,
+ );
if (!batchedUpdates.isEmpty()) {
dispatch({
diff --git a/lib/shared/threads/protocols/dm-thread-protocol.js b/lib/shared/threads/protocols/dm-thread-protocol.js
--- a/lib/shared/threads/protocols/dm-thread-protocol.js
+++ b/lib/shared/threads/protocols/dm-thread-protocol.js
@@ -947,6 +947,7 @@
viewerCanUpdateOwnRole: false,
protocolName: protocolNames.COMM_DM,
canReactToRobotext: true,
+ supportsThreadRefreshing: false,
});
function pendingThreadType(numberOfOtherMembers: number) {
diff --git a/lib/shared/threads/protocols/farcaster-thread-protocol.js b/lib/shared/threads/protocols/farcaster-thread-protocol.js
--- a/lib/shared/threads/protocols/farcaster-thread-protocol.js
+++ b/lib/shared/threads/protocols/farcaster-thread-protocol.js
@@ -964,6 +964,7 @@
viewerCanUpdateOwnRole: false,
protocolName: protocolNames.FARCASTER_DC,
canReactToRobotext: false,
+ supportsThreadRefreshing: true,
};
function pendingThreadType(numberOfOtherMembers: number) {
diff --git a/lib/shared/threads/protocols/keyserver-thread-protocol.js b/lib/shared/threads/protocols/keyserver-thread-protocol.js
--- a/lib/shared/threads/protocols/keyserver-thread-protocol.js
+++ b/lib/shared/threads/protocols/keyserver-thread-protocol.js
@@ -751,6 +751,7 @@
viewerCanUpdateOwnRole: true,
protocolName: protocolNames.KEYSERVER,
canReactToRobotext: true,
+ supportsThreadRefreshing: false,
});
function pendingThreadType(numberOfOtherMembers: number) {
diff --git a/lib/shared/threads/thread-spec.js b/lib/shared/threads/thread-spec.js
--- a/lib/shared/threads/thread-spec.js
+++ b/lib/shared/threads/thread-spec.js
@@ -537,6 +537,7 @@
+viewerCanUpdateOwnRole: boolean,
+protocolName: ProtocolName,
+canReactToRobotext: boolean,
+ +supportsThreadRefreshing: boolean,
};
export type ThreadSpec<
diff --git a/native/chat/settings/thread-settings-refresh.react.js b/native/chat/settings/thread-settings-refresh.react.js
new file mode 100644
--- /dev/null
+++ b/native/chat/settings/thread-settings-refresh.react.js
@@ -0,0 +1,85 @@
+// @flow
+
+import Icon from '@expo/vector-icons/Ionicons.js';
+import * as React from 'react';
+import { View, Text, Platform } from 'react-native';
+
+import Button from '../../components/button.react.js';
+import { useStyles } from '../../themes/colors.js';
+
+const unboundStyles = {
+ container: {
+ flex: 1,
+ flexDirection: 'row',
+ paddingHorizontal: 12,
+ paddingVertical: 8,
+ justifyContent: 'center',
+ },
+ icon: {
+ lineHeight: 20,
+ },
+ refreshButton: {
+ paddingTop: Platform.OS === 'ios' ? 4 : 1,
+ },
+ refreshIcon: {
+ color: 'panelForegroundSecondaryLabel',
+ },
+ refreshRow: {
+ backgroundColor: 'panelForeground',
+ paddingHorizontal: 12,
+ },
+ refreshText: {
+ color: 'panelForegroundSecondaryLabel',
+ flex: 1,
+ fontSize: 16,
+ },
+ disabled: {
+ color: 'disabledButtonText',
+ },
+};
+
+type RefreshProps = {
+ +onPress: () => Promise<void>,
+};
+
+function ThreadSettingsRefresh(props: RefreshProps): React.Node {
+ const styles = useStyles(unboundStyles);
+ const [isRefreshing, setIsRefreshing] = React.useState(false);
+
+ const onPressWrapper = React.useCallback(async () => {
+ if (isRefreshing) {
+ return;
+ }
+ setIsRefreshing(true);
+ try {
+ await props.onPress();
+ } finally {
+ setIsRefreshing(false);
+ }
+ }, [isRefreshing, props]);
+
+ const disabledStyle = isRefreshing ? styles.disabled : null;
+
+ return (
+ <View style={styles.refreshRow}>
+ <Button
+ onPress={onPressWrapper}
+ style={styles.refreshButton}
+ disabled={isRefreshing}
+ >
+ <View style={styles.container}>
+ <Text style={[styles.refreshText, disabledStyle]}>
+ {isRefreshing ? 'Refreshing...' : 'Refresh thread'}
+ </Text>
+ <Icon
+ name="refresh"
+ size={20}
+ style={[styles.icon, styles.refreshIcon, disabledStyle]}
+ />
+ </View>
+ </Button>
+ </View>
+ );
+}
+
+export default ThreadSettingsRefresh;
diff --git a/native/chat/settings/thread-settings.react.js b/native/chat/settings/thread-settings.react.js
--- a/native/chat/settings/thread-settings.react.js
+++ b/native/chat/settings/thread-settings.react.js
@@ -31,6 +31,8 @@
childThreadInfos,
threadInfoSelector,
} from 'lib/selectors/thread-selectors.js';
+import { useRefreshFarcasterConversation } from 'lib/shared/farcaster/farcaster-hooks.js';
+import { conversationIDFromFarcasterThreadID } from 'lib/shared/id-utils.js';
import { getAvailableRelationshipButtons } from 'lib/shared/relationship-utils.js';
import {
getSingleOtherUser,
@@ -84,6 +86,7 @@
import ThreadSettingsParent from './thread-settings-parent.react.js';
import ThreadSettingsPromoteSidebar from './thread-settings-promote-sidebar.react.js';
import ThreadSettingsPushNotifs from './thread-settings-push-notifs.react.js';
+import ThreadSettingsRefresh from './thread-settings-refresh.react.js';
import ThreadSettingsVisibility from './thread-settings-visibility.react.js';
import ThreadAncestors from '../../components/thread-ancestors.react.js';
import {
@@ -234,7 +237,7 @@
+verticalBounds: ?VerticalBounds,
}
| {
- +itemType: 'promoteSidebar' | 'leaveThread' | 'deleteThread',
+ +itemType: 'promoteSidebar' | 'leaveThread' | 'deleteThread' | 'refresh',
+key: string,
+threadInfo: ResolvedThreadInfo,
+navigate: ThreadSettingsNavigate,
@@ -296,6 +299,7 @@
+canDeleteThread: boolean,
+canManageInviteLinks: boolean,
+inviteLinkExists: boolean,
+ +refreshFarcasterConversation: (threadID: string) => Promise<void>,
};
type State = {
+numMembersShowing: number,
@@ -758,6 +762,17 @@
) => {
const buttons = [];
+ const supportsThreadRefreshing =
+ threadSpecs[threadInfo.type].protocol().supportsThreadRefreshing;
+ if (supportsThreadRefreshing) {
+ buttons.push({
+ itemType: 'refresh',
+ key: 'refresh',
+ threadInfo,
+ navigate,
+ });
+ }
+
if (this.props.canPromoteSidebar) {
buttons.push({
itemType: 'promoteSidebar',
@@ -1055,6 +1070,8 @@
buttonStyle={item.buttonStyle}
/>
);
+ } else if (item.itemType === 'refresh') {
+ return <ThreadSettingsRefresh onPress={this.onPressRefresh} />;
} else if (item.itemType === 'deleteThread') {
return (
<ThreadSettingsDeleteThread
@@ -1152,6 +1169,11 @@
threadInfo: this.props.threadInfo,
});
};
+
+ onPressRefresh = async () => {
+ const { refreshFarcasterConversation, threadInfo } = this.props;
+ await refreshFarcasterConversation(threadInfo.id);
+ };
}
const threadMembersChangeIsSaving = (
@@ -1347,6 +1369,19 @@
);
}, [callFetchPrimaryLinks, dispatchActionPromise, isCommunityRoot]);
+ const refreshFarcasterConversationHook = useRefreshFarcasterConversation();
+ const refreshFarcasterConversation = React.useCallback(
+ async (farcasterThreadID: string) => {
+ const conversationID =
+ conversationIDFromFarcasterThreadID(farcasterThreadID);
+ await refreshFarcasterConversationHook(
+ conversationID,
+ Number.POSITIVE_INFINITY,
+ );
+ },
+ [refreshFarcasterConversationHook],
+ );
+
return (
<ThreadSettings
{...props}
@@ -1371,6 +1406,7 @@
canDeleteThread={canDeleteThread}
canManageInviteLinks={canManageLinks}
inviteLinkExists={!!inviteLink}
+ refreshFarcasterConversation={refreshFarcasterConversation}
/>
);
},
diff --git a/web/chat/thread-menu.react.js b/web/chat/thread-menu.react.js
--- a/web/chat/thread-menu.react.js
+++ b/web/chat/thread-menu.react.js
@@ -10,6 +10,8 @@
childThreadInfos,
otherUsersButNoOtherAdmins,
} from 'lib/selectors/thread-selectors.js';
+import { useRefreshFarcasterConversation } from 'lib/shared/farcaster/farcaster-hooks.js';
+import { conversationIDFromFarcasterThreadID } from 'lib/shared/id-utils.js';
import {
threadIsChannel,
useThreadHasPermission,
@@ -47,6 +49,43 @@
const { threadInfo } = props;
const { onPromoteSidebar, canPromoteSidebar } = usePromoteSidebar(threadInfo);
+ const [isRefreshing, setIsRefreshing] = React.useState(false);
+
+ const supportsThreadRefreshing =
+ threadSpecs[threadInfo.type].protocol().supportsThreadRefreshing;
+
+ const refreshFarcasterConversationHook = useRefreshFarcasterConversation();
+ const onClickRefresh = React.useCallback(async () => {
+ if (isRefreshing) {
+ return;
+ }
+ setIsRefreshing(true);
+ try {
+ const conversationID = conversationIDFromFarcasterThreadID(threadInfo.id);
+ await refreshFarcasterConversationHook(
+ conversationID,
+ Number.POSITIVE_INFINITY,
+ );
+ } finally {
+ setIsRefreshing(false);
+ }
+ }, [isRefreshing, threadInfo.id, refreshFarcasterConversationHook]);
+
+ const refreshItem = React.useMemo(() => {
+ if (!supportsThreadRefreshing) {
+ return null;
+ }
+ return (
+ <MenuItem
+ key="refresh"
+ text={isRefreshing ? 'Refreshing...' : 'Refresh thread'}
+ icon="refresh"
+ onClick={onClickRefresh}
+ disabled={isRefreshing}
+ />
+ );
+ }, [supportsThreadRefreshing, isRefreshing, onClickRefresh]);
+
const onClickSettings = React.useCallback(
() => pushModal(<ThreadSettingsModal threadID={threadInfo.id} />),
[pushModal, threadInfo.id],
@@ -303,6 +342,7 @@
notificationsItem,
membersItem,
threadMediaGalleryItem,
+ refreshItem,
sidebarItem,
viewSubchannelsItem,
createSubchannelsItem,
@@ -316,6 +356,7 @@
notificationsItem,
membersItem,
threadMediaGalleryItem,
+ refreshItem,
sidebarItem,
viewSubchannelsItem,
promoteSidebar,
diff --git a/web/components/menu-item.react.js b/web/components/menu-item.react.js
--- a/web/components/menu-item.react.js
+++ b/web/components/menu-item.react.js
@@ -14,6 +14,7 @@
+onClick?: () => mixed,
+text: string,
+dangerous?: boolean,
+ +disabled?: boolean,
};
export type MenuItemProps =
| {
@@ -26,7 +27,7 @@
};
function MenuItem(props: MenuItemProps): React.Node {
- const { onClick, icon, iconComponent, text, dangerous } = props;
+ const { onClick, icon, iconComponent, text, dangerous, disabled } = props;
const itemClasses = classNames(css.menuAction, {
[css.menuActionDangerous]: dangerous,
@@ -38,7 +39,7 @@
}
return (
- <Button className={itemClasses} onClick={onClick}>
+ <Button className={itemClasses} onClick={onClick} disabled={disabled}>
<div className={css.menuActionIcon}>{menuItemIcon}</div>
<div>{text}</div>
</Button>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Dec 6, 3:43 PM (21 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5839313
Default Alt Text
D15410.1765035820.diff (11 KB)
Attached To
Mode
D15410: [lib] Add an option to refresh a thread
Attached
Detach File
Event Timeline
Log In to Comment