1050 lines
42 KiB
JavaScript
1050 lines
42 KiB
JavaScript
/**
|
|
* TikTok Swiper Mode and Scrolling - Deobfuscated JavaScript Bundle
|
|
* Original files: 89650.836eaa0d.js + 60248.341443e1.js
|
|
*
|
|
* This bundle contains modules for:
|
|
* - Swiper mode navigation (next/previous video)
|
|
* - Comment system management
|
|
* - User interaction tracking
|
|
* - Video feed scrolling mechanics
|
|
* - Live streaming analytics
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
// Initialize loadable chunks array
|
|
(self.__LOADABLE_LOADED_CHUNKS__ = self.__LOADABLE_LOADED_CHUNKS__ || []).push([["89650"], {
|
|
|
|
/**
|
|
* Module 15305: Component Name Utilities
|
|
* Helper functions for component identification
|
|
*/
|
|
15305: function(exports, module, require) {
|
|
require.d(exports, {
|
|
x: function() { return getComponentName; }
|
|
});
|
|
|
|
/**
|
|
* Get component name for debugging and analytics
|
|
*/
|
|
function getComponentName(component) {
|
|
var displayName = component.displayName;
|
|
var name = component.name;
|
|
return displayName || name || "UnknownComponent";
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Module 45615: Video Path Generation and Photo Mode
|
|
* URL generation for video and photo content
|
|
*/
|
|
45615: function(exports, module, require) {
|
|
require.d(exports, {
|
|
Nw: function() { return generateVideoPath; },
|
|
Rj: function() { return PHOTO_MODE_AWEME_TYPE; },
|
|
_u: function() { return useVideoPathGenerator; }
|
|
});
|
|
|
|
var validationUtils = require(56110);
|
|
var objectUtils = require(31032);
|
|
var React = require(40099);
|
|
var notificationTypes = require(48007);
|
|
var contextUtils = require(35144);
|
|
var routeUtils = require(63230);
|
|
var botTypes = require(56243);
|
|
var stateUtils = require(72961);
|
|
var selectorUtils = require(43264);
|
|
var urlUtils = require(80863);
|
|
|
|
var PHOTO_MODE_AWEME_TYPE = 150;
|
|
|
|
/**
|
|
* Generate video/photo path based on content type and user settings
|
|
*/
|
|
function generateVideoPath(contentData, options) {
|
|
options = options || {};
|
|
var defaultPath = options.defaultPath !== undefined ? options.defaultPath : "";
|
|
var featureParams = options.featureParams;
|
|
|
|
if (validationUtils.A(contentData)) {
|
|
return defaultPath;
|
|
}
|
|
|
|
var isPhotoModeV1 = featureParams && featureParams.photomodeVid === "v1";
|
|
|
|
// Handle different content data structures
|
|
if (contentData.author && contentData.author.uniqueId) {
|
|
return generateVideoPath({
|
|
uniqueId: contentData.author.uniqueId,
|
|
secUid: contentData.author.secUid,
|
|
videoId: contentData.id,
|
|
isPhotomode: contentData.imagePost !== undefined
|
|
}, options);
|
|
}
|
|
|
|
if (contentData.authorId) {
|
|
return generateVideoPath({
|
|
uniqueId: contentData.author,
|
|
secUid: contentData.authorSecId,
|
|
videoId: contentData.id,
|
|
isPhotomode: contentData.imagePost !== undefined
|
|
}, options);
|
|
}
|
|
|
|
// Handle @ mention notifications
|
|
if (contentData.at) {
|
|
var aweme = contentData.at.aweme;
|
|
var contentType = (aweme && aweme.aweme_type === PHOTO_MODE_AWEME_TYPE && isPhotoModeV1) ?
|
|
"photo" : "video";
|
|
return "/@" + aweme.author.uid + "/" + contentType + "/" + aweme.aweme_id;
|
|
}
|
|
|
|
// Handle comment notifications
|
|
if (contentData.comment) {
|
|
var commentAweme = contentData.comment.aweme;
|
|
var commentContentType = (commentAweme && commentAweme.aweme_type === PHOTO_MODE_AWEME_TYPE && isPhotoModeV1) ?
|
|
"photo" : "video";
|
|
return "/@" + commentAweme.author.uid + "/" + commentContentType + "/" + commentAweme.aweme_id;
|
|
}
|
|
|
|
// Handle like notifications
|
|
if (contentData.digg) {
|
|
var diggAweme = contentData.digg.aweme;
|
|
var diggContentType = (diggAweme && diggAweme.aweme_type === PHOTO_MODE_AWEME_TYPE && isPhotoModeV1) ?
|
|
"photo" : "video";
|
|
return "/@" + diggAweme.author.uid + "/" + diggContentType + "/" + diggAweme.aweme_id;
|
|
}
|
|
|
|
// Handle template notifications
|
|
if (contentData.template_notice) {
|
|
var templateData = parseTemplateNotice(contentData);
|
|
var templateContentType = templateData.isPhotoMode && isPhotoModeV1 ? "photo" : "video";
|
|
return "/@" + templateData.authorId + "/" + templateContentType + "/" + templateData.awemeId;
|
|
}
|
|
|
|
// Handle direct owner/object ID structure
|
|
if (contentData.owner_id && contentData.object_id) {
|
|
return generateVideoPath({
|
|
uniqueId: contentData.owner_id,
|
|
videoId: contentData.object_id,
|
|
isPhotomode: contentData.aweme_type === PHOTO_MODE_AWEME_TYPE
|
|
}, options);
|
|
}
|
|
|
|
// Handle standard video path generation
|
|
var uniqueId = contentData.uniqueId;
|
|
var secUid = contentData.secUid;
|
|
var videoId = contentData.videoId;
|
|
var isPhotomode = contentData.isPhotomode;
|
|
|
|
var processedUniqueId = urlUtils.l3({ uniqueId: uniqueId, secUid: secUid });
|
|
|
|
if (processedUniqueId && videoId) {
|
|
if (isPhotomode && isPhotoModeV1) {
|
|
return routeUtils.Lj.photo({ uniqueId: processedUniqueId, id: videoId });
|
|
} else {
|
|
return routeUtils.Lj.video({ uniqueId: processedUniqueId, id: videoId });
|
|
}
|
|
}
|
|
|
|
return defaultPath;
|
|
}
|
|
|
|
/**
|
|
* Parse template notice data for URL generation
|
|
*/
|
|
function parseTemplateNotice(contentData) {
|
|
var nudgeInfo = contentData.template_notice.extra_data ?
|
|
contentData.template_notice.extra_data.nudge_info : undefined;
|
|
var notice = contentData.template_notice.notice;
|
|
var nudgeData = nudgeInfo || {};
|
|
|
|
var awemeId = nudgeData.aweme_id;
|
|
var authorId = nudgeData.author_id;
|
|
var coverUrl = nudgeData.cover_url;
|
|
var awemeType = nudgeData.aweme_type;
|
|
|
|
// Fallback cover URL extraction
|
|
if (!coverUrl && notice && notice.image_url && notice.image_url.url_list) {
|
|
coverUrl = notice.image_url.url_list[0];
|
|
}
|
|
|
|
// Fallback aweme ID extraction
|
|
if (!awemeId && notice && notice.right_schema_url) {
|
|
var match = notice.right_schema_url.match(/\/detail\/(\d+)/);
|
|
awemeId = match ? match[1] : undefined;
|
|
}
|
|
|
|
// Handle creator video repost notices
|
|
if (!authorId && contentData.type === notificationTypes.TDV.CreatorVideoRepostNotice) {
|
|
var userData = contextUtils.x().user;
|
|
authorId = userData ? userData.uid : undefined;
|
|
}
|
|
|
|
return {
|
|
isPhotoMode: awemeType === PHOTO_MODE_AWEME_TYPE || (coverUrl && coverUrl.includes("photomode")),
|
|
awemeId: awemeId,
|
|
authorId: authorId,
|
|
coverUrl: coverUrl
|
|
};
|
|
}
|
|
|
|
/**
|
|
* React hook for video path generation with photo mode support
|
|
*/
|
|
function useVideoPathGenerator() {
|
|
var contextData = stateUtils.L$(selectorUtils.W(function() {
|
|
return ["abTestVersion", "botType"];
|
|
}, []));
|
|
|
|
var abTestVersion = contextData.abTestVersion;
|
|
var botType = contextData.botType;
|
|
|
|
// Determine photo mode version based on bot type and A/B test
|
|
var photoModeVersion = botType === botTypes.Y.NotBot ?
|
|
(abTestVersion && abTestVersion.parameters.webapp_seo_photomode_user_exp ?
|
|
abTestVersion.parameters.webapp_seo_photomode_user_exp.vid || "v0" : "v0") :
|
|
"v1";
|
|
|
|
return React.useCallback(function(contentData, options) {
|
|
return generateVideoPath(contentData, objectUtils.A({
|
|
featureParams: {
|
|
photomodeVid: photoModeVersion
|
|
}
|
|
}, options));
|
|
}, [photoModeVersion]);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Module 32878: Swiper Mode Module
|
|
* Core swiper navigation for video feed
|
|
*/
|
|
32878: function(exports, module, require) {
|
|
require.d(exports, {
|
|
AT: function() { return IconType; },
|
|
RW: function() { return SeekType; },
|
|
aL: function() { return SwiperModeModule; }
|
|
});
|
|
|
|
var moduleBase = require(48748);
|
|
var classUtils = require(95170);
|
|
var objectUtils = require(35383);
|
|
var inheritanceUtils = require(7120);
|
|
var assignUtils = require(5377);
|
|
var mergeUtils = require(45996);
|
|
var arrayUtils = require(6586);
|
|
var spreadUtils = require(54333);
|
|
var typeUtils = require(79262);
|
|
var observableUtils = require(23999);
|
|
var rxjsUtils = require(19293);
|
|
var tapOperator = require(95719);
|
|
var combineLatestOperator = require(24451);
|
|
var mapOperator = require(62564);
|
|
var switchMapOperator = require(68710);
|
|
var moduleSystem = require(78990);
|
|
var decoratorUtils = require(82379);
|
|
var moduleDecorator = require(1455);
|
|
var deviceUtils = require(69597);
|
|
var itemListKeys = require(38306);
|
|
var switchMapUtils = require(9659);
|
|
var itemListModule = require(49244);
|
|
var videoPlayerModule = require(10354);
|
|
|
|
/**
|
|
* Icon types for video player controls
|
|
*/
|
|
var IconType = {
|
|
None: "none",
|
|
Mute: "mute",
|
|
Unmute: "unmute",
|
|
Play: "play",
|
|
Pause: "pause"
|
|
};
|
|
|
|
/**
|
|
* Seek operation types
|
|
*/
|
|
var SeekType = {
|
|
Forward: "forward",
|
|
BackWard: "backward",
|
|
None: "none"
|
|
};
|
|
|
|
/**
|
|
* Swiper Mode Module
|
|
* Handles video navigation in swiper/feed mode
|
|
*/
|
|
var SwiperModeModule = function(baseModule) {
|
|
function SwiperModeModule(itemListService, videoPlayerJotai, videoExperience) {
|
|
var instance;
|
|
classUtils._(this, SwiperModeModule);
|
|
instance = moduleBase._(this, SwiperModeModule);
|
|
|
|
instance.itemList = itemListService;
|
|
instance.videoPlayerJotai = videoPlayerJotai;
|
|
instance.videoExperience = videoExperience;
|
|
|
|
instance.defaultState = {
|
|
currentIndex: {}, // Current video indices by list key
|
|
disabled: false, // Whether navigation is disabled
|
|
itemListKey: itemListKeys.Lz.ForYou, // Current item list key
|
|
onboardingShowing: false, // Onboarding modal visibility
|
|
loginCTAShowing: false, // Login CTA visibility
|
|
leavingModalShowing: false, // Leaving modal visibility
|
|
playbackRate: 1, // Video playback rate
|
|
dimmer: false, // Screen dimmer state
|
|
showBrowseMode: false, // Browse mode visibility
|
|
needLeavingModal: false, // Whether leaving modal is needed
|
|
iconType: "none", // Current icon type
|
|
seekType: "none" // Current seek type
|
|
};
|
|
|
|
return instance;
|
|
}
|
|
|
|
inheritanceUtils._(SwiperModeModule, baseModule);
|
|
|
|
var prototype = SwiperModeModule.prototype;
|
|
|
|
/**
|
|
* Set the current item list key
|
|
*/
|
|
prototype.setItemListKey = function(state, itemListKey) {
|
|
state.itemListKey = itemListKey;
|
|
};
|
|
|
|
/**
|
|
* Set current video index for a specific list
|
|
*/
|
|
prototype.setCurrentIndex = function(state, indexData) {
|
|
var key = indexData.key;
|
|
var value = indexData.value;
|
|
state.currentIndex = mergeUtils._(assignUtils._(assignUtils._({}, state.currentIndex),
|
|
objectUtils._(objectUtils._({}, key, value)));
|
|
};
|
|
|
|
/**
|
|
* Set navigation disabled state
|
|
*/
|
|
prototype.setDisabled = function(state, isDisabled) {
|
|
state.disabled = isDisabled;
|
|
};
|
|
|
|
/**
|
|
* Set whether leaving modal is needed
|
|
*/
|
|
prototype.setNeedLeavingModal = function(state, isNeeded) {
|
|
state.needLeavingModal = isNeeded;
|
|
};
|
|
|
|
/**
|
|
* Set onboarding modal visibility
|
|
*/
|
|
prototype.setOnboardingShowing = function(state, isShowing) {
|
|
state.onboardingShowing = isShowing;
|
|
};
|
|
|
|
/**
|
|
* Set login CTA visibility
|
|
*/
|
|
prototype.setLoginCTAShowing = function(state, isShowing) {
|
|
state.loginCTAShowing = isShowing;
|
|
};
|
|
|
|
/**
|
|
* Set leaving modal visibility
|
|
*/
|
|
prototype.setLeavingModalShowing = function(state, isShowing) {
|
|
state.leavingModalShowing = isShowing;
|
|
};
|
|
|
|
/**
|
|
* Set video playback rate
|
|
*/
|
|
prototype.setPlaybackRate = function(state, rate) {
|
|
state.playbackRate = rate;
|
|
};
|
|
|
|
/**
|
|
* Set screen dimmer state
|
|
*/
|
|
prototype.setDimmer = function(state, isDimmed) {
|
|
state.dimmer = isDimmed;
|
|
};
|
|
|
|
/**
|
|
* Set browse mode visibility
|
|
*/
|
|
prototype.setShowBrowseMode = function(state, isShowing) {
|
|
state.showBrowseMode = isShowing;
|
|
};
|
|
|
|
/**
|
|
* Set current icon type
|
|
*/
|
|
prototype.setIconType = function(state, iconType) {
|
|
state.iconType = iconType;
|
|
};
|
|
|
|
/**
|
|
* Set current seek type
|
|
*/
|
|
prototype.setSeekType = function(state, seekType) {
|
|
state.seekType = seekType;
|
|
};
|
|
|
|
/**
|
|
* Handle navigation to next video
|
|
* Core method for forward video navigation
|
|
*/
|
|
prototype.handleNextVideo = function(trigger$) {
|
|
var self = this;
|
|
|
|
return trigger$.pipe(
|
|
// Report interaction start
|
|
tapOperator.M(function() {
|
|
self.videoExperience.reportVideoInteractStart({
|
|
startTime: Date.now(),
|
|
situation: deviceUtils.uT.SwiperSlideNext
|
|
});
|
|
}),
|
|
|
|
// Combine with current state
|
|
combineLatestOperator.E(this.state$, this.itemList.state$),
|
|
|
|
// Calculate next video index
|
|
mapOperator.T(function(stateArray) {
|
|
var swiperState = stateArray[0];
|
|
var itemListState = stateArray[1];
|
|
var playMode = swiperState.playMode;
|
|
var itemListKey = itemListState.itemListKey;
|
|
var currentIndex = itemListState.currentIndex;
|
|
|
|
var browserList = itemListState[itemListKey] ?
|
|
itemListState[itemListKey].browserList || [] : [];
|
|
var currentIdx = currentIndex[itemListKey] || 0;
|
|
var nextIndex = Math.min(currentIdx + 1, browserList.length - 1);
|
|
var isIndexInvalid = !browserList.length || nextIndex < 0;
|
|
var newId = browserList[nextIndex] || "";
|
|
|
|
return {
|
|
newIndex: nextIndex,
|
|
isIndexInvalid: isIndexInvalid,
|
|
newId: newId,
|
|
playMode: playMode,
|
|
itemListKey: itemListKey
|
|
};
|
|
}),
|
|
|
|
// Log warnings for invalid indices
|
|
tapOperator.M(function(navigationData) {
|
|
if (navigationData.isIndexInvalid) {
|
|
console.warn("cannot switch to next video for some reasons");
|
|
}
|
|
}),
|
|
|
|
// Update video index
|
|
switchMapOperator.Z(function(navigationData) {
|
|
return observableUtils.of.apply(void 0, spreadUtils._(self.updateVideoIndex(navigationData)));
|
|
}),
|
|
|
|
switchMapUtils.n({})
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Handle navigation to previous video
|
|
* Core method for backward video navigation
|
|
*/
|
|
prototype.handlePrevVideo = function(trigger$) {
|
|
var self = this;
|
|
|
|
return trigger$.pipe(
|
|
// Report interaction start
|
|
tapOperator.M(function() {
|
|
self.videoExperience.reportVideoInteractStart({
|
|
startTime: Date.now(),
|
|
situation: deviceUtils.uT.SwiperSlidePrev
|
|
});
|
|
}),
|
|
|
|
// Combine with current state
|
|
combineLatestOperator.E(this.state$, this.itemList.state$),
|
|
|
|
// Calculate previous video index
|
|
mapOperator.T(function(stateArray) {
|
|
var swiperState = stateArray[0];
|
|
var itemListState = stateArray[1];
|
|
var playMode = swiperState.playMode;
|
|
var itemListKey = itemListState.itemListKey;
|
|
var currentIndex = itemListState.currentIndex;
|
|
|
|
var browserList = itemListState[itemListKey] ?
|
|
itemListState[itemListKey].browserList || [] : [];
|
|
var currentIdx = currentIndex[itemListKey] || 0;
|
|
var prevIndex = Math.max(currentIdx - 1, 0);
|
|
var isIndexInvalid = !browserList.length || prevIndex > browserList.length - 1;
|
|
var newId = browserList[prevIndex] || "";
|
|
|
|
return {
|
|
newIndex: prevIndex,
|
|
isIndexInvalid: isIndexInvalid,
|
|
newId: newId,
|
|
playMode: playMode,
|
|
itemListKey: itemListKey
|
|
};
|
|
}),
|
|
|
|
// Log warnings for invalid indices
|
|
tapOperator.M(function(navigationData) {
|
|
if (navigationData.isIndexInvalid) {
|
|
console.warn("cannot switch to prev video for some reasons");
|
|
}
|
|
}),
|
|
|
|
// Update video index
|
|
switchMapOperator.Z(function(navigationData) {
|
|
return observableUtils.of.apply(void 0, spreadUtils._(self.updateVideoIndex(navigationData)));
|
|
}),
|
|
|
|
switchMapUtils.n({})
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Update video index and player state
|
|
* Core method for video switching logic
|
|
*/
|
|
prototype.updateVideoIndex = function(navigationData) {
|
|
var newIndex = navigationData.newIndex;
|
|
var isIndexInvalid = navigationData.isIndexInvalid;
|
|
var newId = navigationData.newId;
|
|
var playMode = navigationData.playMode;
|
|
var itemListKey = navigationData.itemListKey;
|
|
|
|
if (isIndexInvalid) {
|
|
return [this.noop()];
|
|
}
|
|
|
|
return [
|
|
// Update video player with new video
|
|
this.videoPlayerJotai.getActions().updateVideo({
|
|
currentVideo: {
|
|
index: newIndex,
|
|
id: newId,
|
|
mode: playMode
|
|
},
|
|
playProgress: 0
|
|
}),
|
|
|
|
// Update current index in swiper state
|
|
this.getActions().setCurrentIndex({
|
|
key: itemListKey,
|
|
value: newIndex
|
|
})
|
|
];
|
|
};
|
|
|
|
return SwiperModeModule;
|
|
}(moduleSystem.E);
|
|
|
|
// Apply decorators to methods
|
|
function applyDecorators(decorators, target, propertyKey, descriptor) {
|
|
// Decorator application logic (simplified)
|
|
return descriptor;
|
|
}
|
|
|
|
// Apply action decorators to state setters
|
|
applyDecorators([{ h5: function() {} }], SwiperModeModule.prototype, "setItemListKey", null);
|
|
applyDecorators([{ h5: function() {} }], SwiperModeModule.prototype, "setCurrentIndex", null);
|
|
applyDecorators([{ h5: function() {} }], SwiperModeModule.prototype, "setDisabled", null);
|
|
applyDecorators([{ h5: function() {} }], SwiperModeModule.prototype, "setNeedLeavingModal", null);
|
|
applyDecorators([{ h5: function() {} }], SwiperModeModule.prototype, "setOnboardingShowing", null);
|
|
applyDecorators([{ h5: function() {} }], SwiperModeModule.prototype, "setLoginCTAShowing", null);
|
|
applyDecorators([{ h5: function() {} }], SwiperModeModule.prototype, "setLeavingModalShowing", null);
|
|
applyDecorators([{ h5: function() {} }], SwiperModeModule.prototype, "setPlaybackRate", null);
|
|
applyDecorators([{ h5: function() {} }], SwiperModeModule.prototype, "setDimmer", null);
|
|
applyDecorators([{ h5: function() {} }], SwiperModeModule.prototype, "setShowBrowseMode", null);
|
|
applyDecorators([{ h5: function() {} }], SwiperModeModule.prototype, "setIconType", null);
|
|
applyDecorators([{ h5: function() {} }], SwiperModeModule.prototype, "setSeekType", null);
|
|
|
|
// Apply effect decorators to handlers
|
|
applyDecorators([{ Mj: function() {} }], SwiperModeModule.prototype, "handleNextVideo", null);
|
|
applyDecorators([{ Mj: function() {} }], SwiperModeModule.prototype, "handlePrevVideo", null);
|
|
|
|
// Apply module decorator
|
|
SwiperModeModule = applyDecorators([{ nV: function(name) {} }], SwiperModeModule);
|
|
},
|
|
|
|
/**
|
|
* Module 82473: User Follow System
|
|
* User relationship management and follow functionality
|
|
*/
|
|
82473: function(exports, module, require) {
|
|
require.d(exports, {
|
|
Tu: function() { return useFollowButton; },
|
|
yS: function() { return useUserData; }
|
|
});
|
|
|
|
var objectUtils = require(35383);
|
|
var assignUtils = require(5377);
|
|
var mergeUtils = require(45996);
|
|
var arrayUtils = require(6586);
|
|
var React = require(40099);
|
|
var selectorUtils = require(11854);
|
|
var relationTypes = require(8561);
|
|
var notificationTypes = require(48007);
|
|
var analyticsUtils = require(37786);
|
|
var sceneTypes = require(68920);
|
|
var liveAnalytics = require(47045);
|
|
var userUtils = require(35379);
|
|
var contextUtils = require(43264);
|
|
var followUtils = require(93844);
|
|
var routerUtils = require(17505);
|
|
|
|
/**
|
|
* Get user data from state
|
|
*/
|
|
function useUserData(uniqueId) {
|
|
uniqueId = uniqueId !== undefined ? uniqueId : "";
|
|
return userUtils.nW(function(state) {
|
|
return state.users[uniqueId];
|
|
}, selectorUtils.bN);
|
|
}
|
|
|
|
/**
|
|
* Follow button text mapping based on relationship status
|
|
*/
|
|
var followButtonTextMap = {};
|
|
followButtonTextMap[relationTypes.yf.UNKNOW] = "Follow";
|
|
followButtonTextMap[relationTypes.yf.NONE] = "Follow";
|
|
followButtonTextMap[relationTypes.yf.FOLLOW] = "Following";
|
|
followButtonTextMap[relationTypes.yf.FOLLOWING_REQUEST] = "requested";
|
|
followButtonTextMap[relationTypes.yf.MUTAL] = "friends";
|
|
followButtonTextMap[relationTypes.yf.BLOCK] = "webapp_unblocked_button1";
|
|
followButtonTextMap[relationTypes.yf.BLOCKED] = "Follow";
|
|
followButtonTextMap[relationTypes.yf.FOLLOWER] = "Follow";
|
|
|
|
var FOLLOW_BACK_TEXT = "Inbox_Follow_back";
|
|
|
|
/**
|
|
* Get follow button text based on relationship status
|
|
*/
|
|
function getFollowButtonText(relationStatus, followerStatus) {
|
|
if (followerStatus === notificationTypes.FU4.FollowerFollowStatus) {
|
|
if (relationStatus < relationTypes.yf.FOLLOW) {
|
|
return FOLLOW_BACK_TEXT;
|
|
}
|
|
if (relationStatus === relationTypes.yf.FOLLOW) {
|
|
return followButtonTextMap[relationTypes.yf.MUTAL];
|
|
}
|
|
} else if (!followerStatus && relationStatus === relationTypes.yf.FOLLOWER) {
|
|
return FOLLOW_BACK_TEXT;
|
|
}
|
|
|
|
return followButtonTextMap[relationStatus];
|
|
}
|
|
|
|
/**
|
|
* React hook for follow button functionality
|
|
*/
|
|
function useFollowButton(params) {
|
|
var uniqueId = params.uniqueId;
|
|
var followerStatus = params.followerStatus;
|
|
var prevent = params.prevent;
|
|
var onNeedLogin = params.onNeedLogin;
|
|
var teaParams = params.teaParams || {};
|
|
var isInLiveCard = params.isInLiveCard !== undefined ? params.isInLiveCard : false;
|
|
var liveCardTeaParams = params.liveCardTeaParams || {};
|
|
var liveFollowStatus = params.liveFollowStatus;
|
|
|
|
// Get current user context
|
|
var userContext = contextUtils.W(function() {
|
|
return ["user"];
|
|
}, []);
|
|
var isLoggedIn = !!(userContext && userContext.user);
|
|
|
|
// Get user relationship data
|
|
var userRelationData = arrayUtils._(userUtils.JY(function(state) {
|
|
var user = state.users[uniqueId];
|
|
return {
|
|
relation: user && user.relation !== undefined ? user.relation : relationTypes.yf.UNKNOW,
|
|
secUid: user && user.secUid !== undefined ? user.secUid : ""
|
|
};
|
|
}, selectorUtils.bN), 2);
|
|
|
|
var relationData = userRelationData[0];
|
|
var relationStatus = relationData.relation;
|
|
var secUid = relationData.secUid;
|
|
var userActions = userRelationData[1];
|
|
|
|
// Update live card relation if needed
|
|
React.useEffect(function() {
|
|
if (isInLiveCard) {
|
|
userActions.setUserRelation({
|
|
uniqueId: uniqueId,
|
|
relation: Number(liveFollowStatus || 0)
|
|
});
|
|
}
|
|
}, []);
|
|
|
|
var useFollowV2 = followUtils.tcd();
|
|
var playMode = teaParams.play_mode;
|
|
var groupId = teaParams.group_id;
|
|
|
|
/**
|
|
* Handle follow button click
|
|
*/
|
|
var handleFollow = React.useCallback(function(event) {
|
|
// Prevent default if needed
|
|
if (prevent) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
|
|
// Track general click analytics
|
|
analyticsUtils.O.handleGeneralClick("follow", {
|
|
scene: teaParams.scene || sceneTypes.UH.VideoFeed,
|
|
group_id: groupId,
|
|
play_mode: playMode
|
|
});
|
|
|
|
if (!isLoggedIn) {
|
|
return onNeedLogin ? onNeedLogin() : undefined;
|
|
}
|
|
|
|
// Handle different relationship states
|
|
if (relationStatus === relationTypes.yf.BLOCK) {
|
|
// Handle unblock action
|
|
userActions.blockOrUnblockUser({
|
|
uniqueId: uniqueId,
|
|
secUid: secUid,
|
|
isBlock: true
|
|
});
|
|
} else {
|
|
// Handle follow/unfollow action
|
|
userActions.postCommitFollowUser(mergeUtils._(assignUtils._(assignUtils._({}, teaParams), {
|
|
uniqueId: uniqueId,
|
|
useFollowV2: useFollowV2
|
|
})));
|
|
|
|
// Track live card follow events
|
|
if (isInLiveCard) {
|
|
if (relationStatus === relationTypes.yf.FOLLOW) {
|
|
liveAnalytics.YH.handleUnFollow(liveCardTeaParams);
|
|
} else {
|
|
liveAnalytics.YH.handleFollow(liveCardTeaParams);
|
|
}
|
|
}
|
|
}
|
|
}, [prevent, isLoggedIn, relationStatus, userActions, teaParams, uniqueId, useFollowV2, isInLiveCard, onNeedLogin, secUid, liveCardTeaParams]);
|
|
|
|
// Get localized button text
|
|
var buttonText = followUtils.s()(getFollowButtonText(relationStatus, followerStatus));
|
|
|
|
return {
|
|
isFollowing: relationStatus === relationTypes.yf.FOLLOW || relationStatus === relationTypes.yf.MUTAL,
|
|
text: buttonText,
|
|
handleFollow: handleFollow,
|
|
relation: relationStatus
|
|
};
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Module 31926: Item Helper Functions
|
|
* Utility functions for item data access
|
|
*/
|
|
31926: function(exports, module, require) {
|
|
require.d(exports, {
|
|
$: function() { return getItemWithSelector; },
|
|
k: function() { return getItem; }
|
|
});
|
|
|
|
var selectorUtils = require(11854);
|
|
var itemUtils = require(72702);
|
|
|
|
/**
|
|
* Get item data by ID
|
|
*/
|
|
function getItem(itemId) {
|
|
return itemUtils.F3(function(state) {
|
|
return state[itemId];
|
|
}, selectorUtils.bN);
|
|
}
|
|
|
|
/**
|
|
* Get item data with custom selector
|
|
*/
|
|
function getItemWithSelector(itemId, selector) {
|
|
return itemUtils.F3(function(state) {
|
|
return selector(state[itemId]);
|
|
}, selectorUtils.bN);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Module 56243: Bot Type Detection
|
|
* Detection and classification of web crawlers/bots
|
|
*/
|
|
56243: function(exports, module, require) {
|
|
require.d(exports, {
|
|
Y: function() { return BotType; }
|
|
});
|
|
|
|
/**
|
|
* Bot type enumeration for SEO and crawler detection
|
|
*/
|
|
var BotType = {
|
|
NotBot: "others",
|
|
ValidGoogleBot: "Googlebot",
|
|
ValidBingBot: "Bingbot",
|
|
ValidSlurpBot: "Slurpbot",
|
|
ValidYandexBot: "Yandexbot",
|
|
ValidNaverBot: "Naverbot",
|
|
ValidDuckduckgoBot: "Duckduckgobot",
|
|
ValidNeevaBot: "Neevabot",
|
|
ValidYahooJPBot: "YahooJPbot",
|
|
ValidPerplexityBot: "PerplexityBot",
|
|
FakeGoogleBot: "FakeGooglebot",
|
|
FakeBingBot: "FakeBingbot",
|
|
FakeNaverBot: "FakeNaverbot",
|
|
FakeYahooJPBot: "FakeYahooJPbot",
|
|
FakeYandexBot: "FakeYandexbot",
|
|
FakeSlurpBot: "FakeSlurpbot"
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Module 68920: Scene Types and Analytics
|
|
* Scene classification for analytics tracking
|
|
*/
|
|
68920: function(exports, module, require) {
|
|
require.d(exports, {
|
|
CZ: function() { return ClickType; },
|
|
UH: function() { return SceneType; },
|
|
mr: function() { return channelAnalytics; }
|
|
});
|
|
|
|
var objectUtils = require(5377);
|
|
var teaAnalytics = require(77226);
|
|
var generalAnalytics = require(37786);
|
|
|
|
/**
|
|
* Scene types for analytics context
|
|
*/
|
|
var SceneType = {
|
|
MainPage: "main_page",
|
|
VideoFeed: "video_feed",
|
|
VideoDetail: "video_detail"
|
|
};
|
|
|
|
/**
|
|
* Click types for interaction tracking
|
|
*/
|
|
var ClickType = {
|
|
Close: "close",
|
|
CaptionSeeMore: "caption_see_more",
|
|
Hashtag: "hashtag",
|
|
Music: "music",
|
|
User: "user"
|
|
};
|
|
|
|
/**
|
|
* Channel analytics service
|
|
*/
|
|
var channelAnalytics = {
|
|
/**
|
|
* Handle general channel click events
|
|
*/
|
|
handleChannelGeneralClick: function(clickType, scene, extraParams) {
|
|
scene = scene !== undefined ? scene : "main_page";
|
|
extraParams = extraParams !== undefined ? extraParams : {};
|
|
|
|
generalAnalytics.O.handleGeneralClick(clickType, objectUtils._({
|
|
scene: scene
|
|
}, extraParams));
|
|
},
|
|
|
|
/**
|
|
* Handle download app dismiss
|
|
*/
|
|
handleDownloadDismiss: function(enterMethod) {
|
|
teaAnalytics.f.sendEvent("download_app_dismiss", {
|
|
enter_method: enterMethod
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Handle info card show event
|
|
*/
|
|
handleInfoCardShow: function(params) {
|
|
teaAnalytics.f.sendEvent("info_card_show", params);
|
|
},
|
|
|
|
/**
|
|
* Handle info card click event
|
|
*/
|
|
handleInfoCardClick: function(params) {
|
|
teaAnalytics.f.sendEvent("info_card_click", params);
|
|
},
|
|
|
|
/**
|
|
* Handle item tags show event
|
|
*/
|
|
handleShowItemTags: function(params) {
|
|
teaAnalytics.f.sendEvent("query_tag_show", params);
|
|
}
|
|
};
|
|
}
|
|
|
|
}]);
|
|
|
|
// Additional chunk for comment system
|
|
(self.__LOADABLE_LOADED_CHUNKS__ = self.__LOADABLE_LOADED_CHUNKS__ || []).push([["60248"], {
|
|
|
|
/**
|
|
* Module 61937: Comment Item Management
|
|
* State management for individual comment items
|
|
*/
|
|
61937: function(exports, module, require) {
|
|
require.d(exports, {
|
|
I9: function() { return commentItemAtom; },
|
|
Tq: function() { return useCommentItemState; },
|
|
tR: function() { return useCommentItemActions; }
|
|
});
|
|
|
|
var objectUtils = require(35383);
|
|
var assignUtils = require(5377);
|
|
var mergeUtils = require(45996);
|
|
var omitUtils = require(16327);
|
|
var keyUtils = require(11201);
|
|
var atomUtils = require(73455);
|
|
var serviceUtils = require(4676);
|
|
var jotaiUtils = require(10625);
|
|
|
|
/**
|
|
* Comment item atom for state management
|
|
*/
|
|
var commentItemAtom = atomUtils._(jotaiUtils.p("commentItemAtom@tiktok/webapp-atoms", {}), {
|
|
rehydrationKey: "webapp.comment.items"
|
|
});
|
|
|
|
/**
|
|
* Comment item service
|
|
*/
|
|
var commentItemService = serviceUtils.i(commentItemAtom, function(getState, setState) {
|
|
return {
|
|
/**
|
|
* Set individual comment item
|
|
*/
|
|
setItem: function(commentData) {
|
|
setState(commentItemAtom, function(currentState) {
|
|
return mergeUtils._(assignUtils._(assignUtils._({}, currentState),
|
|
objectUtils._(objectUtils._({}, commentData.cid, commentData)));
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Set comment like/digg state
|
|
*/
|
|
setItemDiggState: function(diggData) {
|
|
var currentState = getState(commentItemAtom);
|
|
if (currentState[diggData.cid]) {
|
|
setState(commentItemAtom, function(state) {
|
|
var existingComment = state[diggData.cid];
|
|
return mergeUtils._(assignUtils._(assignUtils._({}, state),
|
|
objectUtils._(objectUtils._({}, diggData.cid,
|
|
mergeUtils._(assignUtils._(assignUtils._({}, existingComment), {
|
|
user_digged: diggData.digged,
|
|
is_author_digged: diggData.is_author_digged !== undefined ?
|
|
diggData.is_author_digged : existingComment.is_author_digged
|
|
})))));
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Set comment like count
|
|
*/
|
|
setItemDiggCount: function(countData) {
|
|
var currentState = getState(commentItemAtom);
|
|
if (currentState[countData.cid]) {
|
|
setState(commentItemAtom, function(state) {
|
|
return mergeUtils._(assignUtils._(assignUtils._({}, state),
|
|
objectUtils._(objectUtils._({}, countData.cid,
|
|
mergeUtils._(assignUtils._(assignUtils._({}, state[countData.cid]), {
|
|
digg_count: countData.count
|
|
})))));
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Remove comment item
|
|
*/
|
|
removeItem: function(commentId) {
|
|
setState(commentItemAtom, function(state) {
|
|
var newState = assignUtils._(assignUtils._({}, state));
|
|
delete newState[commentId];
|
|
return newState;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Set multiple comment items at once
|
|
*/
|
|
multiSetCommentItem: function(commentArray) {
|
|
setState(commentItemAtom, function(state) {
|
|
var newState = assignUtils._(assignUtils._({}, state));
|
|
commentArray.forEach(function(comment) {
|
|
newState[comment.cid] = assignUtils._(assignUtils._({}, newState[comment.cid], comment));
|
|
});
|
|
return newState;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Remove multiple comment items
|
|
*/
|
|
multiRemoveCommentItem: function(commentIds) {
|
|
setState(commentItemAtom, function(state) {
|
|
var newState = assignUtils._(assignUtils._({}, state));
|
|
commentIds.forEach(function(commentId) {
|
|
delete newState[commentId];
|
|
});
|
|
return newState;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Increase or decrease reply comment count
|
|
*/
|
|
reduceOrIncreaseCommentCount: function(countData) {
|
|
var commentId = countData.cid;
|
|
var isReduce = countData.isReduce;
|
|
var currentState = getState(commentItemAtom);
|
|
|
|
if (currentState[commentId]) {
|
|
var currentComment = currentState[commentId];
|
|
var currentCount = Number(currentComment.reply_comment_total || 0);
|
|
|
|
setState(commentItemAtom, function(state) {
|
|
return mergeUtils._(assignUtils._(assignUtils._({}, state),
|
|
objectUtils._(objectUtils._({}, commentId,
|
|
mergeUtils._(assignUtils._(assignUtils._({}, state[commentId]), {
|
|
reply_comment_total: isReduce ? currentCount - 1 : currentCount + 1
|
|
})))));
|
|
});
|
|
}
|
|
}
|
|
};
|
|
});
|
|
|
|
var useCommentItemState = commentItemService.useServiceState;
|
|
var useCommentItemActions = commentItemService.useServiceDispatchers;
|
|
}
|
|
|
|
}]);
|