Files
VRCX/html/src/classes/sharedFeed.js

597 lines
24 KiB
JavaScript

import * as workerTimers from 'worker-timers';
import configRepository from '../repository/config.js';
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
export default class extends baseClass {
constructor(_app, _API, _t) {
super(_app, _API, _t);
}
_data = {
sharedFeed: {
gameLog: {
wrist: [],
lastEntryDate: ''
},
feedTable: {
wrist: [],
lastEntryDate: ''
},
notificationTable: {
wrist: [],
lastEntryDate: ''
},
friendLogTable: {
wrist: [],
lastEntryDate: ''
},
moderationAgainstTable: {
wrist: [],
lastEntryDate: ''
},
pendingUpdate: false
},
updateSharedFeedTimer: null,
updateSharedFeedPending: false,
updateSharedFeedPendingForceUpdate: false
};
_methods = {
updateSharedFeed(forceUpdate) {
if (!this.friendLogInitStatus) {
return;
}
if (this.updateSharedFeedTimer) {
if (forceUpdate) {
this.updateSharedFeedPendingForceUpdate = true;
}
this.updateSharedFeedPending = true;
} else {
this.updateSharedExecute(forceUpdate);
this.updateSharedFeedTimer = setTimeout(() => {
if (this.updateSharedFeedPending) {
this.updateSharedExecute(
this.updateSharedFeedPendingForceUpdate
);
}
this.updateSharedFeedTimer = null;
}, 150);
}
},
updateSharedExecute(forceUpdate) {
try {
this.updateSharedFeedDebounce(forceUpdate);
} catch (err) {
console.error(err);
}
this.updateSharedFeedTimer = null;
this.updateSharedFeedPending = false;
this.updateSharedFeedPendingForceUpdate = false;
},
updateSharedFeedDebounce(forceUpdate) {
this.updateSharedFeedGameLog(forceUpdate);
this.updateSharedFeedFeedTable(forceUpdate);
this.updateSharedFeedNotificationTable(forceUpdate);
this.updateSharedFeedFriendLogTable(forceUpdate);
this.updateSharedFeedModerationAgainstTable(forceUpdate);
var feeds = this.sharedFeed;
if (!feeds.pendingUpdate) {
return;
}
var wristFeed = [];
wristFeed = wristFeed.concat(
feeds.gameLog.wrist,
feeds.feedTable.wrist,
feeds.notificationTable.wrist,
feeds.friendLogTable.wrist,
feeds.moderationAgainstTable.wrist
);
// OnPlayerJoining/Traveling
API.currentTravelers.forEach((ref) => {
var isFavorite = this.localFavoriteFriends.has(ref.id);
if (
(this.sharedFeedFilters.wrist.OnPlayerJoining ===
'Friends' ||
(this.sharedFeedFilters.wrist.OnPlayerJoining ===
'VIP' &&
isFavorite)) &&
!$app.lastLocation.playerList.has(ref.id)
) {
if (ref.$location.tag === $app.lastLocation.location) {
var feedEntry = {
...ref,
isFavorite,
isFriend: true,
type: 'OnPlayerJoining'
};
wristFeed.unshift(feedEntry);
} else {
var worldRef = API.cachedWorlds.get(
ref.$location.worldId
);
var groupName = '';
if (ref.$location.groupId) {
var groupRef = API.cachedGroups.get(
ref.$location.groupId
);
if (typeof groupRef !== 'undefined') {
groupName = groupRef.name;
} else {
// no group cache, fetch group and try again
API.getGroup({
groupId: ref.$location.groupId
})
.then((args) => {
workerTimers.setTimeout(() => {
// delay to allow for group cache to update
$app.sharedFeed.pendingUpdate = true;
$app.updateSharedFeed(false);
}, 100);
return args;
})
.catch((err) => {
console.error(err);
});
}
}
if (typeof worldRef !== 'undefined') {
var feedEntry = {
created_at: ref.created_at,
type: 'GPS',
userId: ref.id,
displayName: ref.displayName,
location: ref.$location.tag,
worldName: worldRef.name,
groupName,
previousLocation: '',
isFavorite,
time: 0,
isFriend: true,
isTraveling: true
};
wristFeed.unshift(feedEntry);
} else {
// no world cache, fetch world and try again
API.getWorld({
worldId: ref.$location.worldId
})
.then((args) => {
workerTimers.setTimeout(() => {
// delay to allow for world cache to update
$app.sharedFeed.pendingUpdate = true;
$app.updateSharedFeed(false);
}, 100);
return args;
})
.catch((err) => {
console.error(err);
});
}
}
}
});
wristFeed.sort(function (a, b) {
if (a.created_at < b.created_at) {
return 1;
}
if (a.created_at > b.created_at) {
return -1;
}
return 0;
});
wristFeed.splice(16);
AppApi.ExecuteVrFeedFunction(
'wristFeedUpdate',
JSON.stringify(wristFeed)
);
this.applyUserDialogLocation();
this.applyWorldDialogInstances();
this.applyGroupDialogInstances();
feeds.pendingUpdate = false;
},
updateSharedFeedGameLog(forceUpdate) {
// Location, OnPlayerJoined, OnPlayerLeft
var sessionTable = this.gameLogSessionTable;
var i = sessionTable.length;
if (i > 0) {
if (
sessionTable[i - 1].created_at ===
this.sharedFeed.gameLog.lastEntryDate &&
forceUpdate === false
) {
return;
}
this.sharedFeed.gameLog.lastEntryDate =
sessionTable[i - 1].created_at;
} else {
return;
}
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
var wristArr = [];
var w = 0;
var wristFilter = this.sharedFeedFilters.wrist;
var currentUserLeaveTime = 0;
var locationJoinTime = 0;
for (var i = sessionTable.length - 1; i > -1; i--) {
var ctx = sessionTable[i];
if (ctx.created_at < bias) {
break;
}
if (ctx.type === 'Notification') {
continue;
}
// on Location change remove OnPlayerLeft
if (ctx.type === 'LocationDestination') {
currentUserLeaveTime = Date.parse(ctx.created_at);
var currentUserLeaveTimeOffset =
currentUserLeaveTime + 5 * 1000;
for (var k = w - 1; k > -1; k--) {
var feedItem = wristArr[k];
if (
(feedItem.type === 'OnPlayerLeft' ||
feedItem.type === 'BlockedOnPlayerLeft' ||
feedItem.type === 'MutedOnPlayerLeft') &&
Date.parse(feedItem.created_at) >=
currentUserLeaveTime &&
Date.parse(feedItem.created_at) <=
currentUserLeaveTimeOffset
) {
wristArr.splice(k, 1);
w--;
}
}
}
// on Location change remove OnPlayerJoined
if (ctx.type === 'Location') {
locationJoinTime = Date.parse(ctx.created_at);
var locationJoinTimeOffset = locationJoinTime + 20 * 1000;
for (var k = w - 1; k > -1; k--) {
var feedItem = wristArr[k];
if (
(feedItem.type === 'OnPlayerJoined' ||
feedItem.type === 'BlockedOnPlayerJoined' ||
feedItem.type === 'MutedOnPlayerJoined') &&
Date.parse(feedItem.created_at) >=
locationJoinTime &&
Date.parse(feedItem.created_at) <=
locationJoinTimeOffset
) {
wristArr.splice(k, 1);
w--;
}
}
}
// remove current user
if (
(ctx.type === 'OnPlayerJoined' ||
ctx.type === 'OnPlayerLeft' ||
ctx.type === 'PortalSpawn') &&
ctx.displayName === API.currentUser.displayName
) {
continue;
}
var isFriend = false;
var isFavorite = false;
if (ctx.userId) {
isFriend = this.friends.has(ctx.userId);
isFavorite = this.localFavoriteFriends.has(ctx.userId);
} else if (ctx.displayName) {
for (var ref of API.cachedUsers.values()) {
if (ref.displayName === ctx.displayName) {
isFriend = this.friends.has(ref.id);
isFavorite = this.localFavoriteFriends.has(ref.id);
break;
}
}
}
// add tag colour
var tagColour = '';
if (ctx.userId) {
var tagRef = this.customUserTags.get(ctx.userId);
if (typeof tagRef !== 'undefined') {
tagColour = tagRef.colour;
}
}
// BlockedOnPlayerJoined, BlockedOnPlayerLeft, MutedOnPlayerJoined, MutedOnPlayerLeft
if (
ctx.type === 'OnPlayerJoined' ||
ctx.type === 'OnPlayerLeft'
) {
for (var ref of API.cachedPlayerModerations.values()) {
if (
ref.targetDisplayName !== ctx.displayName &&
ref.sourceUserId !== ctx.userId
) {
continue;
}
if (ref.type === 'block') {
var type = `Blocked${ctx.type}`;
} else if (ref.type === 'mute') {
var type = `Muted${ctx.type}`;
} else {
continue;
}
var entry = {
created_at: ctx.created_at,
type,
displayName: ref.targetDisplayName,
userId: ref.targetUserId,
isFriend,
isFavorite
};
if (
wristFilter[type] &&
(wristFilter[type] === 'Everyone' ||
(wristFilter[type] === 'Friends' && isFriend) ||
(wristFilter[type] === 'VIP' && isFavorite))
) {
wristArr.unshift(entry);
}
this.queueGameLogNoty(entry);
}
}
// when too many user joins happen at once when switching instances
// the "w" counter maxes out and wont add any more entries
// until the onJoins are cleared by "Location"
// e.g. if a "VideoPlay" occurs between "OnPlayerJoined" and "Location" it wont be added
if (
w < 50 &&
wristFilter[ctx.type] &&
(wristFilter[ctx.type] === 'On' ||
wristFilter[ctx.type] === 'Everyone' ||
(wristFilter[ctx.type] === 'Friends' && isFriend) ||
(wristFilter[ctx.type] === 'VIP' && isFavorite))
) {
wristArr.push({
...ctx,
tagColour,
isFriend,
isFavorite
});
++w;
}
}
this.sharedFeed.gameLog.wrist = wristArr;
this.sharedFeed.pendingUpdate = true;
},
updateSharedFeedFeedTable(forceUpdate) {
// GPS, Online, Offline, Status, Avatar
var feedSession = this.feedSessionTable;
var i = feedSession.length;
if (i > 0) {
if (
feedSession[i - 1].created_at ===
this.sharedFeed.feedTable.lastEntryDate &&
forceUpdate === false
) {
return;
}
this.sharedFeed.feedTable.lastEntryDate =
feedSession[i - 1].created_at;
} else {
return;
}
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
var wristArr = [];
var w = 0;
var wristFilter = this.sharedFeedFilters.wrist;
for (var i = feedSession.length - 1; i > -1; i--) {
var ctx = feedSession[i];
if (ctx.created_at < bias) {
break;
}
if (ctx.type === 'Avatar') {
continue;
}
// hide private worlds from feed
if (
this.hidePrivateFromFeed &&
ctx.type === 'GPS' &&
ctx.location === 'private'
) {
continue;
}
var isFriend = this.friends.has(ctx.userId);
var isFavorite = this.localFavoriteFriends.has(ctx.userId);
if (
w < 20 &&
wristFilter[ctx.type] &&
(wristFilter[ctx.type] === 'Friends' ||
(wristFilter[ctx.type] === 'VIP' && isFavorite))
) {
wristArr.push({
...ctx,
isFriend,
isFavorite
});
++w;
}
}
this.sharedFeed.feedTable.wrist = wristArr;
this.sharedFeed.pendingUpdate = true;
},
updateSharedFeedNotificationTable(forceUpdate) {
// invite, requestInvite, requestInviteResponse, inviteResponse, friendRequest
var notificationTable = this.notificationTable;
var i = notificationTable.length;
if (i > 0) {
if (
notificationTable[i - 1].created_at ===
this.sharedFeed.notificationTable.lastEntryDate &&
forceUpdate === false
) {
return;
}
this.sharedFeed.notificationTable.lastEntryDate =
notificationTable[i - 1].created_at;
} else {
return;
}
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
var wristArr = [];
var w = 0;
var wristFilter = this.sharedFeedFilters.wrist;
for (var i = notificationTable.length - 1; i > -1; i--) {
var ctx = notificationTable[i];
if (ctx.created_at < bias) {
break;
}
if (ctx.senderUserId === API.currentUser.id) {
continue;
}
var isFriend = this.friends.has(ctx.senderUserId);
var isFavorite = this.localFavoriteFriends.has(
ctx.senderUserId
);
if (
w < 20 &&
wristFilter[ctx.type] &&
(wristFilter[ctx.type] === 'On' ||
wristFilter[ctx.type] === 'Friends' ||
(wristFilter[ctx.type] === 'VIP' && isFavorite))
) {
wristArr.push({
...ctx,
isFriend,
isFavorite
});
++w;
}
}
this.sharedFeed.notificationTable.wrist = wristArr;
this.sharedFeed.pendingUpdate = true;
},
updateSharedFeedFriendLogTable(forceUpdate) {
// TrustLevel, Friend, FriendRequest, Unfriend, DisplayName
var friendLog = this.friendLogTable;
var i = friendLog.length;
if (i > 0) {
if (
friendLog[i - 1].created_at ===
this.sharedFeed.friendLogTable.lastEntryDate &&
forceUpdate === false
) {
return;
}
this.sharedFeed.friendLogTable.lastEntryDate =
friendLog[i - 1].created_at;
} else {
return;
}
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
var wristArr = [];
var w = 0;
var wristFilter = this.sharedFeedFilters.wrist;
for (var i = friendLog.length - 1; i > -1; i--) {
var ctx = friendLog[i];
if (ctx.created_at < bias) {
break;
}
if (ctx.type === 'FriendRequest') {
continue;
}
var isFriend = this.friends.has(ctx.userId);
var isFavorite = this.localFavoriteFriends.has(ctx.userId);
if (
w < 20 &&
wristFilter[ctx.type] &&
(wristFilter[ctx.type] === 'On' ||
wristFilter[ctx.type] === 'Friends' ||
(wristFilter[ctx.type] === 'VIP' && isFavorite))
) {
wristArr.push({
...ctx,
isFriend,
isFavorite
});
++w;
}
}
this.sharedFeed.friendLogTable.wrist = wristArr;
this.sharedFeed.pendingUpdate = true;
},
updateSharedFeedModerationAgainstTable(forceUpdate) {
// Unblocked, Blocked, Muted, Unmuted
var moderationAgainst = this.moderationAgainstTable;
var i = moderationAgainst.length;
if (i > 0) {
if (
moderationAgainst[i - 1].created_at ===
this.sharedFeed.moderationAgainstTable.lastEntryDate &&
forceUpdate === false
) {
return;
}
this.sharedFeed.moderationAgainstTable.lastEntryDate =
moderationAgainst[i - 1].created_at;
} else {
return;
}
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
var wristArr = [];
var w = 0;
var wristFilter = this.sharedFeedFilters.wrist;
for (var i = moderationAgainst.length - 1; i > -1; i--) {
var ctx = moderationAgainst[i];
if (ctx.created_at < bias) {
break;
}
var isFriend = this.friends.has(ctx.userId);
var isFavorite = this.localFavoriteFriends.has(ctx.userId);
// add tag colour
var tagColour = '';
var tagRef = this.customUserTags.get(ctx.userId);
if (typeof tagRef !== 'undefined') {
tagColour = tagRef.colour;
}
if (
w < 20 &&
wristFilter[ctx.type] &&
wristFilter[ctx.type] === 'On'
) {
wristArr.push({
...ctx,
isFriend,
isFavorite,
tagColour
});
++w;
}
}
this.sharedFeed.moderationAgainstTable.wrist = wristArr;
this.sharedFeed.pendingUpdate = true;
},
saveSharedFeedFilters() {
configRepository.setString(
'sharedFeedFilters',
JSON.stringify(this.sharedFeedFilters)
);
this.updateSharedFeed(true);
},
async resetNotyFeedFilters() {
this.sharedFeedFilters.noty = {
...this.sharedFeedFiltersDefaults.noty
};
this.saveSharedFeedFilters();
},
async resetWristFeedFilters() {
this.sharedFeedFilters.wrist = {
...this.sharedFeedFiltersDefaults.wrist
};
this.saveSharedFeedFilters();
}
};
}