2025-01-27 23:51:10 +01:00

431 lines
12 KiB
TypeScript

import type { ActionReturnType } from '../../types';
import { copyTextToClipboard } from '../../../util/clipboard';
import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { omit } from '../../../util/iteratees';
import * as langProvider from '../../../util/oldLangProvider';
import { callApi } from '../../../api/gramjs';
import { addActionHandler, getGlobal, setGlobal } from '../../index';
import { addStoriesForPeer } from '../../reducers';
import { updateTabState } from '../../reducers/tabs';
import {
selectCurrentViewedStory,
selectPeer,
selectPeerFirstStoryId,
selectPeerFirstUnreadStoryId,
selectPeerStories,
selectStoryListForViewer,
selectTabState,
} from '../../selectors';
import { fetchChatByUsername } from '../api/chats';
addActionHandler('openStoryViewer', async (global, actions, payload): Promise<void> => {
const {
peerId, storyId, isSinglePeer, isSingleStory, isPrivate, isArchive, origin, tabId = getCurrentTabId(),
} = payload;
const peer = selectPeer(global, peerId);
if (!peer) {
return;
}
const tabState = selectTabState(global, tabId);
const peerStories = selectPeerStories(global, peerId);
if (storyId && (!peerStories || !peerStories.byId[storyId])) {
const result = await callApi('fetchPeerStoriesByIds', { peer, ids: [storyId] });
if (!result) {
return;
}
global = getGlobal();
global = addStoriesForPeer(global, peerId, result.stories);
}
const storyList = tabState.storyViewer.storyList
|| selectStoryListForViewer(global, peerId, storyId, isSingleStory, isSinglePeer, isPrivate, isArchive);
global = updateTabState(global, {
storyViewer: {
...tabState.storyViewer,
peerId,
storyId: storyId || selectPeerFirstUnreadStoryId(global, peerId) || selectPeerFirstStoryId(global, peerId),
isSinglePeer,
isPrivate,
isArchive,
isSingleStory,
viewModal: undefined,
origin,
storyList,
},
}, tabId);
setGlobal(global);
});
addActionHandler('openStoryViewerByUsername', async (global, actions, payload): Promise<void> => {
const {
username, storyId, origin, tabId = getCurrentTabId(),
} = payload;
const chat = await fetchChatByUsername(global, username);
if (!chat) {
return;
}
actions.openStoryViewer({
peerId: chat.id,
storyId,
isSinglePeer: true,
isSingleStory: true,
origin,
tabId,
});
});
addActionHandler('closeStoryViewer', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
const {
isMuted, isRibbonShown, isArchivedRibbonShown, storyId,
} = selectTabState(global, tabId).storyViewer;
if (!storyId) return global;
global = updateTabState(global, {
storyViewer: {
isMuted,
isRibbonShown,
isArchivedRibbonShown,
lastViewedByPeerIds: undefined,
storyList: undefined,
},
}, tabId);
return global;
});
addActionHandler('setStoryViewerMuted', (global, actions, payload): ActionReturnType => {
const {
isMuted,
tabId = getCurrentTabId(),
} = payload;
return updateTabState(global, {
storyViewer: {
...selectTabState(global, tabId).storyViewer,
isMuted,
},
}, tabId);
});
addActionHandler('toggleStoryRibbon', (global, actions, payload): ActionReturnType => {
const { isShown, isArchived, tabId = getCurrentTabId() } = payload;
const orderedIds = global.stories.orderedPeerIds[isArchived ? 'archived' : 'active'];
if (!orderedIds?.length) {
return global;
}
return updateTabState(global, {
storyViewer: {
...selectTabState(global, tabId).storyViewer,
[isArchived ? 'isArchivedRibbonShown' : 'isRibbonShown']: isShown,
},
}, tabId);
});
addActionHandler('openPreviousStory', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
const tabState = selectTabState(global, tabId);
const {
peerId, storyId, isSinglePeer, isSingleStory, storyList,
} = tabState.storyViewer;
if (isSingleStory || !storyList) {
actions.closeStoryViewer({ tabId });
return undefined;
}
if (!peerId || !storyId) {
return undefined;
}
const peer = selectPeer(global, peerId);
const peerStories = selectPeerStories(global, peerId);
if (!peerStories || !peer) {
return undefined;
}
const { peerIds: orderedPeerIds, storyIdsByPeerId } = storyList;
const peerStoryIds = storyIdsByPeerId[peerId] ?? [];
const currentStoryIndex = peerStoryIds.indexOf(storyId);
let previousStoryIndex: number;
let previousPeerId: string;
if (currentStoryIndex > 0) {
previousStoryIndex = currentStoryIndex - 1;
previousPeerId = peerId;
} else {
const previousPeerIdIndex = orderedPeerIds.indexOf(peerId) - 1;
if (isSinglePeer || previousPeerIdIndex < 0) {
return undefined;
}
previousPeerId = orderedPeerIds[previousPeerIdIndex];
previousStoryIndex = (storyIdsByPeerId?.[previousPeerId]?.length || 1) - 1;
}
const previousStoryId = storyIdsByPeerId?.[previousPeerId]?.[previousStoryIndex];
if (!previousStoryId) {
return undefined;
}
return updateTabState(global, {
storyViewer: {
...tabState.storyViewer,
peerId: previousPeerId,
storyId: previousStoryId,
},
}, tabId);
});
addActionHandler('openNextStory', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
const tabState = selectTabState(global, tabId);
const {
peerId, storyId, isSinglePeer, isSingleStory, storyList,
} = tabState.storyViewer;
if (isSingleStory || !storyList) {
actions.closeStoryViewer({ tabId });
return undefined;
}
if (!peerId || !storyId) {
return undefined;
}
const peer = selectPeer(global, peerId);
const peerStories = selectPeerStories(global, peerId);
if (!peerStories || !peer) {
return undefined;
}
const { peerIds: orderedPeerIds, storyIdsByPeerId } = storyList;
const peerStoryIds = storyIdsByPeerId[peerId] ?? [];
const currentStoryIndex = peerStoryIds.indexOf(storyId);
let nextStoryIndex: number;
let nextPeerId: string;
if (currentStoryIndex < peerStoryIds.length - 1) {
nextStoryIndex = currentStoryIndex + 1;
nextPeerId = peerId;
} else {
const nextPeerIdIndex = orderedPeerIds.indexOf(peerId) + 1;
if (isSinglePeer || nextPeerIdIndex > orderedPeerIds.length - 1) {
actions.closeStoryViewer({ tabId });
return undefined;
}
nextPeerId = orderedPeerIds[nextPeerIdIndex];
nextStoryIndex = 0;
}
const nextStoryId = storyIdsByPeerId?.[nextPeerId]?.[nextStoryIndex];
if (!nextStoryId) {
return undefined;
}
return updateTabState(global, {
storyViewer: {
...tabState.storyViewer,
peerId: nextPeerId,
storyId: nextStoryId,
},
}, tabId);
});
addActionHandler('openStoryViewModal', (global, actions, payload): ActionReturnType => {
const { storyId, tabId = getCurrentTabId() } = payload;
const tabState = selectTabState(global, tabId);
return updateTabState(global, {
storyViewer: {
...tabState.storyViewer,
viewModal: {
storyId,
nextOffset: '',
isLoading: true,
},
},
}, tabId);
});
addActionHandler('closeStoryViewModal', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
const tabState = selectTabState(global, tabId);
return updateTabState(global, {
storyViewer: omit(tabState.storyViewer, ['viewModal']),
}, tabId);
});
addActionHandler('copyStoryLink', async (global, actions, payload): Promise<void> => {
const { peerId, storyId, tabId = getCurrentTabId() } = payload;
const peer = selectPeer(global, peerId);
if (!peer) {
return;
}
const link = await callApi('fetchStoryLink', { peer, storyId });
if (!link) {
return;
}
copyTextToClipboard(link);
actions.showNotification({
message: langProvider.oldTranslate('LinkCopied'),
tabId,
});
});
addActionHandler('sendMessage', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload;
const { storyId, peerId: storyPeerId } = selectCurrentViewedStory(global, tabId);
const isStoryReply = Boolean(storyId && storyPeerId);
if (!isStoryReply) {
return;
}
const { gif, sticker, isReaction } = payload;
let message: string;
if (gif) {
message = 'Story.Tooltip.GifSent';
} else if (sticker) {
message = 'Story.Tooltip.StickerSent';
} else if (isReaction) {
message = 'Story.Tooltip.ReactionSent';
} else {
message = 'Story.Tooltip.MessageSent';
}
actions.showNotification({
message: langProvider.oldTranslate(message),
actionText: langProvider.oldTranslate('Story.ToastViewInChat'),
action: [{
action: 'closeStoryViewer',
payload: undefined,
}, {
action: 'openChat',
payload: { id: storyPeerId },
}],
tabId,
});
});
addActionHandler('openStoryPrivacyEditor', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
const tabState = selectTabState(global, tabId);
return updateTabState(global, {
storyViewer: {
...tabState.storyViewer,
isPrivacyModalOpen: true,
},
}, tabId);
});
addActionHandler('closeStoryPrivacyEditor', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
const tabState = selectTabState(global, tabId);
return updateTabState(global, {
storyViewer: {
...tabState.storyViewer,
isPrivacyModalOpen: false,
},
}, tabId);
});
addActionHandler('toggleStealthModal', (global, actions, payload): ActionReturnType => {
const { isOpen, tabId = getCurrentTabId() } = payload || {};
const tabState = selectTabState(global, tabId);
return updateTabState(global, {
storyViewer: {
...tabState.storyViewer,
isStealthModalOpen: isOpen,
},
}, tabId);
});
addActionHandler('clearStoryViews', (global, actions, payload): ActionReturnType => {
const { isLoading, tabId = getCurrentTabId() } = payload || {};
const tabState = selectTabState(global, tabId);
if (!tabState.storyViewer.viewModal) return global;
return updateTabState(global, {
storyViewer: {
...tabState.storyViewer,
viewModal: {
...tabState.storyViewer.viewModal,
views: undefined,
isLoading,
nextOffset: '',
},
},
}, tabId);
});
addActionHandler('updateStoryView', (global, actions, payload): ActionReturnType => {
const {
userId, isUserBlocked, areStoriesBlocked, tabId = getCurrentTabId(),
} = payload;
const tabState = selectTabState(global, tabId);
const { viewModal } = tabState.storyViewer;
if (!viewModal?.storyId) return undefined;
const updatedViews = viewModal?.views?.map((view) => {
if (view.peerId === userId) {
return {
...view,
isUserBlocked: isUserBlocked || undefined,
areStoriesBlocked: areStoriesBlocked || undefined,
};
}
return view;
});
return updateTabState(global, {
storyViewer: {
...tabState.storyViewer,
viewModal: {
...viewModal,
views: updatedViews,
},
},
}, tabId);
});
addActionHandler('closeBoostModal', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
return updateTabState(global, {
boostModal: undefined,
}, tabId);
});
addActionHandler('closeBoostStatistics', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
return updateTabState(global, {
boostStatistics: undefined,
}, tabId);
});